summaryrefslogtreecommitdiffstats
path: root/kate/part
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commitce4a32fe52ef09d8f5ff1dd22c001110902b60a2 (patch)
tree5ac38a06f3dde268dc7927dc155896926aaf7012 /kate/part
downloadtdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.tar.gz
tdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdelibs@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kate/part')
-rw-r--r--kate/part/.kateconfig1
-rw-r--r--kate/part/Makefile.am44
-rw-r--r--kate/part/configure.in.in89
-rw-r--r--kate/part/katearbitraryhighlight.cpp162
-rw-r--r--kate/part/katearbitraryhighlight.h87
-rw-r--r--kate/part/kateattribute.cpp268
-rw-r--r--kate/part/kateattribute.h147
-rw-r--r--kate/part/kateautoindent.cpp2543
-rw-r--r--kate/part/kateautoindent.h581
-rw-r--r--kate/part/katebookmarks.cpp287
-rw-r--r--kate/part/katebookmarks.h86
-rw-r--r--kate/part/katebuffer.cpp1660
-rw-r--r--kate/part/katebuffer.h709
-rw-r--r--kate/part/katecmds.cpp605
-rw-r--r--kate/part/katecmds.h178
-rw-r--r--kate/part/katecodecompletion.cpp566
-rw-r--r--kate/part/katecodecompletion.h164
-rw-r--r--kate/part/katecodefoldinghelpers.cpp1662
-rw-r--r--kate/part/katecodefoldinghelpers.h222
-rw-r--r--kate/part/kateconfig.cpp1429
-rw-r--r--kate/part/kateconfig.h537
-rw-r--r--kate/part/katecursor.cpp192
-rw-r--r--kate/part/katecursor.h250
-rw-r--r--kate/part/katedialogs.cpp1740
-rw-r--r--kate/part/katedialogs.h406
-rw-r--r--kate/part/katedocument.cpp5174
-rw-r--r--kate/part/katedocument.h1073
-rw-r--r--kate/part/katedocumenthelpers.cpp56
-rw-r--r--kate/part/katedocumenthelpers.h71
-rw-r--r--kate/part/katefactory.cpp276
-rw-r--r--kate/part/katefactory.h312
-rw-r--r--kate/part/katefiletype.cpp596
-rw-r--r--kate/part/katefiletype.h142
-rw-r--r--kate/part/katefont.cpp127
-rw-r--r--kate/part/katefont.h114
-rw-r--r--kate/part/katehighlight.cpp3473
-rw-r--r--kate/part/katehighlight.h438
-rw-r--r--kate/part/kateindentscriptabstracts.cpp49
-rw-r--r--kate/part/kateindentscriptabstracts.h99
-rw-r--r--kate/part/katejscript.cpp1169
-rw-r--r--kate/part/katejscript.h233
-rw-r--r--kate/part/katekeyinterceptorfunctor.h25
-rw-r--r--kate/part/katelinerange.cpp75
-rw-r--r--kate/part/katelinerange.h70
-rw-r--r--kate/part/kateluaindentscript.cpp528
-rw-r--r--kate/part/kateluaindentscript.h69
-rw-r--r--kate/part/kateprinter.cpp1005
-rw-r--r--kate/part/kateprinter.h136
-rw-r--r--kate/part/katerenderer.cpp1032
-rw-r--r--kate/part/katerenderer.h272
-rw-r--r--kate/part/kateschema.cpp1611
-rw-r--r--kate/part/kateschema.h323
-rw-r--r--kate/part/katesearch.cpp1012
-rw-r--r--kate/part/katesearch.h243
-rw-r--r--kate/part/katespell.cpp221
-rw-r--r--kate/part/katespell.h86
-rw-r--r--kate/part/katesupercursor.cpp746
-rw-r--r--kate/part/katesupercursor.h463
-rw-r--r--kate/part/katesyntaxdocument.cpp475
-rw-r--r--kate/part/katesyntaxdocument.h164
-rw-r--r--kate/part/katetemplatehandler.cpp342
-rw-r--r--kate/part/katetemplatehandler.h68
-rw-r--r--kate/part/katetextline.cpp443
-rw-r--r--kate/part/katetextline.h456
-rw-r--r--kate/part/kateundo.cpp392
-rw-r--r--kate/part/kateundo.h141
-rw-r--r--kate/part/kateview.cpp1920
-rw-r--r--kate/part/kateview.h571
-rw-r--r--kate/part/kateviewhelpers.cpp1205
-rw-r--r--kate/part/kateviewhelpers.h207
-rw-r--r--kate/part/kateviewinternal.cpp3496
-rw-r--r--kate/part/kateviewinternal.h397
-rw-r--r--kate/part/test_regression.cpp1344
-rw-r--r--kate/part/test_regression.h249
74 files changed, 47804 insertions, 0 deletions
diff --git a/kate/part/.kateconfig b/kate/part/.kateconfig
new file mode 100644
index 000000000..5b0885abe
--- /dev/null
+++ b/kate/part/.kateconfig
@@ -0,0 +1 @@
+kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/Makefile.am b/kate/part/Makefile.am
new file mode 100644
index 000000000..4a182452e
--- /dev/null
+++ b/kate/part/Makefile.am
@@ -0,0 +1,44 @@
+kde_module_LTLIBRARIES = libkatepart.la
+
+noinst_LTLIBRARIES = libkate.la
+
+libkate_la_SOURCES = katesearch.cpp katebuffer.cpp katecmds.cpp \
+ kateundo.cpp katecursor.cpp katedialogs.cpp katedocument.cpp \
+ katefactory.cpp katehighlight.cpp katesyntaxdocument.cpp \
+ katetextline.cpp kateview.cpp kateconfig.cpp kateviewhelpers.cpp \
+ katecodecompletion.cpp katedocumenthelpers.cpp \
+ katecodefoldinghelpers.cpp kateviewinternal.cpp katebookmarks.cpp \
+ kateprinter.cpp katefont.cpp katelinerange.cpp katesupercursor.cpp \
+ katearbitraryhighlight.cpp katerenderer.cpp kateattribute.cpp \
+ kateautoindent.cpp katefiletype.cpp kateschema.cpp katedocument.skel \
+ katetemplatehandler.cpp katejscript.cpp katespell.cpp kateindentscriptabstracts.cpp \
+ kateluaindentscript.cpp
+
+libkatepart_la_SOURCES = dummy.cpp
+
+libkatepart_la_LIBADD = libkate.la ../interfaces/libkatepartinterfaces.la $(top_builddir)/kdeprint/libkdeprint.la $(top_builddir)/kutils/libkutils.la $(top_builddir)/kjs/libkjs.la $(LUA_LIBS)
+
+libkatepart_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN)
+
+INCLUDES= -I../interfaces -I$(top_srcdir) -I$(top_srcdir)/kdeprint -I$(top_srcdir)/interfaces -I$(top_srcdir)/interfaces/kregexpeditor -I$(top_srcdir)/kdefx -I$(top_srcdir)/kutils -I$(top_builddir)/kjs $(LUA_INCLUDES) $(all_includes)
+
+METASOURCES = AUTO
+
+LUT_FILES = katejscript.lut.h
+
+CREATE_HASH_TABLE = $(top_srcdir)/kjs/create_hash_table
+
+dummy.cpp: $(srcdir)/Makefile.am
+ touch $@
+
+katejscript.lut.h : $(srcdir)/katejscript.cpp $(CREATE_HASH_TABLE)
+ $(PERL) $(CREATE_HASH_TABLE) $(srcdir)/katejscript.cpp > $@
+katejscript.lo: katejscript.lut.h
+
+CLEANFILES = $(LUT_FILES)
+
+## test program
+TESTS = testkateregression
+check_PROGRAMS = testkateregression
+testkateregression_SOURCES = test_regression.cpp
+testkateregression_LDADD = $(libkatepart_la_LIBADD)
diff --git a/kate/part/configure.in.in b/kate/part/configure.in.in
new file mode 100644
index 000000000..faf0717e9
--- /dev/null
+++ b/kate/part/configure.in.in
@@ -0,0 +1,89 @@
+AC_DEFUN([AC_PATH_LUA], [
+
+dnl Based on the lua check used by yzis-M3
+
+ HAVE_LUA=""
+
+ AC_ARG_WITH([lua],
+ AC_HELP_STRING([--without-lua], [Build without Lua libraries (default: check)]))
+
+ AC_ARG_WITH(lua-dir,
+ AC_HELP_STRING([--with-lua-dir=DIR],[where the root of Lua 5.x is installed]),
+ [
+ LUA="$withval"
+ LUA_INCLUDES=-I"$withval"/include
+ LUA_LIBS="-L$withval/lib" ])
+
+ AC_ARG_WITH(lua-includes,
+ AC_HELP_STRING([--with-lua-includes=DIR],[where the Lua includes are]),
+ [ LUA_INCLUDES="-I$withval" ])
+
+ AC_ARG_WITH(lua-libraries,
+ AC_HELP_STRING([--with-lua-libraries=DIR],[where the Lua library is installed]),
+ [
+ LUA_LIBS="-L$withval" ])
+
+
+ if test "x$with_lua" = "xno"; then
+ AC_MSG_RESULT([Not using Lua])
+ else
+ if ! test "x$LUA" = "x"; then
+ AC_MSG_RESULT(using Lua from $LUA)
+ fi
+ if ! test "x$LUA_LIBS" = "x"; then
+ AC_MSG_RESULT(using Lua libraries in $LUA_LIBS)
+ fi
+ if ! test "x$LUA_INCLUDES" = "x"; then
+ AC_MSG_RESULT(using Lua includes in $LUA_INCLUDES)
+ fi
+
+ dnl checking some headers first
+ ac_save_CFLAGS="$CFLAGS"
+ ac_save_CPPFLAGS="$CPPFLAGS"
+ ac_save_LDFLAGS="$LDFLAGS"
+ CFLAGS="$LUA_INCLUDES $CFLAGS"
+ CPPFLAGS="$LUA_INCLUDES $CPPFLAGS"
+ LDFLAGS="$LUA_LIBS $LDFLAGS"
+
+ LUAH_FOUND=""
+ AC_CHECK_HEADER(lua.h,LUAH_FOUND="true",
+ [ AC_MSG_RESULT([lua.h was not found or was not usable, Lua 5.0 headers are required !]) ]
+ )
+ LUALIBH_FOUND=""
+ AC_CHECK_HEADER(lualib.h,LUALIBH_FOUND="true",
+ [ AC_MSG_RESULT([lualib.h was not found or was not usable, Lua 5.0 headers are required !]) ]
+ )
+
+
+ dnl find the libs name
+ if test -z "$LUALIBH_FOUND" -o -z "$LUAH_FOUND"; then
+ LUA_LIBS=""
+ else
+ AC_CHECK_LIB(lua50,lua_version, LUA_LIBS="$LUA_LIBS -llua50 -llualib50",
+ AC_CHECK_LIB(lua,lua_version, LUA_LIBS="$LUA_LIBS -llua -llualib",
+ [LUA_LIBS=""
+ AC_MSG_RESULT([Lua 5.0 libraries were not found !]) ]
+ )
+ )
+ fi
+ CFLAGS="$ac_save_CFLAGS"
+ CPPFLAGS="$ac_save_CPPFLAGS"
+ LDFLAGS="$ac_save_LDFLAGS"
+
+
+ if test -z "$LUA_LIBS"; then
+ LUA=""
+ LUA_INCLUDES=""
+ LUA_LIBS=""
+ else
+ AC_DEFINE_UNQUOTED(HAVE_LUA, 1, [Define if you have LUA > 5.0])
+ HAVE_LUA="yes"
+ fi
+ AC_SUBST(LUA)
+ AC_SUBST(LUA_INCLUDES)
+ AC_SUBST(LUA_LIBS)
+ fi
+
+])
+
+AC_PATH_LUA
diff --git a/kate/part/katearbitraryhighlight.cpp b/kate/part/katearbitraryhighlight.cpp
new file mode 100644
index 000000000..2ecbececc
--- /dev/null
+++ b/kate/part/katearbitraryhighlight.cpp
@@ -0,0 +1,162 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2003 Hamish Rodda <rodda@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "katearbitraryhighlight.h"
+#include "katearbitraryhighlight.moc"
+
+#include "katesupercursor.h"
+#include "katedocument.h"
+
+#include <kdebug.h>
+
+#include <qfont.h>
+
+KateArbitraryHighlightRange::KateArbitraryHighlightRange(KateSuperCursor* start,
+KateSuperCursor* end, QObject* parent, const char* name) :
+KateSuperRange(start, end, parent, name) {
+}
+
+KateArbitraryHighlightRange::KateArbitraryHighlightRange(KateDocument* doc, const KateRange& range, QObject* parent, const char* name)
+ : KateSuperRange(doc, range, parent, name)
+{
+}
+
+KateArbitraryHighlightRange::KateArbitraryHighlightRange(KateDocument* doc, const KateTextCursor& start, const KateTextCursor& end, QObject* parent, const char* name)
+ : KateSuperRange(doc, start, end, parent, name)
+{
+}
+
+KateArbitraryHighlightRange::~KateArbitraryHighlightRange()
+{
+}
+
+KateArbitraryHighlight::KateArbitraryHighlight(KateDocument* parent, const char* name)
+ : QObject(parent, name)
+{
+}
+
+KateAttribute KateArbitraryHighlightRange::merge(QPtrList<KateSuperRange> ranges)
+{
+ ranges.sort();
+
+ KateAttribute ret;
+
+ if (ranges.first() && ranges.current()->inherits("KateArbitraryHighlightRange"))
+ ret = *(static_cast<KateArbitraryHighlightRange*>(ranges.current()));
+
+ KateSuperRange* r;
+ while ((r = ranges.next())) {
+ if (r->inherits("KateArbitraryHighlightRange")) {
+ KateArbitraryHighlightRange* hl = static_cast<KateArbitraryHighlightRange*>(r);
+ ret += *hl;
+ }
+ }
+
+ return ret;
+}
+
+void KateArbitraryHighlight::addHighlightToDocument(KateSuperRangeList* list)
+{
+ m_docHLs.append(list);
+ connect(list, SIGNAL(rangeEliminated(KateSuperRange*)), SLOT(slotRangeEliminated(KateSuperRange*)));
+ connect(list, SIGNAL(destroyed(QObject*)),SLOT(slotRangeListDeleted(QObject*)));
+}
+
+void KateArbitraryHighlight::addHighlightToView(KateSuperRangeList* list, KateView* view)
+{
+ if (!m_viewHLs[view])
+ m_viewHLs.insert(view, new QPtrList<KateSuperRangeList>());
+
+ m_viewHLs[view]->append(list);
+
+ connect(list, SIGNAL(rangeEliminated(KateSuperRange*)), SLOT(slotTagRange(KateSuperRange*)));
+ connect(list, SIGNAL(tagRange(KateSuperRange*)), SLOT(slotTagRange(KateSuperRange*)));
+ connect(list, SIGNAL(destroyed(QObject*)),SLOT(slotRangeListDeleted(QObject*)));
+}
+
+void KateArbitraryHighlight::slotRangeListDeleted(QObject* obj) {
+ int id=m_docHLs.findRef(static_cast<KateSuperRangeList*>(obj));
+ if (id>=0) m_docHLs.take(id);
+
+ for (QMap<KateView*, QPtrList<KateSuperRangeList>* >::Iterator it = m_viewHLs.begin(); it != m_viewHLs.end(); ++it)
+ for (KateSuperRangeList* l = (*it)->first(); l; l = (*it)->next())
+ if (l==obj) {
+ l->take();
+ break; //should we check if one list is stored more than once for a view ?? I don't think adding the same list 2 or more times is sane, but who knows (jowenn)
+ }
+}
+
+KateSuperRangeList& KateArbitraryHighlight::rangesIncluding(uint line, KateView* view)
+{
+ // OPTIMISE make return value persistent
+
+ static KateSuperRangeList s_return(false);
+
+ Q_ASSERT(!s_return.autoDelete());
+ s_return.clear();
+
+ //--- TEMPORARY OPTIMISATION: return the actual range when there are none or one. ---
+ if (m_docHLs.count() + m_viewHLs.count() == 0)
+ return s_return;
+ else if (m_docHLs.count() + m_viewHLs.count() == 1)
+ if (m_docHLs.count())
+ return *(m_docHLs.first());
+ else
+ if (m_viewHLs.values().first() && m_viewHLs.values().first()->count() == 1)
+ if (m_viewHLs.keys().first() == view && m_viewHLs.values().first())
+ return *(m_viewHLs.values().first()->first());
+ //--- END Temporary optimisation ---
+
+ if (view) {
+ QPtrList<KateSuperRangeList>* list = m_viewHLs[view];
+ if (list)
+ for (KateSuperRangeList* l = list->first(); l; l = list->next())
+ if (l->count())
+ s_return.appendList(l->rangesIncluding(line));
+
+ } else {
+ for (QMap<KateView*, QPtrList<KateSuperRangeList>* >::Iterator it = m_viewHLs.begin(); it != m_viewHLs.end(); ++it)
+ for (KateSuperRangeList* l = (*it)->first(); l; l = (*it)->next())
+ if (l->count())
+ s_return.appendList(l->rangesIncluding(line));
+ }
+
+ for (KateSuperRangeList* l = m_docHLs.first(); l; l = m_docHLs.next())
+ if (l->count())
+ s_return.appendList(l->rangesIncluding(line));
+
+ return s_return;
+}
+
+void KateArbitraryHighlight::slotTagRange(KateSuperRange* range)
+{
+ emit tagLines(viewForRange(range), range);
+}
+
+KateView* KateArbitraryHighlight::viewForRange(KateSuperRange* range)
+{
+ for (QMap<KateView*, QPtrList<KateSuperRangeList>* >::Iterator it = m_viewHLs.begin(); it != m_viewHLs.end(); ++it)
+ for (KateSuperRangeList* l = (*it)->first(); l; l = (*it)->next())
+ if (l->contains(range))
+ return it.key();
+
+ // This must belong to a document-global highlight
+ return 0L;
+}
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katearbitraryhighlight.h b/kate/part/katearbitraryhighlight.h
new file mode 100644
index 000000000..7955e7889
--- /dev/null
+++ b/kate/part/katearbitraryhighlight.h
@@ -0,0 +1,87 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2003 Hamish Rodda <rodda@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KATEARBITRARYHIGHLIGHT_H
+#define KATEARBITRARYHIGHLIGHT_H
+
+#include "kateattribute.h"
+#include "katesupercursor.h"
+
+#include <qobject.h>
+#include <qptrlist.h>
+#include <qmap.h>
+
+class KateDocument;
+class KateView;
+
+class KateArbitraryHighlightRange : public KateSuperRange, public KateAttribute
+{
+ Q_OBJECT
+
+public:
+ KateArbitraryHighlightRange(KateSuperCursor* start, KateSuperCursor* end, QObject* parent = 0L, const char* name = 0L);
+ KateArbitraryHighlightRange(KateDocument* doc, const KateRange& range, QObject* parent = 0L, const char* name = 0L);
+ KateArbitraryHighlightRange(KateDocument* doc, const KateTextCursor& start, const KateTextCursor& end, QObject* parent = 0L, const char* name = 0L);
+
+ virtual ~KateArbitraryHighlightRange();
+
+ virtual void changed() { slotTagRange(); };
+
+ static KateAttribute merge(QPtrList<KateSuperRange> ranges);
+};
+
+/**
+ * An arbitrary highlighting interface for Kate.
+ *
+ * Ideas for more features:
+ * - integration with syntax highlighting:
+ * - eg. a signal for when a new context is created, destroyed, changed
+ * - hopefully make this extension more complimentary to the current syntax highlighting
+ * - signal for cursor movement
+ * - signal for mouse movement
+ * - identical highlight for whole list
+ * - signals for view movement
+ */
+class KateArbitraryHighlight : public QObject
+{
+ Q_OBJECT
+
+public:
+ KateArbitraryHighlight(KateDocument* parent = 0L, const char* name = 0L);
+
+ void addHighlightToDocument(KateSuperRangeList* list);
+ void addHighlightToView(KateSuperRangeList* list, KateView* view);
+
+ KateSuperRangeList& rangesIncluding(uint line, KateView* view = 0L);
+
+signals:
+ void tagLines(KateView* view, KateSuperRange* range);
+
+private slots:
+ void slotTagRange(KateSuperRange* range);
+ void slotRangeListDeleted(QObject* obj);
+private:
+ KateView* viewForRange(KateSuperRange* range);
+
+ QMap<KateView*, QPtrList<KateSuperRangeList>* > m_viewHLs;
+ QPtrList<KateSuperRangeList> m_docHLs;
+};
+
+#endif
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/kateattribute.cpp b/kate/part/kateattribute.cpp
new file mode 100644
index 000000000..5de93a406
--- /dev/null
+++ b/kate/part/kateattribute.cpp
@@ -0,0 +1,268 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2003 Hamish Rodda <rodda@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kateattribute.h"
+
+KateAttribute::KateAttribute()
+ : m_weight(QFont::Normal)
+ , m_italic(false)
+ , m_underline(false)
+ , m_overline(false)
+ , m_strikeout(false)
+ , m_itemsSet(0)
+
+{
+}
+
+KateAttribute::~KateAttribute()
+{
+}
+
+void KateAttribute::clear()
+{
+ m_itemsSet=0;
+}
+
+KateAttribute& KateAttribute::operator+=(const KateAttribute& a)
+{
+ if (a.itemSet(Weight))
+ setWeight(a.weight());
+
+ if (a.itemSet(Italic))
+ setItalic(a.italic());
+
+ if (a.itemSet(Underline))
+ setUnderline(a.underline());
+
+ if (a.itemSet(Overline))
+ setOverline(a.overline());
+
+ if (a.itemSet(StrikeOut))
+ setStrikeOut(a.strikeOut());
+
+ if (a.itemSet(Outline))
+ setOutline(a.outline());
+
+ if (a.itemSet(TextColor))
+ setTextColor(a.textColor());
+
+ if (a.itemSet(SelectedTextColor))
+ setSelectedTextColor(a.selectedTextColor());
+
+ if (a.itemSet(BGColor))
+ setBGColor(a.bgColor());
+
+ if (a.itemSet(SelectedBGColor))
+ setSelectedBGColor(a.selectedBGColor());
+
+ return *this;
+}
+
+QFont KateAttribute::font(const QFont& ref)
+{
+ QFont ret = ref;
+
+ if (itemSet(Weight))
+ ret.setWeight(weight());
+ if (itemSet(Italic))
+ ret.setItalic(italic());
+ if (itemSet(Underline))
+ ret.setUnderline(underline());
+ if (itemSet(Overline))
+ ret.setOverline(overline());
+ if (itemSet(StrikeOut))
+ ret.setStrikeOut(strikeOut());
+
+ return ret;
+}
+
+void KateAttribute::setWeight(int weight)
+{
+ if (!(m_itemsSet & Weight) || m_weight != weight)
+ {
+ m_itemsSet |= Weight;
+
+ m_weight = weight;
+
+ changed();
+ }
+}
+
+void KateAttribute::setBold(bool enable)
+{
+ setWeight(enable ? QFont::Bold : QFont::Normal);
+}
+
+void KateAttribute::setItalic(bool enable)
+{
+ if (!(m_itemsSet & Italic) || m_italic != enable)
+ {
+ m_itemsSet |= Italic;
+
+ m_italic = enable;
+
+ changed();
+ }
+}
+
+void KateAttribute::setUnderline(bool enable)
+{
+ if (!(m_itemsSet & Underline) || m_underline != enable)
+ {
+ m_itemsSet |= Underline;
+
+ m_underline = enable;
+
+ changed();
+ }
+}
+
+void KateAttribute::setOverline(bool enable)
+{
+ if (!(m_itemsSet & Overline) || m_overline != enable)
+ {
+ m_itemsSet |= Overline;
+
+ m_overline = enable;
+
+ changed();
+ }
+}
+
+void KateAttribute::setStrikeOut(bool enable)
+{
+ if (!(m_itemsSet & StrikeOut) || m_strikeout != enable)
+ {
+ m_itemsSet |= StrikeOut;
+
+ m_strikeout = enable;
+
+ changed();
+ }
+}
+
+void KateAttribute::setOutline(const QColor& color)
+{
+ if (!(m_itemsSet & Outline) || m_outline != color)
+ {
+ m_itemsSet |= Outline;
+
+ m_outline = color;
+
+ changed();
+ }
+}
+
+void KateAttribute::setTextColor(const QColor& color)
+{
+ if (!(m_itemsSet & TextColor) || m_textColor != color)
+ {
+ m_itemsSet |= TextColor;
+
+ m_textColor = color;
+
+ changed();
+ }
+}
+
+void KateAttribute::setSelectedTextColor(const QColor& color)
+{
+ if (!(m_itemsSet & SelectedTextColor) || m_selectedTextColor != color)
+ {
+ m_itemsSet |= SelectedTextColor;
+
+ m_selectedTextColor = color;
+
+ changed();
+ }
+}
+
+void KateAttribute::setBGColor(const QColor& color)
+{
+ if (!(m_itemsSet & BGColor) || m_bgColor != color)
+ {
+ m_itemsSet |= BGColor;
+
+ m_bgColor = color;
+
+ changed();
+ }
+}
+
+void KateAttribute::setSelectedBGColor(const QColor& color)
+{
+ if (!(m_itemsSet & SelectedBGColor) || m_selectedBGColor != color)
+ {
+ m_itemsSet |= SelectedBGColor;
+
+ m_selectedBGColor = color;
+
+ changed();
+ }
+}
+
+bool operator ==(const KateAttribute& h1, const KateAttribute& h2)
+{
+ if (h1.m_itemsSet != h2.m_itemsSet)
+ return false;
+
+ if (h1.itemSet(KateAttribute::Weight))
+ if (h1.m_weight != h2.m_weight)
+ return false;
+
+ if (h1.itemSet(KateAttribute::Italic))
+ if (h1.m_italic != h2.m_italic)
+ return false;
+
+ if (h1.itemSet(KateAttribute::Underline))
+ if (h1.m_underline != h2.m_underline)
+ return false;
+
+ if (h1.itemSet(KateAttribute::StrikeOut))
+ if (h1.m_strikeout != h2.m_strikeout)
+ return false;
+
+ if (h1.itemSet(KateAttribute::Outline))
+ if (h1.m_outline != h2.m_outline)
+ return false;
+
+ if (h1.itemSet(KateAttribute::TextColor))
+ if (h1.m_textColor != h2.m_textColor)
+ return false;
+
+ if (h1.itemSet(KateAttribute::SelectedTextColor))
+ if (h1.m_selectedTextColor != h2.m_selectedTextColor)
+ return false;
+
+ if (h1.itemSet(KateAttribute::BGColor))
+ if (h1.m_bgColor != h2.m_bgColor)
+ return false;
+
+ if (h1.itemSet(KateAttribute::SelectedBGColor))
+ if (h1.m_selectedBGColor != h2.m_selectedBGColor)
+ return false;
+
+ return true;
+}
+
+bool operator !=(const KateAttribute& h1, const KateAttribute& h2)
+{
+ return !(h1 == h2);
+}
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/kateattribute.h b/kate/part/kateattribute.h
new file mode 100644
index 000000000..c4933e690
--- /dev/null
+++ b/kate/part/kateattribute.h
@@ -0,0 +1,147 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2003 Hamish Rodda <rodda@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KATE_ATTRIBUTE_H__
+#define __KATE_ATTRIBUTE_H__
+
+#include "katefont.h"
+
+#include <qcolor.h>
+
+/**
+ * The Attribute class incorporates all text decorations supported by Kate.
+ *
+ * TODO: store the actual font as well.
+ * TODO: update changed mechanism - use separate bitfield
+ */
+class KateAttribute
+{
+public:
+ enum items {
+ Weight = 0x1,
+ Bold = 0x2,
+ Italic = 0x4,
+ Underline = 0x8,
+ StrikeOut = 0x10,
+ Outline = 0x20,
+ TextColor = 0x40,
+ SelectedTextColor = 0x80,
+ BGColor = 0x100,
+ SelectedBGColor = 0x200,
+ Overline = 0x400
+ };
+
+ KateAttribute();
+ virtual ~KateAttribute();
+
+ QFont font(const QFont& ref);
+
+ inline int width(KateFontStruct& fs, const QString& text, int col, int tabWidth) const
+ { return fs.width(text, col, bold(), italic(), tabWidth); };
+
+ // Non-preferred function when you have a string and you want one char's width!!
+ inline int width(KateFontStruct& fs, const QChar& c, int tabWidth) const
+ { return fs.width(c, bold(), italic(), tabWidth); };
+
+ inline bool itemSet(int item) const
+ { return item & m_itemsSet; };
+
+ inline bool isSomethingSet() const
+ { return m_itemsSet; };
+
+ inline int itemsSet() const
+ { return m_itemsSet; };
+
+ inline void clearAttribute(int item)
+ { m_itemsSet &= (~item); }
+
+ inline int weight() const
+ { return m_weight; };
+
+ void setWeight(int weight);
+
+ inline bool bold() const
+ { return weight() >= QFont::Bold; };
+
+ void setBold(bool enable = true);
+
+ inline bool italic() const
+ { return m_italic; };
+
+ void setItalic(bool enable = true);
+
+ inline bool overline() const
+ { return m_overline; };
+
+ void setOverline(bool enable = true);
+
+ inline bool underline() const
+ { return m_underline; };
+
+ void setUnderline(bool enable = true);
+
+ inline bool strikeOut() const
+ { return m_strikeout; };
+
+ void setStrikeOut(bool enable = true);
+
+ inline const QColor& outline() const
+ { return m_outline; };
+
+ void setOutline(const QColor& color);
+
+ inline const QColor& textColor() const
+ { return m_textColor; };
+
+ void setTextColor(const QColor& color);
+
+ inline const QColor& selectedTextColor() const
+ { return m_selectedTextColor; };
+
+ void setSelectedTextColor(const QColor& color);
+
+ inline const QColor& bgColor() const
+ { return m_bgColor; };
+
+ void setBGColor(const QColor& color);
+
+ inline const QColor& selectedBGColor() const
+ { return m_selectedBGColor; };
+
+ void setSelectedBGColor(const QColor& color);
+
+ KateAttribute& operator+=(const KateAttribute& a);
+
+ friend bool operator ==(const KateAttribute& h1, const KateAttribute& h2);
+ friend bool operator !=(const KateAttribute& h1, const KateAttribute& h2);
+
+ virtual void changed() { m_changed = true; };
+ bool isChanged() { bool ret = m_changed; m_changed = false; return ret; };
+
+ void clear();
+
+private:
+ int m_weight;
+ bool m_italic, m_underline, m_overline,m_strikeout, m_changed;
+ QColor m_outline, m_textColor, m_selectedTextColor, m_bgColor, m_selectedBGColor;
+ int m_itemsSet;
+};
+
+#endif
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/kateautoindent.cpp b/kate/part/kateautoindent.cpp
new file mode 100644
index 000000000..7c58b6051
--- /dev/null
+++ b/kate/part/kateautoindent.cpp
@@ -0,0 +1,2543 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2003 Jesse Yurkovich <yurkjes@iit.edu>
+ Copyright (C) 2004 >Anders Lund <anders@alweb.dk> (KateVarIndent class)
+ Copyright (C) 2005 Dominik Haumann <dhdev@gmx.de> (basic support for config page)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kateautoindent.h"
+#include "kateautoindent.moc"
+
+#include "kateconfig.h"
+#include "katehighlight.h"
+#include "katefactory.h"
+#include "katejscript.h"
+#include "kateview.h"
+
+#include <klocale.h>
+#include <kdebug.h>
+#include <kpopupmenu.h>
+
+#include <cctype>
+
+//BEGIN KateAutoIndent
+
+KateAutoIndent *KateAutoIndent::createIndenter (KateDocument *doc, uint mode)
+{
+ if (mode == KateDocumentConfig::imNormal)
+ return new KateNormalIndent (doc);
+ else if (mode == KateDocumentConfig::imCStyle)
+ return new KateCSmartIndent (doc);
+ else if (mode == KateDocumentConfig::imPythonStyle)
+ return new KatePythonIndent (doc);
+ else if (mode == KateDocumentConfig::imXmlStyle)
+ return new KateXmlIndent (doc);
+ else if (mode == KateDocumentConfig::imCSAndS)
+ return new KateCSAndSIndent (doc);
+ else if ( mode == KateDocumentConfig::imVarIndent )
+ return new KateVarIndent ( doc );
+// else if ( mode == KateDocumentConfig::imScriptIndent)
+// return new KateScriptIndent ( doc );
+
+ return new KateAutoIndent (doc);
+}
+
+QStringList KateAutoIndent::listModes ()
+{
+ QStringList l;
+
+ l << modeDescription(KateDocumentConfig::imNone);
+ l << modeDescription(KateDocumentConfig::imNormal);
+ l << modeDescription(KateDocumentConfig::imCStyle);
+ l << modeDescription(KateDocumentConfig::imPythonStyle);
+ l << modeDescription(KateDocumentConfig::imXmlStyle);
+ l << modeDescription(KateDocumentConfig::imCSAndS);
+ l << modeDescription(KateDocumentConfig::imVarIndent);
+// l << modeDescription(KateDocumentConfig::imScriptIndent);
+
+ return l;
+}
+
+QString KateAutoIndent::modeName (uint mode)
+{
+ if (mode == KateDocumentConfig::imNormal)
+ return QString ("normal");
+ else if (mode == KateDocumentConfig::imCStyle)
+ return QString ("cstyle");
+ else if (mode == KateDocumentConfig::imPythonStyle)
+ return QString ("python");
+ else if (mode == KateDocumentConfig::imXmlStyle)
+ return QString ("xml");
+ else if (mode == KateDocumentConfig::imCSAndS)
+ return QString ("csands");
+ else if ( mode == KateDocumentConfig::imVarIndent )
+ return QString( "varindent" );
+// else if ( mode == KateDocumentConfig::imScriptIndent )
+// return QString( "scriptindent" );
+
+ return QString ("none");
+}
+
+QString KateAutoIndent::modeDescription (uint mode)
+{
+ if (mode == KateDocumentConfig::imNormal)
+ return i18n ("Normal");
+ else if (mode == KateDocumentConfig::imCStyle)
+ return i18n ("C Style");
+ else if (mode == KateDocumentConfig::imPythonStyle)
+ return i18n ("Python Style");
+ else if (mode == KateDocumentConfig::imXmlStyle)
+ return i18n ("XML Style");
+ else if (mode == KateDocumentConfig::imCSAndS)
+ return i18n ("S&S C Style");
+ else if ( mode == KateDocumentConfig::imVarIndent )
+ return i18n("Variable Based Indenter");
+// else if ( mode == KateDocumentConfig::imScriptIndent )
+// return i18n("JavaScript Indenter");
+
+ return i18n ("None");
+}
+
+uint KateAutoIndent::modeNumber (const QString &name)
+{
+ if (modeName(KateDocumentConfig::imNormal) == name)
+ return KateDocumentConfig::imNormal;
+ else if (modeName(KateDocumentConfig::imCStyle) == name)
+ return KateDocumentConfig::imCStyle;
+ else if (modeName(KateDocumentConfig::imPythonStyle) == name)
+ return KateDocumentConfig::imPythonStyle;
+ else if (modeName(KateDocumentConfig::imXmlStyle) == name)
+ return KateDocumentConfig::imXmlStyle;
+ else if (modeName(KateDocumentConfig::imCSAndS) == name)
+ return KateDocumentConfig::imCSAndS;
+ else if ( modeName( KateDocumentConfig::imVarIndent ) == name )
+ return KateDocumentConfig::imVarIndent;
+// else if ( modeName( KateDocumentConfig::imScriptIndent ) == name )
+// return KateDocumentConfig::imScriptIndent;
+
+ return KateDocumentConfig::imNone;
+}
+
+bool KateAutoIndent::hasConfigPage (uint mode)
+{
+// if ( mode == KateDocumentConfig::imScriptIndent )
+// return true;
+
+ return false;
+}
+
+IndenterConfigPage* KateAutoIndent::configPage(QWidget *parent, uint mode)
+{
+// if ( mode == KateDocumentConfig::imScriptIndent )
+// return new ScriptIndentConfigPage(parent, "script_indent_config_page");
+
+ return 0;
+}
+
+KateAutoIndent::KateAutoIndent (KateDocument *_doc)
+: QObject(), doc(_doc)
+{
+}
+KateAutoIndent::~KateAutoIndent ()
+{
+}
+
+//END KateAutoIndent
+
+//BEGIN KateViewIndentAction
+KateViewIndentationAction::KateViewIndentationAction(KateDocument *_doc, const QString& text, QObject* parent, const char* name)
+ : KActionMenu (text, parent, name), doc(_doc)
+{
+ connect(popupMenu(),SIGNAL(aboutToShow()),this,SLOT(slotAboutToShow()));
+}
+
+void KateViewIndentationAction::slotAboutToShow()
+{
+ QStringList modes = KateAutoIndent::listModes ();
+
+ popupMenu()->clear ();
+ for (uint z=0; z<modes.size(); ++z)
+ popupMenu()->insertItem ( '&' + KateAutoIndent::modeDescription(z).replace('&', "&&"), this, SLOT(setMode(int)), 0, z);
+
+ popupMenu()->setItemChecked (doc->config()->indentationMode(), true);
+}
+
+void KateViewIndentationAction::setMode (int mode)
+{
+ doc->config()->setIndentationMode((uint)mode);
+}
+//END KateViewIndentationAction
+
+//BEGIN KateNormalIndent
+
+KateNormalIndent::KateNormalIndent (KateDocument *_doc)
+ : KateAutoIndent (_doc)
+{
+ // if highlighting changes, update attributes
+ connect(_doc, SIGNAL(hlChanged()), this, SLOT(updateConfig()));
+}
+
+KateNormalIndent::~KateNormalIndent ()
+{
+}
+
+void KateNormalIndent::updateConfig ()
+{
+ KateDocumentConfig *config = doc->config();
+
+ useSpaces = config->configFlags() & KateDocument::cfSpaceIndent || config->configFlags() & KateDocumentConfig::cfReplaceTabsDyn;
+ mixedIndent = useSpaces && config->configFlags() & KateDocumentConfig::cfMixedIndent;
+ keepProfile = config->configFlags() & KateDocument::cfKeepIndentProfile;
+ tabWidth = config->tabWidth();
+ indentWidth = useSpaces? config->indentationWidth() : tabWidth;
+
+ commentAttrib = 255;
+ doxyCommentAttrib = 255;
+ regionAttrib = 255;
+ symbolAttrib = 255;
+ alertAttrib = 255;
+ tagAttrib = 255;
+ wordAttrib = 255;
+ keywordAttrib = 255;
+ normalAttrib = 255;
+ extensionAttrib = 255;
+ preprocessorAttrib = 255;
+ stringAttrib = 255;
+ charAttrib = 255;
+
+ KateHlItemDataList items;
+ doc->highlight()->getKateHlItemDataListCopy (0, items);
+
+ for (uint i=0; i<items.count(); i++)
+ {
+ QString name = items.at(i)->name;
+ if (name.find("Comment") != -1 && commentAttrib == 255)
+ {
+ commentAttrib = i;
+ }
+ else if (name.find("Region Marker") != -1 && regionAttrib == 255)
+ {
+ regionAttrib = i;
+ }
+ else if (name.find("Symbol") != -1 && symbolAttrib == 255)
+ {
+ symbolAttrib = i;
+ }
+ else if (name.find("Alert") != -1)
+ {
+ alertAttrib = i;
+ }
+ else if (name.find("Comment") != -1 && commentAttrib != 255 && doxyCommentAttrib == 255)
+ {
+ doxyCommentAttrib = i;
+ }
+ else if (name.find("Tags") != -1 && tagAttrib == 255)
+ {
+ tagAttrib = i;
+ }
+ else if (name.find("Word") != -1 && wordAttrib == 255)
+ {
+ wordAttrib = i;
+ }
+ else if (name.find("Keyword") != -1 && keywordAttrib == 255)
+ {
+ keywordAttrib = i;
+ }
+ else if (name.find("Normal") != -1 && normalAttrib == 255)
+ {
+ normalAttrib = i;
+ }
+ else if (name.find("Extensions") != -1 && extensionAttrib == 255)
+ {
+ extensionAttrib = i;
+ }
+ else if (name.find("Preprocessor") != -1 && preprocessorAttrib == 255)
+ {
+ preprocessorAttrib = i;
+ }
+ else if (name.find("String") != -1 && stringAttrib == 255)
+ {
+ stringAttrib = i;
+ }
+ else if (name.find("Char") != -1 && charAttrib == 255)
+ {
+ charAttrib = i;
+ }
+ }
+}
+
+bool KateNormalIndent::isBalanced (KateDocCursor &begin, const KateDocCursor &end, QChar open, QChar close, uint &pos) const
+{
+ int parenOpen = 0;
+ bool atLeastOne = false;
+ bool getNext = false;
+
+ pos = doc->plainKateTextLine(begin.line())->firstChar();
+
+ // Iterate one-by-one finding opening and closing chars
+ // Assume that open and close are 'Symbol' characters
+ while (begin < end)
+ {
+ QChar c = begin.currentChar();
+ if (begin.currentAttrib() == symbolAttrib)
+ {
+ if (c == open)
+ {
+ if (!atLeastOne)
+ {
+ atLeastOne = true;
+ getNext = true;
+ pos = measureIndent(begin) + 1;
+ }
+ parenOpen++;
+ }
+ else if (c == close)
+ {
+ parenOpen--;
+ }
+ }
+ else if (getNext && !c.isSpace())
+ {
+ getNext = false;
+ pos = measureIndent(begin);
+ }
+
+ if (atLeastOne && parenOpen <= 0)
+ return true;
+
+ if (!begin.moveForward(1))
+ break;
+ }
+
+ return (atLeastOne) ? false : true;
+}
+
+bool KateNormalIndent::skipBlanks (KateDocCursor &cur, KateDocCursor &max, bool newline) const
+{
+ int curLine = cur.line();
+ if (newline)
+ cur.moveForward(1);
+
+ if (cur >= max)
+ return false;
+
+ do
+ {
+ uchar attrib = cur.currentAttrib();
+ const QString hlFile = doc->highlight()->hlKeyForAttrib( attrib );
+
+ if (attrib != commentAttrib && attrib != regionAttrib && attrib != alertAttrib && attrib != preprocessorAttrib && !hlFile.endsWith("doxygen.xml"))
+ {
+ QChar c = cur.currentChar();
+ if (!c.isNull() && !c.isSpace())
+ break;
+ }
+
+ if (!cur.moveForward(1))
+ {
+ // not able to move forward, so set cur to max
+ cur = max;
+ break;
+ }
+ // Make sure col is 0 if we spill into next line i.e. count the '\n' as a character
+ if (curLine != cur.line())
+ {
+ if (!newline)
+ break;
+ curLine = cur.line();
+ cur.setCol(0);
+ }
+ } while (cur < max);
+
+ if (cur > max)
+ cur = max;
+ return true;
+}
+
+uint KateNormalIndent::measureIndent (KateDocCursor &cur) const
+{
+ // We cannot short-cut by checking for useSpaces because there may be
+ // tabs in the line despite this setting.
+
+ return doc->plainKateTextLine(cur.line())->cursorX(cur.col(), tabWidth);
+}
+
+QString KateNormalIndent::tabString(uint pos) const
+{
+ QString s;
+ pos = kMin (pos, 80U); // sanity check for large values of pos
+
+ if (!useSpaces || mixedIndent)
+ {
+ while (pos >= tabWidth)
+ {
+ s += '\t';
+ pos -= tabWidth;
+ }
+ }
+ while (pos > 0)
+ {
+ s += ' ';
+ pos--;
+ }
+ return s;
+}
+
+void KateNormalIndent::processNewline (KateDocCursor &begin, bool /*needContinue*/)
+{
+ int line = begin.line() - 1;
+ int pos = begin.col();
+
+ while ((line > 0) && (pos < 0)) // search a not empty text line
+ pos = doc->plainKateTextLine(--line)->firstChar();
+
+ if (pos > 0)
+ {
+ QString filler = doc->text(line, 0, line, pos);
+ doc->insertText(begin.line(), 0, filler);
+ begin.setCol(filler.length());
+ }
+ else
+ begin.setCol(0);
+}
+
+//END
+
+//BEGIN KateCSmartIndent
+
+KateCSmartIndent::KateCSmartIndent (KateDocument *doc)
+: KateNormalIndent (doc),
+ allowSemi (false),
+ processingBlock (false)
+{
+ kdDebug(13030)<<"CREATING KATECSMART INTDETER"<<endl;
+}
+
+KateCSmartIndent::~KateCSmartIndent ()
+{
+
+}
+
+void KateCSmartIndent::processLine (KateDocCursor &line)
+{
+ kdDebug(13030)<<"PROCESSING LINE "<<line.line()<<endl;
+ KateTextLine::Ptr textLine = doc->plainKateTextLine(line.line());
+
+ int firstChar = textLine->firstChar();
+ // Empty line is worthless ... but only when doing more than 1 line
+ if (firstChar == -1 && processingBlock)
+ return;
+
+ uint indent = 0;
+
+ // TODO Here we do not check for beginning and ending comments ...
+ QChar first = textLine->getChar(firstChar);
+ QChar last = textLine->getChar(textLine->lastChar());
+
+ if (first == '}')
+ {
+ indent = findOpeningBrace(line);
+ }
+ else if (first == ')')
+ {
+ indent = findOpeningParen(line);
+ }
+ else if (first == '{')
+ {
+ // If this is the first brace, we keep the indent at 0
+ KateDocCursor temp(line.line(), firstChar, doc);
+ if (!firstOpeningBrace(temp))
+ indent = calcIndent(temp, false);
+ }
+ else if (first == ':')
+ {
+ // Initialization lists (handle c++ and c#)
+ int pos = findOpeningBrace(line);
+ if (pos == 0)
+ indent = indentWidth;
+ else
+ indent = pos + (indentWidth * 2);
+ }
+ else if (last == ':')
+ {
+ if (textLine->stringAtPos (firstChar, "case") ||
+ textLine->stringAtPos (firstChar, "default") ||
+ textLine->stringAtPos (firstChar, "public") ||
+ textLine->stringAtPos (firstChar, "private") ||
+ textLine->stringAtPos (firstChar, "protected") ||
+ textLine->stringAtPos (firstChar, "signals") ||
+ textLine->stringAtPos (firstChar, "Q_SIGNALS") ||
+ textLine->stringAtPos (firstChar, "Q_SLOTS") ||
+ textLine->stringAtPos (firstChar, "slots"))
+ {
+ indent = findOpeningBrace(line) + indentWidth;
+ }
+ }
+ else if (first == '*')
+ {
+ if (last == '/')
+ {
+ int lineEnd = textLine->lastChar();
+ if (lineEnd > 0 && textLine->getChar(lineEnd - 1) == '*')
+ {
+ indent = findOpeningComment(line);
+ if (textLine->attribute(firstChar) == doxyCommentAttrib)
+ indent++;
+ }
+ else
+ return;
+ }
+ else
+ {
+ KateDocCursor temp = line;
+ if (textLine->attribute(firstChar) == doxyCommentAttrib)
+ indent = calcIndent(temp, false) + 1;
+ else
+ indent = calcIndent(temp, true);
+ }
+ }
+ else if (first == '#')
+ {
+ // c# regions
+ if (textLine->stringAtPos (firstChar, "#region") ||
+ textLine->stringAtPos (firstChar, "#endregion"))
+ {
+ KateDocCursor temp = line;
+ indent = calcIndent(temp, true);
+ }
+ }
+ else
+ {
+ // Everything else ...
+ if (first == '/' && last != '/')
+ return;
+
+ KateDocCursor temp = line;
+ indent = calcIndent(temp, true);
+ if (indent == 0)
+ {
+ KateNormalIndent::processNewline(line, true);
+ return;
+ }
+ }
+
+ // Slightly faster if we don't indent what we don't have to
+ if (indent != measureIndent(line) || first == '}' || first == '{' || first == '#')
+ {
+ doc->removeText(line.line(), 0, line.line(), firstChar);
+ QString filler = tabString(indent);
+ if (indent > 0) doc->insertText(line.line(), 0, filler);
+ if (!processingBlock) line.setCol(filler.length());
+ }
+}
+
+void KateCSmartIndent::processSection (const KateDocCursor &begin, const KateDocCursor &end)
+{
+ kdDebug(13030)<<"PROCESS SECTION"<<endl;
+ KateDocCursor cur = begin;
+ QTime t;
+ t.start();
+
+ processingBlock = (end.line() - cur.line() > 0) ? true : false;
+
+ while (cur.line() <= end.line())
+ {
+ processLine (cur);
+ if (!cur.gotoNextLine())
+ break;
+ }
+
+ processingBlock = false;
+ kdDebug(13030) << "+++ total: " << t.elapsed() << endl;
+}
+
+bool KateCSmartIndent::handleDoxygen (KateDocCursor &begin)
+{
+ // Factor out the rather involved Doxygen stuff here ...
+ int line = begin.line();
+ int first = -1;
+ while ((line > 0) && (first < 0))
+ first = doc->plainKateTextLine(--line)->firstChar();
+
+ if (first >= 0)
+ {
+ KateTextLine::Ptr textLine = doc->plainKateTextLine(line);
+ bool insideDoxygen = false;
+ bool justAfterDoxygen = false;
+ if (textLine->attribute(first) == doxyCommentAttrib || textLine->attribute(textLine->lastChar()) == doxyCommentAttrib)
+ {
+ const int last = textLine->lastChar();
+ if (last <= 0 || !(justAfterDoxygen = textLine->stringAtPos(last-1, "*/")))
+ insideDoxygen = true;
+ if (justAfterDoxygen)
+ justAfterDoxygen &= textLine->string().find("/**") < 0;
+ while (textLine->attribute(first) != doxyCommentAttrib && first <= textLine->lastChar())
+ first++;
+ if (textLine->stringAtPos(first, "//"))
+ return false;
+ }
+
+ // Align the *'s and then go ahead and insert one too ...
+ if (insideDoxygen)
+ {
+ textLine = doc->plainKateTextLine(begin.line());
+ first = textLine->firstChar();
+ int indent = findOpeningComment(begin);
+ QString filler = tabString (indent);
+
+ bool doxygenAutoInsert = doc->config()->configFlags() & KateDocumentConfig::cfDoxygenAutoTyping;
+
+ if ( doxygenAutoInsert &&
+ ((first < 0) || (!textLine->stringAtPos(first, "*/") && !textLine->stringAtPos(first, "*"))))
+ {
+ filler = filler + " * ";
+ }
+
+ doc->removeText (begin.line(), 0, begin.line(), first);
+ doc->insertText (begin.line(), 0, filler);
+ begin.setCol(filler.length());
+
+ return true;
+ }
+ // Align position with beginning of doxygen comment. Otherwise the
+ // indentation is one too much.
+ else if (justAfterDoxygen)
+ {
+ textLine = doc->plainKateTextLine(begin.line());
+ first = textLine->firstChar();
+ int indent = findOpeningComment(begin);
+ QString filler = tabString (indent);
+
+ doc->removeText (begin.line(), 0, begin.line(), first);
+ doc->insertText (begin.line(), 0, filler);
+ begin.setCol(filler.length());
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void KateCSmartIndent::processNewline (KateDocCursor &begin, bool needContinue)
+{
+ if (!handleDoxygen (begin))
+ {
+ KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line());
+ bool inMiddle = textLine->firstChar() > -1;
+
+ int indent = calcIndent (begin, needContinue);
+
+ if (indent > 0 || inMiddle)
+ {
+ QString filler = tabString (indent);
+ doc->insertText (begin.line(), 0, filler);
+ begin.setCol(filler.length());
+
+ // Handles cases where user hits enter at the beginning or middle of text
+ if (inMiddle)
+ {
+ processLine(begin);
+ begin.setCol(textLine->firstChar());
+ }
+ }
+ else
+ {
+ KateNormalIndent::processNewline (begin, needContinue);
+ }
+
+ if (begin.col() < 0)
+ begin.setCol(0);
+ }
+}
+
+/**
+ * Returns true when the given attribute matches any "colon influence immune"
+ * attribute
+ * @param indenter indenter
+ * @param attr1 attribute of previous char
+ * @param attr2 attribute of char preceding previous char
+ * @param prev1 previous character (0 if none)
+ * @param prev2 character preceding previous character (0 if none)
+ */
+static inline bool isColonImmune(const KateNormalIndent &indenter,
+ uchar attr1, uchar attr2,
+ QChar prev1, QChar prev2)
+{
+ return attr1 == indenter.preprocessorAttrib
+ // FIXME: no way to discriminate against multiline comment and single
+ // line comment. Therefore, using prev? is futile.
+ || attr1 == indenter.commentAttrib /*&& prev2 != '*' && prev1 != '/'*/
+ || attr1 == indenter.doxyCommentAttrib
+ || attr1 == indenter.stringAttrib && (attr2 != indenter.stringAttrib
+ || (prev1 != '"' || prev2 == '\\' && attr2 == indenter.charAttrib))
+ || prev1 == '\'' && attr1 != indenter.charAttrib;
+}
+
+/**
+ * Returns true when the colon is allowed to reindent the current line
+ * @param indenter current indenter
+ * @param line current line
+ * @param curCol column of most recently input character
+ */
+static inline bool colonPermitsReindent(const KateNormalIndent &indenter,
+ const KateTextLine::Ptr &line,
+ int curCol
+ )
+{
+ const QString txt = line->string(0,curCol);
+ // do we have any significant preceding colon?
+ for (int pos = 0; (pos = txt.find(':', pos)) >= 0; pos++) {
+ if (line->attribute(pos) == indenter.symbolAttrib)
+ // yes, it has already contributed to this line's indentation, don't
+ // indent again
+ return false;
+ }
+
+ // otherwise, check whether this colon is not within an influence
+ // immune attribute range
+ return !isColonImmune(indenter, line->attribute(curCol - 1),
+ line->attribute(curCol - 2),
+ txt[curCol - 1], txt[curCol - 2]);
+}
+
+void KateCSmartIndent::processChar(QChar c)
+{
+ // You may be curious about 'n' among the triggers:
+ // It is used to discriminate C#'s #region/#endregion which are indented
+ // against normal preprocessing statements which aren't indented.
+ static const QString triggers("}{)/:#n");
+ static const QString firstTriggers("}{)/:#");
+ static const QString lastTriggers(":n");
+ if (triggers.find(c) < 0)
+ return;
+
+ KateView *view = doc->activeView();
+ int curCol = view->cursorColumnReal() - 1;
+ KateDocCursor begin(view->cursorLine(), 0, doc);
+
+ KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line());
+ const QChar curChar = textLine->getChar(curCol);
+ const int first = textLine->firstChar();
+ const QChar firstChar = textLine->getChar(first);
+
+#if 0 // nice try
+ // Only indent on symbols or preprocessing directives -- never on
+ // anything else
+ kdDebug() << "curChar " << curChar << " curCol " << curCol << " textlen " << textLine->length() << " a " << textLine->attribute( curCol ) << " sym " << symbolAttrib << " pp " << preprocessorAttrib << endl;
+ if (!(((curChar == '#' || curChar == 'n')
+ && textLine->attribute( curCol ) == preprocessorAttrib)
+ || textLine->attribute( curCol ) == symbolAttrib)
+ )
+ return;
+ kdDebug() << "curChar " << curChar << endl;
+#endif
+
+ if (c == 'n')
+ {
+ if (firstChar != '#' || textLine->string(curCol-5, 5) != QString::fromLatin1("regio"))
+ return;
+ }
+
+ if ( c == '/' )
+ {
+ // dominik: if line is "* /", change it to "*/"
+ if ( textLine->attribute( begin.col() ) == doxyCommentAttrib )
+ {
+ // if the first char exists and is a '*', and the next non-space-char
+ // is already the just typed '/', concatenate it to "*/".
+ if ( first != -1
+ && firstChar == '*'
+ && textLine->nextNonSpaceChar( first+1 ) == view->cursorColumnReal()-1 )
+ doc->removeText( view->cursorLine(), first+1, view->cursorLine(), view->cursorColumnReal()-1);
+ }
+
+ // ls: never have comments change the indentation.
+ return;
+ }
+
+ // ls: only reindent line if the user actually expects it
+ // I. e. take action on single braces on line or last colon, but inhibit
+ // any reindentation if any of those characters appear amidst some section
+ // of the line
+ const QChar lastChar = textLine->getChar(textLine->lastChar());
+ int pos;
+ if (((c == firstChar && firstTriggers.find(firstChar) >= 0)
+ || (c == lastChar && lastTriggers.find(lastChar) >= 0))
+ && (c != ':' || colonPermitsReindent(*this, textLine, curCol)))
+ processLine(begin);
+}
+
+
+uint KateCSmartIndent::calcIndent(KateDocCursor &begin, bool needContinue)
+{
+ KateTextLine::Ptr textLine;
+ KateDocCursor cur = begin;
+
+ uint anchorIndent = 0;
+ int anchorPos = 0;
+ int parenCount = 0; // Possibly in a multiline for stmt. Used to skip ';' ...
+ bool found = false;
+ bool isSpecial = false;
+ bool potentialAnchorSeen = false;
+ bool isArg = false; // ...arg,<newline>
+ bool parenthesizedArg = false; // ...(arg,<newline>
+
+ //kdDebug(13030) << "calcIndent begin line:" << begin.line() << " col:" << begin.col() << endl;
+
+ // Find Indent Anchor Point
+ while (cur.gotoPreviousLine())
+ {
+ isSpecial = found = false;
+ textLine = doc->plainKateTextLine(cur.line());
+
+ // Skip comments and handle cases like if (...) { stmt;
+ int pos = textLine->lastChar();
+ int openCount = 0;
+ int otherAnchor = -1;
+ do
+ {
+ if (textLine->attribute(pos) == symbolAttrib)
+ {
+ QChar tc = textLine->getChar (pos);
+ if ((tc == ';' || tc == ':' || tc == ',') && otherAnchor == -1 && parenCount <= 0) {
+ otherAnchor = pos, potentialAnchorSeen = true;
+ isArg = tc == ',';
+ } else if (tc == ')')
+ parenCount++;
+ else if (tc == '(')
+ parenCount--, parenthesizedArg = isArg, potentialAnchorSeen = true;
+ else if (tc == '}')
+ openCount--;
+ else if (tc == '{')
+ {
+ openCount++, potentialAnchorSeen = true;
+ if (openCount == 1)
+ break;
+ }
+ }
+ } while (--pos >= textLine->firstChar());
+
+ if (openCount != 0 || otherAnchor != -1)
+ {
+ found = true;
+ QChar c;
+ if (openCount > 0)
+ c = '{';
+ else if (openCount < 0)
+ c = '}';
+ else if (otherAnchor >= 0)
+ c = textLine->getChar (otherAnchor);
+
+ int specialIndent = 0;
+ if (c == ':' && needContinue)
+ {
+ QChar ch;
+ specialIndent = textLine->firstChar();
+ if (textLine->stringAtPos(specialIndent, "case"))
+ ch = textLine->getChar(specialIndent + 4);
+ else if (textLine->stringAtPos(specialIndent, "default"))
+ ch = textLine->getChar(specialIndent + 7);
+ else if (textLine->stringAtPos(specialIndent, "public"))
+ ch = textLine->getChar(specialIndent + 6);
+ else if (textLine->stringAtPos(specialIndent, "private"))
+ ch = textLine->getChar(specialIndent + 7);
+ else if (textLine->stringAtPos(specialIndent, "protected"))
+ ch = textLine->getChar(specialIndent + 9);
+ else if (textLine->stringAtPos(specialIndent, "signals"))
+ ch = textLine->getChar(specialIndent + 7);
+ else if (textLine->stringAtPos(specialIndent, "Q_SIGNALS"))
+ ch = textLine->getChar(specialIndent + 9);
+ else if (textLine->stringAtPos(specialIndent, "slots"))
+ ch = textLine->getChar(specialIndent + 5);
+ else if (textLine->stringAtPos(specialIndent, "Q_SLOTS"))
+ ch = textLine->getChar(specialIndent + 7);
+
+ if (ch.isNull() || (!ch.isSpace() && ch != '(' && ch != ':'))
+ continue;
+
+ KateDocCursor lineBegin = cur;
+ lineBegin.setCol(specialIndent);
+ specialIndent = measureIndent(lineBegin);
+ isSpecial = true;
+ }
+
+ // Move forward past blank lines
+ KateDocCursor skip = cur;
+ skip.setCol(textLine->lastChar());
+ bool result = skipBlanks(skip, begin, true);
+
+ anchorPos = skip.col();
+ anchorIndent = measureIndent(skip);
+
+ //kdDebug(13030) << "calcIndent anchorPos:" << anchorPos << " anchorIndent:" << anchorIndent << " at line:" << skip.line() << endl;
+
+ // Accept if it's before requested position or if it was special
+ if (result && skip < begin)
+ {
+ cur = skip;
+ break;
+ }
+ else if (isSpecial)
+ {
+ anchorIndent = specialIndent;
+ break;
+ }
+
+ // Are these on a line by themselves? (i.e. both last and first char)
+ if ((c == '{' || c == '}') && textLine->getChar(textLine->firstChar()) == c)
+ {
+ cur.setCol(anchorPos = textLine->firstChar());
+ anchorIndent = measureIndent (cur);
+ break;
+ }
+ }
+ }
+
+ // treat beginning of document as anchor position
+ if (cur.line() == 0 && cur.col() == 0 && potentialAnchorSeen)
+ found = true;
+
+ if (!found)
+ return 0;
+
+ uint continueIndent = (needContinue) ? calcContinue (cur, begin) : 0;
+ //kdDebug(13030) << "calcIndent continueIndent:" << continueIndent << endl;
+
+ // Move forward from anchor and determine last known reference character
+ // Braces take precedance over others ...
+ textLine = doc->plainKateTextLine(cur.line());
+ QChar lastChar = textLine->getChar (anchorPos);
+ int lastLine = cur.line();
+ if (lastChar == '#' || lastChar == '[')
+ {
+ // Never continue if # or [ is encountered at this point here
+ // A fail-safe really... most likely an #include, #region, or a c# attribute
+ continueIndent = 0;
+ }
+
+ int openCount = 0;
+ while (cur.validPosition() && cur < begin)
+ {
+ if (!skipBlanks(cur, begin, true))
+ return isArg && !parenthesizedArg ? begin.col() : 0;
+
+ QChar tc = cur.currentChar();
+ //kdDebug(13030) << " cur.line:" << cur.line() << " cur.col:" << cur.col() << " currentChar '" << tc << "' " << textLine->attribute(cur.col()) << endl;
+ if (cur == begin || tc.isNull())
+ break;
+
+ if (!tc.isSpace() && cur < begin)
+ {
+ uchar attrib = cur.currentAttrib();
+ if (tc == '{' && attrib == symbolAttrib)
+ openCount++;
+ else if (tc == '}' && attrib == symbolAttrib)
+ openCount--;
+
+ lastChar = tc;
+ lastLine = cur.line();
+ }
+ }
+ if (openCount > 0) // Open braces override
+ lastChar = '{';
+
+ uint indent = 0;
+ //kdDebug(13030) << "calcIndent lastChar '" << lastChar << "'" << endl;
+
+ if (lastChar == '{' || (lastChar == ':' && isSpecial && needContinue))
+ {
+ indent = anchorIndent + indentWidth;
+ }
+ else if (lastChar == '}')
+ {
+ indent = anchorIndent;
+ }
+ else if (lastChar == ';')
+ {
+ indent = anchorIndent + ((allowSemi && needContinue) ? continueIndent : 0);
+ }
+ else if (lastChar == ',' || lastChar == '(')
+ {
+ textLine = doc->plainKateTextLine(lastLine);
+ KateDocCursor start(lastLine, textLine->firstChar(), doc);
+ KateDocCursor finish(lastLine, textLine->lastChar() + 1, doc);
+ uint pos = 0;
+
+ if (isBalanced(start, finish, QChar('('), QChar(')'), pos) && false)
+ indent = anchorIndent;
+ else
+ {
+ // TODO: Config option. If we're below 48, go ahead and line them up
+ indent = ((pos < 48) ? pos : anchorIndent + (indentWidth * 2));
+ }
+ }
+ else if (!lastChar.isNull())
+ {
+ if (anchorIndent != 0)
+ indent = anchorIndent + continueIndent;
+ else
+ indent = continueIndent;
+ }
+
+ return indent;
+}
+
+uint KateCSmartIndent::calcContinue(KateDocCursor &start, KateDocCursor &end)
+{
+ KateDocCursor cur = start;
+
+ bool needsBalanced = true;
+ bool isFor = false;
+ allowSemi = false;
+
+ KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
+
+ // Handle cases such as } while (s ... by skipping the leading symbol
+ if (textLine->attribute(cur.col()) == symbolAttrib)
+ {
+ cur.moveForward(1);
+ skipBlanks(cur, end, false);
+ }
+
+ if (textLine->getChar(cur.col()) == '}')
+ {
+ skipBlanks(cur, end, true);
+ if (cur.line() != start.line())
+ textLine = doc->plainKateTextLine(cur.line());
+
+ if (textLine->stringAtPos(cur.col(), "else"))
+ cur.setCol(cur.col() + 4);
+ else
+ return indentWidth * 2;
+
+ needsBalanced = false;
+ }
+ else if (textLine->stringAtPos(cur.col(), "else"))
+ {
+ cur.setCol(cur.col() + 4);
+ needsBalanced = false;
+ int next = textLine->nextNonSpaceChar(cur.col());
+ if (next >= 0 && textLine->stringAtPos(next, "if"))
+ {
+ cur.setCol(next + 2);
+ needsBalanced = true;
+ }
+ }
+ else if (textLine->stringAtPos(cur.col(), "if"))
+ {
+ cur.setCol(cur.col() + 2);
+ }
+ else if (textLine->stringAtPos(cur.col(), "do"))
+ {
+ cur.setCol(cur.col() + 2);
+ needsBalanced = false;
+ }
+ else if (textLine->stringAtPos(cur.col(), "for"))
+ {
+ cur.setCol(cur.col() + 3);
+ isFor = true;
+ }
+ else if (textLine->stringAtPos(cur.col(), "while"))
+ {
+ cur.setCol(cur.col() + 5);
+ }
+ else if (textLine->stringAtPos(cur.col(), "switch"))
+ {
+ cur.setCol(cur.col() + 6);
+ }
+ else if (textLine->stringAtPos(cur.col(), "using"))
+ {
+ cur.setCol(cur.col() + 5);
+ }
+ else
+ {
+ return indentWidth * 2;
+ }
+
+ uint openPos = 0;
+ if (needsBalanced && !isBalanced (cur, end, QChar('('), QChar(')'), openPos))
+ {
+ allowSemi = isFor;
+ if (openPos > 0)
+ return (openPos - textLine->firstChar());
+ else
+ return indentWidth * 2;
+ }
+
+ // Check if this statement ends a line now
+ skipBlanks(cur, end, false);
+ if (cur == end)
+ return indentWidth;
+
+ if (skipBlanks(cur, end, true))
+ {
+ if (cur == end)
+ return indentWidth;
+ else
+ return indentWidth + calcContinue(cur, end);
+ }
+
+ return 0;
+}
+
+uint KateCSmartIndent::findOpeningBrace(KateDocCursor &start)
+{
+ KateDocCursor cur = start;
+ int count = 1;
+
+ // Move backwards 1 by 1 and find the opening brace
+ // Return the indent of that line
+ while (cur.moveBackward(1))
+ {
+ if (cur.currentAttrib() == symbolAttrib)
+ {
+ QChar ch = cur.currentChar();
+ if (ch == '{')
+ count--;
+ else if (ch == '}')
+ count++;
+
+ if (count == 0)
+ {
+ KateDocCursor temp(cur.line(), doc->plainKateTextLine(cur.line())->firstChar(), doc);
+ return measureIndent(temp);
+ }
+ }
+ }
+
+ return 0;
+}
+
+bool KateCSmartIndent::firstOpeningBrace(KateDocCursor &start)
+{
+ KateDocCursor cur = start;
+
+ // Are we the first opening brace at this level?
+ while(cur.moveBackward(1))
+ {
+ if (cur.currentAttrib() == symbolAttrib)
+ {
+ QChar ch = cur.currentChar();
+ if (ch == '{')
+ return false;
+ else if (ch == '}' && cur.col() == 0)
+ break;
+ }
+ }
+
+ return true;
+}
+
+uint KateCSmartIndent::findOpeningParen(KateDocCursor &start)
+{
+ KateDocCursor cur = start;
+ int count = 1;
+
+ // Move backwards 1 by 1 and find the opening (
+ // Return the indent of that line
+ while (cur.moveBackward(1))
+ {
+ if (cur.currentAttrib() == symbolAttrib)
+ {
+ QChar ch = cur.currentChar();
+ if (ch == '(')
+ count--;
+ else if (ch == ')')
+ count++;
+
+ if (count == 0)
+ return measureIndent(cur);
+ }
+ }
+
+ return 0;
+}
+
+uint KateCSmartIndent::findOpeningComment(KateDocCursor &start)
+{
+ KateDocCursor cur = start;
+
+ // Find the line with the opening /* and return the proper indent
+ do
+ {
+ KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
+
+ int pos = textLine->string().find("/*", false);
+ if (pos >= 0)
+ {
+ KateDocCursor temp(cur.line(), pos, doc);
+ return measureIndent(temp);
+ }
+
+ } while (cur.gotoPreviousLine());
+
+ return 0;
+}
+
+//END
+
+//BEGIN KatePythonIndent
+
+QRegExp KatePythonIndent::endWithColon = QRegExp( "^[^#]*:\\s*(#.*)?$" );
+QRegExp KatePythonIndent::stopStmt = QRegExp( "^\\s*(break|continue|raise|return|pass)\\b.*" );
+QRegExp KatePythonIndent::blockBegin = QRegExp( "^\\s*(class|def|if|elif|else|for|while|try)\\b.*" );
+
+KatePythonIndent::KatePythonIndent (KateDocument *doc)
+: KateNormalIndent (doc)
+{
+}
+KatePythonIndent::~KatePythonIndent ()
+{
+}
+
+void KatePythonIndent::processNewline (KateDocCursor &begin, bool /*newline*/)
+{
+ int prevLine = begin.line() - 1;
+ int prevPos = begin.col();
+
+ while ((prevLine > 0) && (prevPos < 0)) // search a not empty text line
+ prevPos = doc->plainKateTextLine(--prevLine)->firstChar();
+
+ int prevBlock = prevLine;
+ int prevBlockPos = prevPos;
+ int extraIndent = calcExtra (prevBlock, prevBlockPos, begin);
+
+ int indent = doc->plainKateTextLine(prevBlock)->cursorX(prevBlockPos, tabWidth);
+ if (extraIndent == 0)
+ {
+ if (!stopStmt.exactMatch(doc->plainKateTextLine(prevLine)->string()))
+ {
+ if (endWithColon.exactMatch(doc->plainKateTextLine(prevLine)->string()))
+ indent += indentWidth;
+ else
+ indent = doc->plainKateTextLine(prevLine)->cursorX(prevPos, tabWidth);
+ }
+ }
+ else
+ indent += extraIndent;
+
+ if (indent > 0)
+ {
+ QString filler = tabString (indent);
+ doc->insertText (begin.line(), 0, filler);
+ begin.setCol(filler.length());
+ }
+ else
+ begin.setCol(0);
+}
+
+int KatePythonIndent::calcExtra (int &prevBlock, int &pos, KateDocCursor &end)
+{
+ int nestLevel = 0;
+ bool levelFound = false;
+ while ((prevBlock > 0))
+ {
+ if (blockBegin.exactMatch(doc->plainKateTextLine(prevBlock)->string()))
+ {
+ if ((!levelFound && nestLevel == 0) || (levelFound && nestLevel - 1 <= 0))
+ {
+ pos = doc->plainKateTextLine(prevBlock)->firstChar();
+ break;
+ }
+
+ nestLevel --;
+ }
+ else if (stopStmt.exactMatch(doc->plainKateTextLine(prevBlock)->string()))
+ {
+ nestLevel ++;
+ levelFound = true;
+ }
+
+ --prevBlock;
+ }
+
+ KateDocCursor cur (prevBlock, pos, doc);
+ QChar c;
+ int extraIndent = 0;
+ while (cur.line() < end.line())
+ {
+ c = cur.currentChar();
+
+ if (c == '(')
+ extraIndent += indentWidth;
+ else if (c == ')')
+ extraIndent -= indentWidth;
+ else if (c == ':')
+ break;
+ else if (c == '\'' || c == '"' )
+ traverseString( c, cur, end );
+
+ if (c.isNull() || c == '#')
+ cur.gotoNextLine();
+ else
+ cur.moveForward(1);
+ }
+
+ return extraIndent;
+}
+
+void KatePythonIndent::traverseString( const QChar &stringChar, KateDocCursor &cur, KateDocCursor &end )
+{
+ QChar c;
+ bool escape = false;
+
+ cur.moveForward(1);
+ c = cur.currentChar();
+ while ( ( c != stringChar || escape ) && cur.line() < end.line() )
+ {
+ if ( escape )
+ escape = false;
+ else if ( c == '\\' )
+ escape = !escape;
+
+ cur.moveForward(1);
+ c = cur.currentChar();
+ }
+}
+
+//END
+
+//BEGIN KateXmlIndent
+
+/* Explanation
+
+The XML indenter simply inherits the indentation of the previous line,
+with the first line starting at 0 (of course!). For each element that
+is opened on the previous line, the indentation is increased by one
+level; for each element that is closed, it is decreased by one.
+
+We also have a special case of opening an element on one line and then
+entering attributes on the following lines, in which case we would like
+to see the following layout:
+<elem attr="..."
+ blah="..." />
+
+<x><a href="..."
+ title="..." />
+</x>
+
+This is accomplished by checking for lines that contain an unclosed open
+tag.
+
+*/
+
+const QRegExp KateXmlIndent::startsWithCloseTag("^[ \t]*</");
+const QRegExp KateXmlIndent::unclosedDoctype("<!DOCTYPE[^>]*$");
+
+KateXmlIndent::KateXmlIndent (KateDocument *doc)
+: KateNormalIndent (doc)
+{
+}
+
+KateXmlIndent::~KateXmlIndent ()
+{
+}
+
+void KateXmlIndent::processNewline (KateDocCursor &begin, bool /*newline*/)
+{
+ begin.setCol(processLine(begin.line()));
+}
+
+void KateXmlIndent::processChar (QChar c)
+{
+ if(c != '/') return;
+
+ // only alter lines that start with a close element
+ KateView *view = doc->activeView();
+ QString text = doc->plainKateTextLine(view->cursorLine())->string();
+ if(text.find(startsWithCloseTag) == -1) return;
+
+ // process it
+ processLine(view->cursorLine());
+}
+
+void KateXmlIndent::processLine (KateDocCursor &line)
+{
+ processLine (line.line());
+}
+
+void KateXmlIndent::processSection (const KateDocCursor &start, const KateDocCursor &end)
+{
+ KateDocCursor cur (start);
+ int endLine = end.line();
+
+ do {
+ processLine(cur.line());
+ if(!cur.gotoNextLine()) break;
+ } while(cur.line() < endLine);
+}
+
+void KateXmlIndent::getLineInfo (uint line, uint &prevIndent, int &numTags,
+ uint &attrCol, bool &unclosedTag)
+{
+ prevIndent = 0;
+ int firstChar;
+ KateTextLine::Ptr prevLine = 0;
+
+ // get the indentation of the first non-empty line
+ while(true) {
+ prevLine = doc->plainKateTextLine(line);
+ if( (firstChar = prevLine->firstChar()) < 0) {
+ if(!line--) return;
+ continue;
+ }
+ break;
+ }
+ prevIndent = prevLine->cursorX(prevLine->firstChar(), tabWidth);
+ QString text = prevLine->string();
+
+ // special case:
+ // <a>
+ // </a> <!-- indentation *already* decreased -->
+ // requires that we discount the </a> from the number of closed tags
+ if(text.find(startsWithCloseTag) != -1) ++numTags;
+
+ // count the number of open and close tags
+ int lastCh = 0;
+ uint pos, len = text.length();
+ bool seenOpen = false;
+ for(pos = 0; pos < len; ++pos) {
+ int ch = text.at(pos).unicode();
+ switch(ch) {
+ case '<':
+ seenOpen = true;
+ unclosedTag = true;
+ attrCol = pos;
+ ++numTags;
+ break;
+
+ // don't indent because of DOCTYPE, comment, CDATA, etc.
+ case '!':
+ if(lastCh == '<') --numTags;
+ break;
+
+ // don't indent because of xml decl or PI
+ case '?':
+ if(lastCh == '<') --numTags;
+ break;
+
+ case '>':
+ if(!seenOpen) {
+ // we are on a line like the second one here:
+ // <element attr="val"
+ // other="val">
+ // so we need to set prevIndent to the indent of the first line
+ //
+ // however, we need to special case "<!DOCTYPE" because
+ // it's not an open tag
+
+ prevIndent = 0;
+
+ for(uint backLine = line; backLine; ) {
+ // find first line with an open tag
+ KateTextLine::Ptr x = doc->plainKateTextLine(--backLine);
+ if(x->string().find('<') == -1) continue;
+
+ // recalculate the indent
+ if(x->string().find(unclosedDoctype) != -1) --numTags;
+ getLineInfo(backLine, prevIndent, numTags, attrCol, unclosedTag);
+ break;
+ }
+ }
+ if(lastCh == '/') --numTags;
+ unclosedTag = false;
+ break;
+
+ case '/':
+ if(lastCh == '<') numTags -= 2; // correct for '<', above
+ break;
+ }
+ lastCh = ch;
+ }
+
+ if(unclosedTag) {
+ // find the start of the next attribute, so we can align with it
+ do {
+ lastCh = text.at(++attrCol).unicode();
+ }while(lastCh && lastCh != ' ' && lastCh != '\t');
+
+ while(lastCh == ' ' || lastCh == '\t') {
+ lastCh = text.at(++attrCol).unicode();
+ }
+
+ attrCol = prevLine->cursorX(attrCol, tabWidth);
+ }
+}
+
+uint KateXmlIndent::processLine (uint line)
+{
+ KateTextLine::Ptr kateLine = doc->plainKateTextLine(line);
+ if(!kateLine) return 0; // sanity check
+
+ // get details from previous line
+ uint prevIndent = 0, attrCol = 0;
+ int numTags = 0;
+ bool unclosedTag = false; // for aligning attributes
+
+ if(line) {
+ getLineInfo(line - 1, prevIndent, numTags, attrCol, unclosedTag);
+ }
+
+ // compute new indent
+ int indent = 0;
+ if(unclosedTag) indent = attrCol;
+ else indent = prevIndent + numTags * indentWidth;
+ if(indent < 0) indent = 0;
+
+ // unindent lines that start with a close tag
+ if(kateLine->string().find(startsWithCloseTag) != -1) {
+ indent -= indentWidth;
+ }
+ if(indent < 0) indent = 0;
+
+ // apply new indent
+ doc->removeText(line, 0, line, kateLine->firstChar());
+ QString filler = tabString(indent);
+ doc->insertText(line, 0, filler);
+
+ return filler.length();
+}
+
+//END
+
+//BEGIN KateCSAndSIndent
+
+KateCSAndSIndent::KateCSAndSIndent (KateDocument *doc)
+: KateNormalIndent (doc)
+{
+}
+
+void KateCSAndSIndent::updateIndentString()
+{
+ if( useSpaces )
+ indentString.fill( ' ', indentWidth );
+ else
+ indentString = '\t';
+}
+
+KateCSAndSIndent::~KateCSAndSIndent ()
+{
+}
+
+void KateCSAndSIndent::processLine (KateDocCursor &line)
+{
+ KateTextLine::Ptr textLine = doc->plainKateTextLine(line.line());
+
+ if (!textLine)
+ return;
+
+ updateIndentString();
+
+ const int oldCol = line.col();
+ QString whitespace = calcIndent(line);
+ // strip off existing whitespace
+ int oldIndent = textLine->firstChar();
+ if ( oldIndent < 0 )
+ oldIndent = doc->lineLength( line.line() );
+ if( oldIndent > 0 )
+ doc->removeText(line.line(), 0, line.line(), oldIndent);
+ // add correct amount
+ doc->insertText(line.line(), 0, whitespace);
+
+ // try to preserve the cursor position in the line
+ if ( int(oldCol + whitespace.length()) >= oldIndent )
+ line.setCol( oldCol + whitespace.length() - oldIndent );
+ else
+ line.setCol( 0 );
+}
+
+void KateCSAndSIndent::processSection (const KateDocCursor &begin, const KateDocCursor &end)
+{
+ QTime t; t.start();
+ for( KateDocCursor cur = begin; cur.line() <= end.line(); )
+ {
+ processLine (cur);
+ if (!cur.gotoNextLine())
+ break;
+ }
+ kdDebug(13030) << "+++ total: " << t.elapsed() << endl;
+}
+
+/**
+ * Returns the first @p chars characters of @p line, converted to whitespace.
+ * If @p convert is set to false, characters at and after the first non-whitespace
+ * character are removed, not converted.
+ */
+static QString initialWhitespace(const KateTextLine::Ptr &line, int chars, bool convert = true)
+{
+ QString text = line->string(0, chars);
+ if( (int)text.length() < chars )
+ {
+ QString filler; filler.fill(' ',chars - text.length());
+ text += filler;
+ }
+ for( uint n = 0; n < text.length(); ++n )
+ {
+ if( text[n] != '\t' && text[n] != ' ' )
+ {
+ if( !convert )
+ return text.left( n );
+ text[n] = ' ';
+ }
+ }
+ return text;
+}
+
+QString KateCSAndSIndent::findOpeningCommentIndentation(const KateDocCursor &start)
+{
+ KateDocCursor cur = start;
+
+ // Find the line with the opening /* and return the indentation of it
+ do
+ {
+ KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
+
+ int pos = textLine->string().findRev("/*");
+ // FIXME: /* inside /* is possible. This screws up in that case...
+ if (pos >= 0)
+ return initialWhitespace(textLine, pos);
+ } while (cur.gotoPreviousLine());
+
+ // should never happen.
+ kdWarning( 13030 ) << " in a comment, but can't find the start of it" << endl;
+ return QString::null;
+}
+
+bool KateCSAndSIndent::handleDoxygen (KateDocCursor &begin)
+{
+ // Look backwards for a nonempty line
+ int line = begin.line();
+ int first = -1;
+ while ((line > 0) && (first < 0))
+ first = doc->plainKateTextLine(--line)->firstChar();
+
+ // no earlier nonempty line
+ if (first < 0)
+ return false;
+
+ KateTextLine::Ptr textLine = doc->plainKateTextLine(line);
+
+ // if the line doesn't end with a doxygen comment (that's not closed)
+ // and doesn't start with a doxygen comment (that's not closed), we don't care.
+ // note that we do need to check the start of the line, or lines ending with, say, @brief aren't
+ // recognised.
+ if ( !(textLine->attribute(textLine->lastChar()) == doxyCommentAttrib && !textLine->endingWith("*/")) &&
+ !(textLine->attribute(textLine->firstChar()) == doxyCommentAttrib && !textLine->string().contains("*/")) )
+ return false;
+
+ // our line is inside a doxygen comment. align the *'s and then maybe insert one too ...
+ textLine = doc->plainKateTextLine(begin.line());
+ first = textLine->firstChar();
+ QString indent = findOpeningCommentIndentation(begin);
+
+ bool doxygenAutoInsert = doc->config()->configFlags() & KateDocumentConfig::cfDoxygenAutoTyping;
+
+ // starts with *: indent one space more to line up *s
+ if ( first >= 0 && textLine->stringAtPos(first, "*") )
+ indent = indent + " ";
+ // does not start with *: insert one if user wants that
+ else if ( doxygenAutoInsert )
+ indent = indent + " * ";
+ // user doesn't want * inserted automatically: put in spaces?
+ //else
+ // indent = indent + " ";
+
+ doc->removeText (begin.line(), 0, begin.line(), first);
+ doc->insertText (begin.line(), 0, indent);
+ begin.setCol(indent.length());
+
+ return true;
+}
+
+/**
+ * @brief User pressed enter. Line has been split; begin is on the new line.
+ * @param begin Three unrelated variables: the new line number, where the first
+ * non-whitespace char was on the previous line, and the document.
+ * @param needContinue Something to do with indenting the current line; always true.
+ */
+void KateCSAndSIndent::processNewline (KateDocCursor &begin, bool /*needContinue*/)
+{
+ // in a comment, add a * doxygen-style.
+ if( handleDoxygen(begin) )
+ return;
+
+ // TODO: if the user presses enter in the middle of a label, maybe the first half of the
+ // label should be indented?
+
+ // where the cursor actually is...
+ int cursorPos = doc->plainKateTextLine( begin.line() )->firstChar();
+ if ( cursorPos < 0 )
+ cursorPos = doc->lineLength( begin.line() );
+ begin.setCol( cursorPos );
+
+ processLine( begin );
+}
+
+/**
+ * Does the line @p line start with a label?
+ * @note May also return @c true if the line starts in a continuation.
+ */
+bool KateCSAndSIndent::startsWithLabel( int line )
+{
+ // Get the current line.
+ KateTextLine::Ptr indentLine = doc->plainKateTextLine(line);
+ const int indentFirst = indentLine->firstChar();
+
+ // Not entirely sure what this check does.
+ int attrib = indentLine->attribute(indentFirst);
+ if (attrib != 0 && attrib != keywordAttrib && attrib != normalAttrib && attrib != extensionAttrib)
+ return false;
+
+ // Get the line text.
+ const QString lineContents = indentLine->string();
+ const int indentLast = indentLine->lastChar();
+ bool whitespaceFound = false;
+ for ( int n = indentFirst; n <= indentLast; ++n )
+ {
+ // Get the character as latin1. Can't use QChar::isLetterOrNumber()
+ // as that includes non 0-9 numbers.
+ char c = lineContents[n].latin1();
+ if ( c == ':' )
+ {
+ // See if the next character is ':' - if so, skip to the character after it.
+ if ( n < lineContents.length() - 1 )
+ {
+ if ( lineContents[n+1].latin1() == ':' )
+ {
+ n += 2;
+ continue;
+ }
+ }
+ // Right this is the relevent ':'.
+ if ( n == indentFirst)
+ {
+ // Just a line with a : on it.
+ return false;
+ }
+ // It is a label of some kind!
+ return true;
+ }
+ if (isspace(c))
+ {
+ if (!whitespaceFound)
+ {
+ if (lineContents.mid(indentFirst, n - indentFirst) == "case")
+ return true;
+ else if (lineContents.mid(indentFirst, n - indentFirst) == "class")
+ return false;
+ whitespaceFound = true;
+ }
+ }
+ // All other characters don't indent.
+ else if ( !isalnum(c) && c != '_' )
+ {
+ return false;
+ }
+ }
+ return false;
+}
+
+template<class T> T min(T a, T b) { return (a < b) ? a : b; }
+
+int KateCSAndSIndent::lastNonCommentChar( const KateDocCursor &line )
+{
+ KateTextLine::Ptr textLine = doc->plainKateTextLine( line.line() );
+ QString str = textLine->string();
+
+ // find a possible start-of-comment
+ int p = -2; // so the first find starts at position 0
+ do p = str.find( "//", p + 2 );
+ while ( p >= 0 && textLine->attribute(p) != commentAttrib && textLine->attribute(p) != doxyCommentAttrib );
+
+ // no // found? use whole string
+ if ( p < 0 )
+ p = str.length();
+
+ // ignore trailing blanks. p starts one-past-the-end.
+ while( p > 0 && str[p-1].isSpace() ) --p;
+ return p - 1;
+}
+
+bool KateCSAndSIndent::inForStatement( int line )
+{
+ // does this line end in a for ( ...
+ // with no closing ) ?
+ int parens = 0, semicolons = 0;
+ for ( ; line >= 0; --line )
+ {
+ KateTextLine::Ptr textLine = doc->plainKateTextLine(line);
+ const int first = textLine->firstChar();
+ const int last = textLine->lastChar();
+
+ // look backwards for a symbol: (){};
+ // match ()s, {...; and }...; => not in a for
+ // ; ; ; => not in a for
+ // ( ; and ( ; ; => a for
+ for ( int curr = last; curr >= first; --curr )
+ {
+ if ( textLine->attribute(curr) != symbolAttrib )
+ continue;
+
+ switch( textLine->getChar(curr) )
+ {
+ case ';':
+ if( ++semicolons > 2 )
+ return false;
+ break;
+ case '{': case '}':
+ return false;
+ case ')':
+ ++parens;
+ break;
+ case '(':
+ if( --parens < 0 )
+ return true;
+ break;
+ }
+ }
+ }
+ // no useful symbols before the ;?
+ // not in a for then
+ return false;
+}
+
+
+// is the start of the line containing 'begin' in a statement?
+bool KateCSAndSIndent::inStatement( const KateDocCursor &begin )
+{
+ // if the current line starts with an open brace, it's not a continuation.
+ // this happens after a function definition (which is treated as a continuation).
+ KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line());
+ const int first = textLine->firstChar();
+ // note that if we're being called from processChar the attribute has not yet been calculated
+ // should be reasonably safe to assume that unattributed {s are symbols; if the { is in a comment
+ // we don't want to touch it anyway.
+ const int attrib = textLine->attribute(first);
+ if( first >= 0 && (attrib == 0 || attrib == symbolAttrib) && textLine->getChar(first) == '{' )
+ return false;
+
+ int line;
+ for ( line = begin.line() - 1; line >= 0; --line )
+ {
+ textLine = doc->plainKateTextLine(line);
+ const int first = textLine->firstChar();
+ if ( first == -1 )
+ continue;
+
+ // starts with #: in a comment, don't care
+ // outside a comment: preprocessor, don't care
+ if ( textLine->getChar( first ) == '#' )
+ continue;
+ KateDocCursor currLine = begin;
+ currLine.setLine( line );
+ const int last = lastNonCommentChar( currLine );
+ if ( last < first )
+ continue;
+
+ // HACK: if we see a comment, assume boldly that this isn't a continuation.
+ // detecting comments (using attributes) is HARD, since they may have
+ // embedded alerts, or doxygen stuff, or just about anything. this is
+ // wrong, and needs fixing. note that only multi-line comments and
+ // single-line comments continued with \ are affected.
+ const int attrib = textLine->attribute(last);
+ if ( attrib == commentAttrib || attrib == doxyCommentAttrib )
+ return false;
+
+ char c = textLine->getChar(last);
+
+ // brace => not a continuation.
+ if ( attrib == symbolAttrib && c == '{' || c == '}' )
+ return false;
+
+ // ; => not a continuation, unless in a for (;;)
+ if ( attrib == symbolAttrib && c == ';' )
+ return inForStatement( line );
+
+ // found something interesting. maybe it's a label?
+ if ( attrib == symbolAttrib && c == ':' )
+ {
+ // the : above isn't necessarily the : in the label, eg in
+ // case 'x': a = b ? c :
+ // this will say no continuation incorrectly. but continued statements
+ // starting on a line with a label at the start is Bad Style (tm).
+ if( startsWithLabel( line ) )
+ {
+ // either starts with a label or a continuation. if the current line
+ // starts in a continuation, we're still in one. if not, this was
+ // a label, so we're not in one now. so continue to the next line
+ // upwards.
+ continue;
+ }
+ }
+
+ // any other character => in a continuation
+ return true;
+ }
+ // no non-comment text found before here - not a continuation.
+ return false;
+}
+
+QString KateCSAndSIndent::continuationIndent( const KateDocCursor &begin )
+{
+ if( !inStatement( begin ) )
+ return QString::null;
+ return indentString;
+}
+
+/**
+ * Figure out how indented the line containing @p begin should be.
+ */
+QString KateCSAndSIndent::calcIndent (const KateDocCursor &begin)
+{
+ KateTextLine::Ptr currLine = doc->plainKateTextLine(begin.line());
+ int currLineFirst = currLine->firstChar();
+
+ // if the line starts inside a comment, no change of indentation.
+ // FIXME: this unnecessarily copies the current indentation over itself.
+ // FIXME: on newline, this should copy from the previous line.
+ if ( currLineFirst >= 0 &&
+ (currLine->attribute(currLineFirst) == commentAttrib ||
+ currLine->attribute(currLineFirst) == doxyCommentAttrib) )
+ return currLine->string( 0, currLineFirst );
+
+ // if the line starts with # (but isn't a c# region thingy), no indentation at all.
+ if( currLineFirst >= 0 && currLine->getChar(currLineFirst) == '#' )
+ {
+ if( !currLine->stringAtPos( currLineFirst+1, QString::fromLatin1("region") ) &&
+ !currLine->stringAtPos( currLineFirst+1, QString::fromLatin1("endregion") ) )
+ return QString::null;
+ }
+
+ /* Strategy:
+ * Look for an open bracket or brace, or a keyword opening a new scope, whichever comes latest.
+ * Found a brace: indent one tab in.
+ * Found a bracket: indent to the first non-white after it.
+ * Found a keyword: indent one tab in. for try, catch and switch, if newline is set, also add
+ * an open brace, a newline, and indent two tabs in.
+ */
+ KateDocCursor cur = begin;
+ int pos, openBraceCount = 0, openParenCount = 0;
+ bool lookingForScopeKeywords = true;
+ const char * const scopeKeywords[] = { "for", "do", "while", "if", "else" };
+ const char * const blockScopeKeywords[] = { "try", "catch", "switch" };
+
+ while (cur.gotoPreviousLine())
+ {
+ KateTextLine::Ptr textLine = doc->plainKateTextLine(cur.line());
+ const int lastChar = textLine->lastChar();
+ const int firstChar = textLine->firstChar();
+
+ // look through line backwards for interesting characters
+ for( pos = lastChar; pos >= firstChar; --pos )
+ {
+ if (textLine->attribute(pos) == symbolAttrib)
+ {
+ char tc = textLine->getChar (pos);
+ switch( tc )
+ {
+ case '(': case '[':
+ if( ++openParenCount > 0 )
+ return calcIndentInBracket( begin, cur, pos );
+ break;
+ case ')': case ']': openParenCount--; break;
+ case '{':
+ if( ++openBraceCount > 0 )
+ return calcIndentInBrace( begin, cur, pos );
+ break;
+ case '}': openBraceCount--; lookingForScopeKeywords = false; break;
+ case ';':
+ if( openParenCount == 0 )
+ lookingForScopeKeywords = false;
+ break;
+ }
+ }
+
+ // if we've not had a close brace or a semicolon yet, and we're at the same parenthesis level
+ // as the cursor, and we're at the start of a scope keyword, indent from it.
+ if ( lookingForScopeKeywords && openParenCount == 0 &&
+ textLine->attribute(pos) == keywordAttrib &&
+ (pos == 0 || textLine->attribute(pos-1) != keywordAttrib ) )
+ {
+ #define ARRLEN( array ) ( sizeof(array)/sizeof(array[0]) )
+ for( uint n = 0; n < ARRLEN(scopeKeywords); ++n )
+ if( textLine->stringAtPos(pos, QString::fromLatin1(scopeKeywords[n]) ) )
+ return calcIndentAfterKeyword( begin, cur, pos, false );
+ for( uint n = 0; n < ARRLEN(blockScopeKeywords); ++n )
+ if( textLine->stringAtPos(pos, QString::fromLatin1(blockScopeKeywords[n]) ) )
+ return calcIndentAfterKeyword( begin, cur, pos, true );
+ #undef ARRLEN
+ }
+ }
+ }
+
+ // no active { in file.
+ return QString::null;
+}
+
+QString KateCSAndSIndent::calcIndentInBracket(const KateDocCursor &indentCursor, const KateDocCursor &bracketCursor, int bracketPos)
+{
+ KateTextLine::Ptr indentLine = doc->plainKateTextLine(indentCursor.line());
+ KateTextLine::Ptr bracketLine = doc->plainKateTextLine(bracketCursor.line());
+
+ // FIXME: hard-coded max indent to bracket width - use a kate variable
+ // FIXME: expand tabs first...
+ if ( bracketPos > 48 )
+ {
+ // how far to indent? we could look back for a brace or keyword, 2 from that.
+ // as it is, we just indent one more than the line with the ( on it.
+ // the potential problem with this is when
+ // you have code ( which does <-- continuation + start of func call
+ // something like this ); <-- extra indentation for func call
+ // then again (
+ // it works better than (
+ // the other method for (
+ // cases like this )));
+ // consequently, i think this method wins.
+ return indentString + initialWhitespace( bracketLine, bracketLine->firstChar() );
+ }
+
+ const int indentLineFirst = indentLine->firstChar();
+
+ int indentTo;
+ const int attrib = indentLine->attribute(indentLineFirst);
+ if( indentLineFirst >= 0 && (attrib == 0 || attrib == symbolAttrib) &&
+ ( indentLine->getChar(indentLineFirst) == ')' || indentLine->getChar(indentLineFirst) == ']' ) )
+ {
+ // If the line starts with a close bracket, line it up
+ indentTo = bracketPos;
+ }
+ else
+ {
+ // Otherwise, line up with the text after the open bracket
+ indentTo = bracketLine->nextNonSpaceChar( bracketPos + 1 );
+ if( indentTo == -1 )
+ indentTo = bracketPos + 2;
+ }
+ return initialWhitespace( bracketLine, indentTo );
+}
+
+QString KateCSAndSIndent::calcIndentAfterKeyword(const KateDocCursor &indentCursor, const KateDocCursor &keywordCursor, int keywordPos, bool blockKeyword)
+{
+ KateTextLine::Ptr keywordLine = doc->plainKateTextLine(keywordCursor.line());
+ KateTextLine::Ptr indentLine = doc->plainKateTextLine(indentCursor.line());
+
+ QString whitespaceToKeyword = initialWhitespace( keywordLine, keywordPos, false );
+ if( blockKeyword ) {
+ // FIXME: we could add the open brace and subsequent newline here since they're definitely needed.
+ }
+
+ // If the line starts with an open brace, don't indent...
+ int first = indentLine->firstChar();
+ // if we're being called from processChar attribute won't be set
+ const int attrib = indentLine->attribute(first);
+ if( first >= 0 && (attrib == 0 || attrib == symbolAttrib) && indentLine->getChar(first) == '{' )
+ return whitespaceToKeyword;
+
+ // don't check for a continuation. rules are simple here:
+ // if we're in a non-compound statement after a scope keyword, we indent all lines
+ // once. so:
+ // if ( some stuff
+ // goes here )
+ // apples, and <-- continuation here is ignored. but this is Bad Style (tm) anyway.
+ // oranges too;
+ return indentString + whitespaceToKeyword;
+}
+
+QString KateCSAndSIndent::calcIndentInBrace(const KateDocCursor &indentCursor, const KateDocCursor &braceCursor, int bracePos)
+{
+ KateTextLine::Ptr braceLine = doc->plainKateTextLine(braceCursor.line());
+ const int braceFirst = braceLine->firstChar();
+
+ QString whitespaceToOpenBrace = initialWhitespace( braceLine, bracePos, false );
+
+ // if the open brace is the start of a namespace, don't indent...
+ // FIXME: this is an extremely poor heuristic. it looks on the line with
+ // the { and the line before to see if they start with a keyword
+ // beginning 'namespace'. that's 99% of usage, I'd guess.
+ {
+ if( braceFirst >= 0 && braceLine->attribute(braceFirst) == keywordAttrib &&
+ braceLine->stringAtPos( braceFirst, QString::fromLatin1( "namespace" ) ) )
+ return continuationIndent(indentCursor) + whitespaceToOpenBrace;
+
+ if( braceCursor.line() > 0 )
+ {
+ KateTextLine::Ptr prevLine = doc->plainKateTextLine(braceCursor.line() - 1);
+ int firstPrev = prevLine->firstChar();
+ if( firstPrev >= 0 && prevLine->attribute(firstPrev) == keywordAttrib &&
+ prevLine->stringAtPos( firstPrev, QString::fromLatin1( "namespace" ) ) )
+ return continuationIndent(indentCursor) + whitespaceToOpenBrace;
+ }
+ }
+
+ KateTextLine::Ptr indentLine = doc->plainKateTextLine(indentCursor.line());
+ const int indentFirst = indentLine->firstChar();
+
+ // if the line starts with a close brace, don't indent...
+ if( indentFirst >= 0 && indentLine->getChar(indentFirst) == '}' )
+ return whitespaceToOpenBrace;
+
+ // if : is the first character (and not followed by another :), this is the start
+ // of an initialization list, or a continuation of a ?:. either way, indent twice.
+ if ( indentFirst >= 0 && indentLine->attribute(indentFirst) == symbolAttrib &&
+ indentLine->getChar(indentFirst) == ':' && indentLine->getChar(indentFirst+1) != ':' )
+ {
+ return indentString + indentString + whitespaceToOpenBrace;
+ }
+
+ const bool continuation = inStatement(indentCursor);
+ // if the current line starts with a label, don't indent...
+ if( !continuation && startsWithLabel( indentCursor.line() ) )
+ return whitespaceToOpenBrace;
+
+ // the normal case: indent once for the brace, again if it's a continuation
+ QString continuationIndent = continuation ? indentString : QString::null;
+ return indentString + continuationIndent + whitespaceToOpenBrace;
+}
+
+void KateCSAndSIndent::processChar(QChar c)
+{
+ // 'n' trigger is for c# regions.
+ static const QString triggers("}{)]/:;#n");
+ if (triggers.find(c) == -1)
+ return;
+
+ // for historic reasons, processChar doesn't get a cursor
+ // to work on. so fabricate one.
+ KateView *view = doc->activeView();
+ KateDocCursor begin(view->cursorLine(), 0, doc);
+
+ KateTextLine::Ptr textLine = doc->plainKateTextLine(begin.line());
+ if ( c == 'n' )
+ {
+ int first = textLine->firstChar();
+ if( first < 0 || textLine->getChar(first) != '#' )
+ return;
+ }
+
+ if ( textLine->attribute( begin.col() ) == doxyCommentAttrib )
+ {
+ // dominik: if line is "* /", change it to "*/"
+ if ( c == '/' )
+ {
+ int first = textLine->firstChar();
+ // if the first char exists and is a '*', and the next non-space-char
+ // is already the just typed '/', concatenate it to "*/".
+ if ( first != -1
+ && textLine->getChar( first ) == '*'
+ && textLine->nextNonSpaceChar( first+1 ) == view->cursorColumnReal()-1 )
+ doc->removeText( view->cursorLine(), first+1, view->cursorLine(), view->cursorColumnReal()-1);
+ }
+
+ // anders: don't change the indent of doxygen lines here.
+ return;
+ }
+
+ processLine(begin);
+}
+
+//END
+
+//BEGIN KateVarIndent
+class KateVarIndentPrivate {
+ public:
+ QRegExp reIndentAfter, reIndent, reUnindent;
+ QString triggers;
+ uint couples;
+ uchar coupleAttrib;
+};
+
+KateVarIndent::KateVarIndent( KateDocument *doc )
+: KateNormalIndent( doc )
+{
+ d = new KateVarIndentPrivate;
+ d->reIndentAfter = QRegExp( doc->variable( "var-indent-indent-after" ) );
+ d->reIndent = QRegExp( doc->variable( "var-indent-indent" ) );
+ d->reUnindent = QRegExp( doc->variable( "var-indent-unindent" ) );
+ d->triggers = doc->variable( "var-indent-triggerchars" );
+ d->coupleAttrib = 0;
+
+ slotVariableChanged( "var-indent-couple-attribute", doc->variable( "var-indent-couple-attribute" ) );
+ slotVariableChanged( "var-indent-handle-couples", doc->variable( "var-indent-handle-couples" ) );
+
+ // update if a setting is changed
+ connect( doc, SIGNAL(variableChanged( const QString&, const QString&) ),
+ this, SLOT(slotVariableChanged( const QString&, const QString& )) );
+}
+
+KateVarIndent::~KateVarIndent()
+{
+ delete d;
+}
+
+void KateVarIndent::processNewline ( KateDocCursor &begin, bool /*needContinue*/ )
+{
+ // process the line left, as well as the one entered
+ KateDocCursor left( begin.line()-1, 0, doc );
+ processLine( left );
+ processLine( begin );
+}
+
+void KateVarIndent::processChar ( QChar c )
+{
+ // process line if the c is in our list, and we are not in comment text
+ if ( d->triggers.contains( c ) )
+ {
+ KateTextLine::Ptr ln = doc->plainKateTextLine( doc->activeView()->cursorLine() );
+ if ( ln->attribute( doc->activeView()->cursorColumn()-1 ) == commentAttrib )
+ return;
+
+ KateView *view = doc->activeView();
+ KateDocCursor begin( view->cursorLine(), 0, doc );
+ kdDebug(13030)<<"variable indenter: process char '"<<c<<", line "<<begin.line()<<endl;
+ processLine( begin );
+ }
+}
+
+void KateVarIndent::processLine ( KateDocCursor &line )
+{
+ QString indent; // store the indent string here
+
+ // find the first line with content that is not starting with comment text,
+ // and take the position from that
+ int ln = line.line();
+ int pos = -1;
+ KateTextLine::Ptr ktl = doc->plainKateTextLine( ln );
+ if ( ! ktl ) return; // no line!?
+
+ // skip blank lines, except for the cursor line
+ KateView *v = doc->activeView();
+ if ( (ktl->firstChar() < 0) && (!v || (int)v->cursorLine() != ln ) )
+ return;
+
+ int fc;
+ if ( ln > 0 )
+ do
+ {
+
+ ktl = doc->plainKateTextLine( --ln );
+ fc = ktl->firstChar();
+ if ( ktl->attribute( fc ) != commentAttrib )
+ pos = fc;
+ }
+ while ( (ln > 0) && (pos < 0) ); // search a not empty text line
+
+ if ( pos < 0 )
+ pos = 0;
+ else
+ pos = ktl->cursorX( pos, tabWidth );
+
+ int adjustment = 0;
+
+ // try 'couples' for an opening on the above line first. since we only adjust by 1 unit,
+ // we only need 1 match.
+ if ( d->couples & Parens && coupleBalance( ln, '(', ')' ) > 0 )
+ adjustment++;
+ else if ( d->couples & Braces && coupleBalance( ln, '{', '}' ) > 0 )
+ adjustment++;
+ else if ( d->couples & Brackets && coupleBalance( ln, '[', ']' ) > 0 )
+ adjustment++;
+
+ // Try 'couples' for a closing on this line first. since we only adjust by 1 unit,
+ // we only need 1 match. For unindenting, we look for a closing character
+ // *at the beginning of the line*
+ // NOTE Assume that a closing brace with the configured attribute on the start
+ // of the line is closing.
+ // When acting on processChar, the character isn't highlighted. So I could
+ // either not check, assuming that the first char *is* meant to close, or do a
+ // match test if the attrib is 0. How ever, doing that is
+ // a potentially huge job, if the match is several hundred lines away.
+ // Currently, the check is done.
+ {
+ KateTextLine::Ptr tl = doc->plainKateTextLine( line.line() );
+ int i = tl->firstChar();
+ if ( i > -1 )
+ {
+ QChar ch = tl->getChar( i );
+ uchar at = tl->attribute( i );
+ kdDebug(13030)<<"attrib is "<<at<<endl;
+ if ( d->couples & Parens && ch == ')'
+ && ( at == d->coupleAttrib
+ || (! at && hasRelevantOpening( KateDocCursor( line.line(), i, doc ) ))
+ )
+ )
+ adjustment--;
+ else if ( d->couples & Braces && ch == '}'
+ && ( at == d->coupleAttrib
+ || (! at && hasRelevantOpening( KateDocCursor( line.line(), i, doc ) ))
+ )
+ )
+ adjustment--;
+ else if ( d->couples & Brackets && ch == ']'
+ && ( at == d->coupleAttrib
+ || (! at && hasRelevantOpening( KateDocCursor( line.line(), i, doc ) ))
+ )
+ )
+ adjustment--;
+ }
+ }
+#define ISCOMMENTATTR(attr) (attr==commentAttrib||attr==doxyCommentAttrib)
+#define ISCOMMENT (ISCOMMENTATTR(ktl->attribute(ktl->firstChar()))||ISCOMMENTATTR(ktl->attribute(matchpos)))
+ // check if we should indent, unless the line starts with comment text,
+ // or the match is in comment text
+ kdDebug(13030)<<"variable indenter: starting indent: "<<pos<<endl;
+ // check if the above line indicates that we shuld add indentation
+ int matchpos = 0;
+ if ( ktl && ! d->reIndentAfter.isEmpty()
+ && (matchpos = d->reIndentAfter.search( doc->textLine( ln ) )) > -1
+ && ! ISCOMMENT )
+ adjustment++;
+
+ // else, check if this line should indent unless ...
+ ktl = doc->plainKateTextLine( line.line() );
+ if ( ! d->reIndent.isEmpty()
+ && (matchpos = d->reIndent.search( doc->textLine( line.line() ) )) > -1
+ && ! ISCOMMENT )
+ adjustment++;
+
+ // else, check if the current line indicates if we should remove indentation unless ...
+ if ( ! d->reUnindent.isEmpty()
+ && (matchpos = d->reUnindent.search( doc->textLine( line.line() ) )) > -1
+ && ! ISCOMMENT )
+ adjustment--;
+
+ kdDebug(13030)<<"variable indenter: adjusting by "<<adjustment<<" units"<<endl;
+
+ if ( adjustment > 0 )
+ pos += indentWidth;
+ else if ( adjustment < 0 )
+ pos -= indentWidth;
+
+ ln = line.line();
+ fc = doc->plainKateTextLine( ln )->firstChar();
+
+ // dont change if there is no change.
+ // ### should I actually compare the strings?
+ // FIXME for some odd reason, the document gets marked as changed
+ // even if we don't change it !?
+ if ( fc == pos )
+ return;
+
+ if ( fc > 0 )
+ doc->removeText (ln, 0, ln, fc );
+
+ if ( pos > 0 )
+ indent = tabString( pos );
+
+ if ( pos > 0 )
+ doc->insertText (ln, 0, indent);
+
+ // try to restore cursor ?
+ line.setCol( pos );
+}
+
+void KateVarIndent::processSection (const KateDocCursor &begin, const KateDocCursor &end)
+{
+ KateDocCursor cur = begin;
+ while (cur.line() <= end.line())
+ {
+ processLine (cur);
+ if (!cur.gotoNextLine())
+ break;
+ }
+}
+
+void KateVarIndent::slotVariableChanged( const QString &var, const QString &val )
+{
+ if ( ! var.startsWith("var-indent") )
+ return;
+
+ if ( var == "var-indent-indent-after" )
+ d->reIndentAfter.setPattern( val );
+ else if ( var == "var-indent-indent" )
+ d->reIndent.setPattern( val );
+ else if ( var == "var-indent-unindent" )
+ d->reUnindent.setPattern( val );
+ else if ( var == "var-indent-triggerchars" )
+ d->triggers = val;
+ else if ( var == "var-indent-handle-couples" )
+ {
+ d->couples = 0;
+ QStringList l = QStringList::split( " ", val );
+ if ( l.contains("parens") ) d->couples |= Parens;
+ if ( l.contains("braces") ) d->couples |= Braces;
+ if ( l.contains("brackets") ) d->couples |= Brackets;
+ }
+ else if ( var == "var-indent-couple-attribute" )
+ {
+ //read a named attribute of the config.
+ KateHlItemDataList items;
+ doc->highlight()->getKateHlItemDataListCopy (0, items);
+
+ for (uint i=0; i<items.count(); i++)
+ {
+ if ( items.at(i)->name.section( ':', 1 ) == val )
+ {
+ d->coupleAttrib = i;
+ break;
+ }
+ }
+ }
+}
+
+int KateVarIndent::coupleBalance ( int line, const QChar &open, const QChar &close ) const
+{
+ int r = 0;
+
+ KateTextLine::Ptr ln = doc->plainKateTextLine( line );
+ if ( ! ln || ! ln->length() ) return 0;
+
+ for ( uint z=0; z < ln->length(); z++ )
+ {
+ QChar c = ln->getChar( z );
+ if ( ln->attribute(z) == d->coupleAttrib )
+ {
+ kdDebug(13030)<<z<<", "<<c<<endl;
+ if (c == open)
+ r++;
+ else if (c == close)
+ r--;
+ }
+ }
+ return r;
+}
+
+bool KateVarIndent::hasRelevantOpening( const KateDocCursor &end ) const
+{
+ KateDocCursor cur = end;
+ int count = 1;
+
+ QChar close = cur.currentChar();
+ QChar opener;
+ if ( close == '}' ) opener = '{';
+ else if ( close = ')' ) opener = '(';
+ else if (close = ']' ) opener = '[';
+ else return false;
+
+ //Move backwards 1 by 1 and find the opening partner
+ while (cur.moveBackward(1))
+ {
+ if (cur.currentAttrib() == d->coupleAttrib)
+ {
+ QChar ch = cur.currentChar();
+ if (ch == opener)
+ count--;
+ else if (ch == close)
+ count++;
+
+ if (count == 0)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+//END KateVarIndent
+
+//BEGIN KateScriptIndent
+KateScriptIndent::KateScriptIndent( KateDocument *doc )
+ : KateNormalIndent( doc )
+{
+ m_script=KateFactory::self()->indentScript ("script-indent-c1-test");
+}
+
+KateScriptIndent::~KateScriptIndent()
+{
+}
+
+void KateScriptIndent::processNewline( KateDocCursor &begin, bool needContinue )
+{
+ kdDebug(13030) << "processNewline" << endl;
+ KateView *view = doc->activeView();
+
+ if (view)
+ {
+ QString errorMsg;
+
+ QTime t;
+ t.start();
+ kdDebug(13030)<<"calling m_script.processChar"<<endl;
+ if( !m_script.processNewline( view, begin, needContinue , errorMsg ) )
+ {
+ kdDebug(13030) << "Error in script-indent: " << errorMsg << endl;
+ }
+ kdDebug(13030) << "ScriptIndent::TIME in ms: " << t.elapsed() << endl;
+ }
+}
+
+void KateScriptIndent::processChar( QChar c )
+{
+ kdDebug(13030) << "processChar" << endl;
+ KateView *view = doc->activeView();
+
+ if (view)
+ {
+ QString errorMsg;
+
+ QTime t;
+ t.start();
+ kdDebug(13030)<<"calling m_script.processChar"<<endl;
+ if( !m_script.processChar( view, c , errorMsg ) )
+ {
+ kdDebug(13030) << "Error in script-indent: " << errorMsg << endl;
+ }
+ kdDebug(13030) << "ScriptIndent::TIME in ms: " << t.elapsed() << endl;
+ }
+}
+
+void KateScriptIndent::processLine (KateDocCursor &line)
+{
+ kdDebug(13030) << "processLine" << endl;
+ KateView *view = doc->activeView();
+
+ if (view)
+ {
+ QString errorMsg;
+
+ QTime t;
+ t.start();
+ kdDebug(13030)<<"calling m_script.processLine"<<endl;
+ if( !m_script.processLine( view, line , errorMsg ) )
+ {
+ kdDebug(13030) << "Error in script-indent: " << errorMsg << endl;
+ }
+ kdDebug(13030) << "ScriptIndent::TIME in ms: " << t.elapsed() << endl;
+ }
+}
+//END KateScriptIndent
+
+//BEGIN ScriptIndentConfigPage, THIS IS ONLY A TEST! :)
+#include <qlabel.h>
+ScriptIndentConfigPage::ScriptIndentConfigPage ( QWidget *parent, const char *name )
+ : IndenterConfigPage(parent, name)
+{
+ QLabel* hello = new QLabel("Hello world! Dummy for testing purpose.", this);
+ hello->show();
+}
+
+ScriptIndentConfigPage::~ScriptIndentConfigPage ()
+{
+}
+
+void ScriptIndentConfigPage::apply ()
+{
+ kdDebug(13030) << "ScriptIndentConfigPagE::apply() was called, save config options now!" << endl;
+}
+//END ScriptIndentConfigPage
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/kateautoindent.h b/kate/part/kateautoindent.h
new file mode 100644
index 000000000..76ba14ee6
--- /dev/null
+++ b/kate/part/kateautoindent.h
@@ -0,0 +1,581 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2003 Jesse Yurkovich <yurkjes@iit.edu>
+ Copyright (C) 2004 >Anders Lund <anders@alweb.dk> (KateVarIndent class)
+ Copyright (C) 2005 Dominik Haumann <dhdev@gmx.de> (basic support for config page)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KATE_AUTO_INDENT_H__
+#define __KATE_AUTO_INDENT_H__
+
+#include <qobject.h>
+
+#include "katecursor.h"
+#include "kateconfig.h"
+#include "katejscript.h"
+class KateDocument;
+
+/**
+ * This widget will be embedded into a modal dialog when clicking
+ * the "Configure..." button in the indentation config page.
+ * To add a config page for an indenter there are several todos:
+ * - Derive a class from this class. This widget will be embedded into
+ * the config dialog.
+ * - Override the slot @p apply(), which is called when the configuration
+ * needs to be saved.
+ * - Override @p KateAutoIndent::configPage() to return an instance of
+ * this dialog.
+ * - Return @p true in @p KateAutoIndent::hasConfigPage() for the
+ * corresponding indenter id.
+ */
+class IndenterConfigPage : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * Standard constructor
+ * @param parent parent widget
+ * @param name name
+ */
+ IndenterConfigPage ( QWidget *parent=0, const char *name=0 ) : QWidget(parent, name) {}
+ virtual ~IndenterConfigPage () {}
+
+ public slots:
+ /**
+ * Apply the changes. Save options here, use @p kapp->config() and
+ * group [Kate Indenter MyIndenter].
+ */
+ virtual void apply () = 0;
+};
+
+/**
+ * Provides Auto-Indent functionality for katepart.
+ * This baseclass is a real dummy, does nothing beside remembering the document it belongs too,
+ * only to have the object around
+ */
+class KateAutoIndent : public QObject
+{
+ Q_OBJECT
+
+ /**
+ * Static methods to create and list indention modes
+ */
+ public:
+ /**
+ * Create an indenter
+ * @param doc document for the indenter
+ * @param mode indention mode wanted
+ * @return created autoindention object
+ */
+ static KateAutoIndent *createIndenter (KateDocument *doc, uint mode);
+
+ /**
+ * List all possible modes by name
+ * @return list of modes
+ */
+ static QStringList listModes ();
+
+ /**
+ * Return the mode name given the mode
+ * @param mode mode index
+ * @return name for this mode index
+ */
+ static QString modeName (uint mode);
+
+ /**
+ * Return the mode description
+ * @param mode mode index
+ * @return mode index
+ */
+ static QString modeDescription (uint mode);
+
+ /**
+ * Maps name -> index
+ * @param name mode name
+ * @return mode index
+ */
+ static uint modeNumber (const QString &name);
+
+ /**
+ * Config page support
+ * @param mode mode index
+ * @return true, if the indenter @p mode has a configuration page
+ */
+ static bool hasConfigPage (uint mode);
+
+ /**
+ * Support for a config page.
+ * @return config page or 0 if not available.
+ */
+ static IndenterConfigPage* configPage(QWidget *parent, uint mode);
+
+ public:
+ /**
+ * Constructor
+ * @param doc parent document
+ */
+ KateAutoIndent (KateDocument *doc);
+
+ /**
+ * Virtual Destructor for the baseclass
+ */
+ virtual ~KateAutoIndent ();
+
+ public slots:
+ /**
+ * Update indenter's configuration (indention width, attributes etc.)
+ */
+ virtual void updateConfig () {};
+
+ public:
+ /**
+ * does this indenter support processNewLine
+ * @return can you do it?
+ */
+ virtual bool canProcessNewLine () const { return false; }
+
+ /**
+ * Called every time a newline character is inserted in the document.
+ *
+ * @param cur The position to start processing. Contains the new cursor position after the indention.
+ * @param needContinue Used to determine whether to calculate a continue indent or not.
+ */
+ virtual void processNewline (KateDocCursor &cur, bool needContinue) { Q_UNUSED(cur); Q_UNUSED(needContinue); }
+
+ /**
+ * Called every time a character is inserted into the document.
+ * @param c character inserted
+ */
+ virtual void processChar (QChar c) { Q_UNUSED(c); }
+
+ /**
+ * Aligns/indents the given line to the proper indent position.
+ */
+ virtual void processLine (KateDocCursor &/*line*/) { }
+
+ /**
+ * Processes a section of text, indenting each line in between.
+ */
+ virtual void processSection (const KateDocCursor &/*begin*/, const KateDocCursor &/*end*/) { }
+
+ /**
+ * Set to true if an actual implementation of 'processLine' is present.
+ * This is used to prevent a needless Undo action from being created.
+ */
+ virtual bool canProcessLine() const { return false; }
+
+ /**
+ * Mode index of this mode
+ * @return modeNumber
+ */
+ virtual uint modeNumber () const { return KateDocumentConfig::imNone; };
+
+ protected:
+ KateDocument *doc;
+};
+
+/**
+ * This action provides a list of available indenters and gets plugged
+ * into the KateView's KActionCollection.
+ */
+class KateViewIndentationAction : public KActionMenu
+{
+ Q_OBJECT
+
+ public:
+ KateViewIndentationAction(KateDocument *_doc, const QString& text, QObject* parent = 0, const char* name = 0);
+
+ ~KateViewIndentationAction(){;};
+
+ private:
+ KateDocument* doc;
+
+ public slots:
+ void slotAboutToShow();
+
+ private slots:
+ void setMode (int mode);
+};
+
+/**
+ * Provides Auto-Indent functionality for katepart.
+ */
+class KateNormalIndent : public KateAutoIndent
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Constructor
+ * @param doc parent document
+ */
+ KateNormalIndent (KateDocument *doc);
+
+ /**
+ * Virtual Destructor for the baseclass
+ */
+ virtual ~KateNormalIndent ();
+
+public slots:
+ /**
+ * Update indenter's configuration (indention width, attributes etc.)
+ */
+ virtual void updateConfig ();
+
+public:
+ /**
+ * does this indenter support processNewLine
+ * @return can you do it?
+ */
+ virtual bool canProcessNewLine () const { return true; }
+
+ /**
+ * Called every time a newline character is inserted in the document.
+ *
+ * @param cur The position to start processing. Contains the new cursor position after the indention.
+ * @param needContinue Used to determine whether to calculate a continue indent or not.
+ */
+ virtual void processNewline (KateDocCursor &cur, bool needContinue);
+
+ /**
+ * Called every time a character is inserted into the document.
+ * @param c character inserted
+ */
+ virtual void processChar (QChar c) { Q_UNUSED(c); }
+
+ /**
+ * Aligns/indents the given line to the proper indent position.
+ */
+ virtual void processLine (KateDocCursor &/*line*/) { }
+
+ /**
+ * Processes a section of text, indenting each line in between.
+ */
+ virtual void processSection (const KateDocCursor &/*begin*/, const KateDocCursor &/*end*/) { }
+
+ /**
+ * Set to true if an actual implementation of 'processLine' is present.
+ * This is used to prevent a needless Undo action from being created.
+ */
+ virtual bool canProcessLine() const { return false; }
+
+ /**
+ * Mode index of this mode
+ * @return modeNumber
+ */
+ virtual uint modeNumber () const { return KateDocumentConfig::imNormal; };
+
+protected:
+
+ /**
+ * Determines if the characters open and close are balanced between @p begin and @p end
+ * Fills in @p pos with the column position of first opened character if found.
+ *
+ * @param begin Beginning cursor position.
+ * @param end Ending cursor position where the processing will stop.
+ * @param open The open character.
+ * @param close The closing character which should be matched against @p open.
+ * @param pos Contains the position of the first @p open character in the line.
+ * @return True if @p open and @p close have an equal number of occurances between @p begin and @p end. False otherwise.
+ */
+ bool isBalanced (KateDocCursor &begin, const KateDocCursor &end, QChar open, QChar close, uint &pos) const;
+
+ /**
+ * Skip all whitespace starting at @p cur and ending at @p max. Spans lines if @p newline is set.
+ * @p cur is set to the current position afterwards.
+ *
+ * @param cur The current cursor position to start from.
+ * @param max The furthest cursor position that will be used for processing
+ * @param newline Whether we are allowed to span multiple lines when skipping blanks
+ * @return True if @p cur < @p max after processing. False otherwise.
+ */
+ bool skipBlanks (KateDocCursor &cur, KateDocCursor &max, bool newline) const;
+
+ /**
+ * Measures the indention of the current textline marked by cur
+ * @param cur The cursor position to measure the indent to.
+ * @return The length of the indention in characters.
+ */
+ uint measureIndent (KateDocCursor &cur) const;
+
+ /**
+ * Produces a string with the proper indentation characters for its length.
+ *
+ * @param length The length of the indention in characters.
+ * @return A QString representing @p length characters (factoring in tabs and spaces)
+ */
+ QString tabString(uint length) const;
+
+ uint tabWidth; //!< The number of characters simulated for a tab
+ uint indentWidth; //!< The number of characters used when tabs are replaced by spaces
+
+public:
+ // Attributes that we should skip over or otherwise know about
+ uchar commentAttrib;
+ uchar doxyCommentAttrib;
+ uchar regionAttrib;
+ uchar symbolAttrib;
+ uchar alertAttrib;
+ uchar tagAttrib;
+ uchar wordAttrib;
+ uchar keywordAttrib;
+ uchar normalAttrib;
+ uchar extensionAttrib;
+ uchar preprocessorAttrib;
+ uchar stringAttrib;
+ uchar charAttrib;
+
+protected:
+ bool useSpaces; //!< Should we use spaces or tabs to indent
+ bool mixedIndent; //!< Optimize indent by mixing spaces and tabs, ala emacs
+ bool keepProfile; //!< Always try to honor the leading whitespace of lines already in the file
+};
+
+class KateCSmartIndent : public KateNormalIndent
+{
+ Q_OBJECT
+
+ public:
+ KateCSmartIndent (KateDocument *doc);
+ ~KateCSmartIndent ();
+
+ virtual void processNewline (KateDocCursor &cur, bool needContinue);
+ virtual void processChar (QChar c);
+
+ virtual void processLine (KateDocCursor &line);
+ virtual void processSection (const KateDocCursor &begin, const KateDocCursor &end);
+
+ virtual bool canProcessLine() const { return true; }
+
+ virtual uint modeNumber () const { return KateDocumentConfig::imCStyle; };
+
+ private:
+ uint calcIndent (KateDocCursor &begin, bool needContinue);
+ uint calcContinue (KateDocCursor &begin, KateDocCursor &end);
+ uint findOpeningBrace (KateDocCursor &start);
+ uint findOpeningParen (KateDocCursor &start);
+ uint findOpeningComment (KateDocCursor &start);
+ bool firstOpeningBrace (KateDocCursor &start);
+ bool handleDoxygen (KateDocCursor &begin);
+
+ bool allowSemi;
+ bool processingBlock;
+};
+
+class KatePythonIndent : public KateNormalIndent
+{
+ Q_OBJECT
+
+ public:
+ KatePythonIndent (KateDocument *doc);
+ ~KatePythonIndent ();
+
+ virtual void processNewline (KateDocCursor &cur, bool needContinue);
+
+ virtual uint modeNumber () const { return KateDocumentConfig::imPythonStyle; };
+
+ private:
+ int calcExtra (int &prevBlock, int &pos, KateDocCursor &end);
+ void traverseString( const QChar &stringChar, KateDocCursor &cur, KateDocCursor &end );
+
+ static QRegExp endWithColon;
+ static QRegExp stopStmt;
+ static QRegExp blockBegin;
+};
+
+class KateXmlIndent : public KateNormalIndent
+{
+ Q_OBJECT
+
+ public:
+ KateXmlIndent (KateDocument *doc);
+ ~KateXmlIndent ();
+
+ virtual uint modeNumber () const { return KateDocumentConfig::imXmlStyle; }
+ virtual void processNewline (KateDocCursor &cur, bool needContinue);
+ virtual void processChar (QChar c);
+ virtual void processLine (KateDocCursor &line);
+ virtual bool canProcessLine() const { return true; }
+ virtual void processSection (const KateDocCursor &begin, const KateDocCursor &end);
+
+ private:
+ // sets the indentation of a single line based on previous line
+ // (returns indentation width)
+ uint processLine (uint line);
+
+ // gets information about a line
+ void getLineInfo (uint line, uint &prevIndent, int &numTags,
+ uint &attrCol, bool &unclosedTag);
+
+ // useful regular expressions
+ static const QRegExp startsWithCloseTag;
+ static const QRegExp unclosedDoctype;
+};
+
+class KateCSAndSIndent : public KateNormalIndent
+{
+ Q_OBJECT
+
+ public:
+ KateCSAndSIndent (KateDocument *doc);
+ ~KateCSAndSIndent ();
+
+ virtual void processNewline (KateDocCursor &begin, bool needContinue);
+ virtual void processChar (QChar c);
+
+ virtual void processLine (KateDocCursor &line);
+ virtual void processSection (const KateDocCursor &begin, const KateDocCursor &end);
+
+ virtual bool canProcessLine() const { return true; }
+
+ virtual uint modeNumber () const { return KateDocumentConfig::imCSAndS; };
+
+ private:
+ void updateIndentString();
+
+ bool inForStatement( int line );
+ int lastNonCommentChar( const KateDocCursor &line );
+ bool startsWithLabel( int line );
+ bool inStatement( const KateDocCursor &begin );
+ QString continuationIndent( const KateDocCursor &begin );
+
+ QString calcIndent (const KateDocCursor &begin);
+ QString calcIndentAfterKeyword(const KateDocCursor &indentCursor, const KateDocCursor &keywordCursor, int keywordPos, bool blockKeyword);
+ QString calcIndentInBracket(const KateDocCursor &indentCursor, const KateDocCursor &bracketCursor, int bracketPos);
+ QString calcIndentInBrace(const KateDocCursor &indentCursor, const KateDocCursor &braceCursor, int bracePos);
+
+ bool handleDoxygen (KateDocCursor &begin);
+ QString findOpeningCommentIndentation (const KateDocCursor &start);
+
+ QString indentString;
+};
+
+/**
+ * This indenter uses document variables to determine when to add/remove indents.
+ *
+ * It attempts to get the following variables from the document:
+ * - var-indent-indent-after: A rerular expression which will cause a line to
+ * be indented by one unit, if the first non-whitespace-only line above matches.
+ * - var-indent-indent: A regular expression, which will cause a matching line
+ * to be indented by one unit.
+ * - var-indent-unindent: A regular expression which will cause the line to be
+ * unindented by one unit if matching.
+ * - var-indent-triggerchars: a list of characters that should cause the
+ * indentiou to be recalculated immediately when typed.
+ * - var-indent-handle-couples: a list of paren sets to handle. Any combination
+ * of 'parens' 'braces' and 'brackets'. Each set type is handled
+ * the following way: If there are unmatched opening instances on the above line,
+ * one indent unit is added, if there are unmatched closing instances on the
+ * current line, one indent unit is removed.
+ * - var-indent-couple-attribute: When looking for unmatched couple openings/closings,
+ * only characters with this attribute is considered. The value must be the
+ * attribute name from the syntax xml file, for example "Symbol". If it's not
+ * specified, attribute 0 is used (usually 'Normal Text').
+ *
+ * The idea is to provide a somewhat intelligent indentation for perl, php,
+ * bash, scheme and in general formats with humble indentation needs.
+ */
+class KateVarIndent : public KateNormalIndent
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * Purely for readability, couples we know and love
+ */
+ enum pairs {
+ Parens=1,
+ Braces=2,
+ Brackets=4,
+ AngleBrackets=8
+ };
+
+ KateVarIndent( KateDocument *doc );
+ virtual ~KateVarIndent();
+
+ virtual void processNewline (KateDocCursor &cur, bool needContinue);
+ virtual void processChar (QChar c);
+
+ virtual void processLine (KateDocCursor &line);
+ virtual void processSection (const KateDocCursor &begin, const KateDocCursor &end);
+
+ virtual bool canProcessLine() const { return true; }
+
+ virtual uint modeNumber () const { return KateDocumentConfig::imVarIndent; };
+
+ private slots:
+ void slotVariableChanged(const QString&, const QString&);
+
+ private:
+ /**
+ * Check if coupled characters are in balance within one line.
+ * @param line the line to check
+ * @param open the opening character
+ * @param close the closing character
+ * @param attrib the attribute the characters must have, defaults to
+ * KateAutoIndent::symbolAttrib
+ */
+ int coupleBalance( int line, const QChar &open, const QChar &close ) const;
+
+ /**
+ * @return true if there is a matching opening with the correct attribute
+ * @param end a cursor pointing to the closing character
+ */
+ bool hasRelevantOpening( const KateDocCursor &end ) const;
+
+ class KateVarIndentPrivate *d;
+};
+
+class KateScriptIndent : public KateNormalIndent
+{
+ Q_OBJECT
+
+ public:
+ KateScriptIndent( KateDocument *doc );
+ ~KateScriptIndent();
+
+ virtual void processNewline( KateDocCursor &cur, bool needContinue );
+ virtual void processChar( QChar c );
+
+ virtual void processLine (KateDocCursor &line);
+// virtual void processSection (const KateDocCursor &begin, const KateDocCursor &end);
+
+ virtual bool canProcessLine() const { return true; }
+
+ virtual uint modeNumber () const { return KateDocumentConfig::imScriptIndent; };
+ private:
+ KateIndentScript m_script;
+};
+
+class ScriptIndentConfigPage : public IndenterConfigPage
+{
+ Q_OBJECT
+
+ public:
+ ScriptIndentConfigPage ( QWidget *parent=0, const char *name=0 );
+ virtual ~ScriptIndentConfigPage ();
+
+ public slots:
+ /**
+ * Apply changes.
+ */
+ virtual void apply ();
+};
+
+#endif
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katebookmarks.cpp b/kate/part/katebookmarks.cpp
new file mode 100644
index 000000000..f92fb4282
--- /dev/null
+++ b/kate/part/katebookmarks.cpp
@@ -0,0 +1,287 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2002, 2003, 2004 Anders Lund <anders.lund@lund.tdcadsl.dk>
+ Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "katebookmarks.h"
+#include "katebookmarks.moc"
+
+#include "katedocument.h"
+#include "kateview.h"
+
+#include <klocale.h>
+#include <kaction.h>
+#include <kpopupmenu.h>
+#include <kstringhandler.h>
+#include <kxmlguiclient.h>
+#include <kxmlguifactory.h>
+
+#include <qregexp.h>
+#include <qmemarray.h>
+#include <qevent.h>
+
+/**
+ Utility: selection sort
+ sort a QMemArray<uint> in ascending order.
+ max it the largest (zerobased) index to sort.
+ To sort the entire array: ssort( *array, array.size() -1 );
+ This is only efficient if ran only once.
+*/
+static void ssort( QMemArray<uint> &a, int max )
+{
+ uint tmp, j, maxpos;
+ for ( uint h = max; h >= 1; h-- )
+ {
+ maxpos = 0;
+ for ( j = 0; j <= h; j++ )
+ maxpos = a[j] > a[maxpos] ? j : maxpos;
+ tmp = a[maxpos];
+ a[maxpos] = a[h];
+ a[h] = tmp;
+ }
+}
+
+// TODO add a insort() or bubble_sort - more efficient for aboutToShow() ?
+
+KateBookmarks::KateBookmarks( KateView* view, Sorting sort )
+ : QObject( view, "kate bookmarks" )
+ , m_view( view )
+ , m_sorting( sort )
+{
+ connect (view->getDoc(), SIGNAL(marksChanged()), this, SLOT(marksChanged()));
+ _tries=0;
+ m_bookmarksMenu = 0L;
+}
+
+KateBookmarks::~KateBookmarks()
+{
+}
+
+void KateBookmarks::createActions( KActionCollection* ac )
+{
+ m_bookmarkToggle = new KToggleAction(
+ i18n("Set &Bookmark"), "bookmark", CTRL+Key_B,
+ this, SLOT(toggleBookmark()),
+ ac, "bookmarks_toggle" );
+ m_bookmarkToggle->setWhatsThis(i18n("If a line has no bookmark then add one, otherwise remove it."));
+ m_bookmarkToggle->setCheckedState( i18n("Clear &Bookmark") );
+
+ m_bookmarkClear = new KAction(
+ i18n("Clear &All Bookmarks"), 0,
+ this, SLOT(clearBookmarks()),
+ ac, "bookmarks_clear");
+ m_bookmarkClear->setWhatsThis(i18n("Remove all bookmarks of the current document."));
+
+ m_goNext = new KAction(
+ i18n("Next Bookmark"), "next", ALT + Key_PageDown,
+ this, SLOT(goNext()),
+ ac, "bookmarks_next");
+ m_goNext->setWhatsThis(i18n("Go to the next bookmark."));
+
+ m_goPrevious = new KAction(
+ i18n("Previous Bookmark"), "previous", ALT + Key_PageUp,
+ this, SLOT(goPrevious()),
+ ac, "bookmarks_previous");
+ m_goPrevious->setWhatsThis(i18n("Go to the previous bookmark."));
+
+ m_bookmarksMenu = (new KActionMenu(i18n("&Bookmarks"), ac, "bookmarks"))->popupMenu();
+
+ //connect the aboutToShow() and aboutToHide() signals with
+ //the bookmarkMenuAboutToShow() and bookmarkMenuAboutToHide() slots
+ connect( m_bookmarksMenu, SIGNAL(aboutToShow()), this, SLOT(bookmarkMenuAboutToShow()));
+ connect( m_bookmarksMenu, SIGNAL(aboutToHide()), this, SLOT(bookmarkMenuAboutToHide()) );
+
+ marksChanged ();
+ bookmarkMenuAboutToHide();
+
+ connect( m_view, SIGNAL( gotFocus( Kate::View * ) ), this, SLOT( slotViewGotFocus( Kate::View * ) ) );
+ connect( m_view, SIGNAL( lostFocus( Kate::View * ) ), this, SLOT( slotViewLostFocus( Kate::View * ) ) );
+}
+
+void KateBookmarks::toggleBookmark ()
+{
+ uint mark = m_view->getDoc()->mark( m_view->cursorLine() );
+ if( mark & KTextEditor::MarkInterface::markType01 )
+ m_view->getDoc()->removeMark( m_view->cursorLine(),
+ KTextEditor::MarkInterface::markType01 );
+ else
+ m_view->getDoc()->addMark( m_view->cursorLine(),
+ KTextEditor::MarkInterface::markType01 );
+}
+
+void KateBookmarks::clearBookmarks ()
+{
+
+ QPtrList<KTextEditor::Mark> m = m_view->getDoc()->marks();
+ for (uint i=0; i < m.count(); i++)
+ m_view->getDoc()->removeMark( m.at(i)->line, KTextEditor::MarkInterface::markType01 );
+
+ // just to be sure ;)
+ marksChanged ();
+}
+
+void KateBookmarks::slotViewGotFocus( Kate::View *v )
+{
+ if ( v == (Kate::View*)m_view )
+ bookmarkMenuAboutToHide();
+}
+
+void KateBookmarks::slotViewLostFocus( Kate::View *v )
+{
+ if ( v == (Kate::View*)m_view )
+ m_bookmarksMenu->clear();
+}
+
+void KateBookmarks::insertBookmarks( QPopupMenu& menu )
+{
+ uint line = m_view->cursorLine();
+ const QRegExp re("&(?!&)");
+ int idx( -1 );
+ int old_menu_count = menu.count();
+ KTextEditor::Mark *next = 0;
+ KTextEditor::Mark *prev = 0;
+
+ QPtrList<KTextEditor::Mark> m = m_view->getDoc()->marks();
+ QMemArray<uint> sortArray( m.count() );
+ QPtrListIterator<KTextEditor::Mark> it( m );
+
+ if ( it.count() > 0 )
+ menu.insertSeparator();
+
+ for( int i = 0; *it; ++it, ++i )
+ {
+ if( (*it)->type & KTextEditor::MarkInterface::markType01 )
+ {
+ QString bText = KStringHandler::rEmSqueeze
+ ( m_view->getDoc()->textLine( (*it)->line ),
+ menu.fontMetrics(), 32 );
+ bText.replace(re, "&&"); // kill undesired accellerators!
+ bText.replace('\t', ' '); // kill tabs, as they are interpreted as shortcuts
+
+ if ( m_sorting == Position )
+ {
+ sortArray[i] = (*it)->line;
+ ssort( sortArray, i );
+ idx = sortArray.find( (*it)->line ) + 3;
+ }
+
+ menu.insertItem(
+ QString("%1 - \"%2\"").arg( (*it)->line+1 ).arg( bText ),
+ m_view, SLOT(gotoLineNumber(int)), 0, (*it)->line, idx );
+
+ if ( (*it)->line < line )
+ {
+ if ( ! prev || prev->line < (*it)->line )
+ prev = (*it);
+ }
+
+ else if ( (*it)->line > line )
+ {
+ if ( ! next || next->line > (*it)->line )
+ next = (*it);
+ }
+ }
+ }
+
+ idx = ++old_menu_count;
+ if ( next )
+ {
+ m_goNext->setText( i18n("&Next: %1 - \"%2\"").arg( next->line + 1 )
+ .arg( KStringHandler::rsqueeze( m_view->getDoc()->textLine( next->line ), 24 ) ) );
+ m_goNext->plug( &menu, idx );
+ idx++;
+ }
+ if ( prev )
+ {
+ m_goPrevious->setText( i18n("&Previous: %1 - \"%2\"").arg(prev->line + 1 )
+ .arg( KStringHandler::rsqueeze( m_view->getDoc()->textLine( prev->line ), 24 ) ) );
+ m_goPrevious->plug( &menu, idx );
+ idx++;
+ }
+ if ( next || prev )
+ menu.insertSeparator( idx );
+
+}
+
+void KateBookmarks::bookmarkMenuAboutToShow()
+{
+
+ QPtrList<KTextEditor::Mark> m = m_view->getDoc()->marks();
+
+ m_bookmarksMenu->clear();
+ m_bookmarkToggle->setChecked( m_view->getDoc()->mark( m_view->cursorLine() )
+ & KTextEditor::MarkInterface::markType01 );
+ m_bookmarkToggle->plug( m_bookmarksMenu );
+ m_bookmarkClear->plug( m_bookmarksMenu );
+
+
+ insertBookmarks(*m_bookmarksMenu);
+}
+
+/*
+ Make sure next/prev actions are plugged, and have a clean text
+*/
+void KateBookmarks::bookmarkMenuAboutToHide()
+{
+ m_bookmarkToggle->plug( m_bookmarksMenu );
+ m_bookmarkClear->plug( m_bookmarksMenu );
+ m_goNext->setText( i18n("Next Bookmark") );
+ m_goNext->plug( m_bookmarksMenu );
+ m_goPrevious->setText( i18n("Previous Bookmark") );
+ m_goPrevious->plug( m_bookmarksMenu );
+}
+
+void KateBookmarks::goNext()
+{
+ QPtrList<KTextEditor::Mark> m = m_view->getDoc()->marks();
+ if (m.isEmpty())
+ return;
+
+ uint line = m_view->cursorLine();
+ int found = -1;
+
+ for (uint z=0; z < m.count(); z++)
+ if ( (m.at(z)->line > line) && ((found == -1) || (uint(found) > m.at(z)->line)) )
+ found = m.at(z)->line;
+
+ if (found != -1)
+ m_view->gotoLineNumber ( found );
+}
+
+void KateBookmarks::goPrevious()
+{
+ QPtrList<KTextEditor::Mark> m = m_view->getDoc()->marks();
+ if (m.isEmpty())
+ return;
+
+ uint line = m_view->cursorLine();
+ int found = -1;
+
+ for (uint z=0; z < m.count(); z++)
+ if ((m.at(z)->line < line) && ((found == -1) || (uint(found) < m.at(z)->line)))
+ found = m.at(z)->line;
+
+ if (found != -1)
+ m_view->gotoLineNumber ( found );
+}
+
+void KateBookmarks::marksChanged ()
+{
+ m_bookmarkClear->setEnabled( !m_view->getDoc()->marks().isEmpty() );
+}
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katebookmarks.h b/kate/part/katebookmarks.h
new file mode 100644
index 000000000..0d72c0ccc
--- /dev/null
+++ b/kate/part/katebookmarks.h
@@ -0,0 +1,86 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2002, 2003 Anders Lund <anders.lund@lund.tdcadsl.dk>
+ Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KATE_BOOKMARKS_H__
+#define __KATE_BOOKMARKS_H__
+
+#include <qobject.h>
+#include <qptrlist.h>
+
+class KateView;
+
+namespace KTextEditor { class Mark; }
+
+namespace Kate { class View; }
+
+class KAction;
+class KToggleAction;
+class KActionCollection;
+class QPopupMenu;
+class QMenuData;
+
+class KateBookmarks : public QObject
+{
+ Q_OBJECT
+
+ public:
+ enum Sorting { Position, Creation };
+ KateBookmarks( KateView* parent, Sorting sort=Position );
+ virtual ~KateBookmarks();
+
+ void createActions( KActionCollection* );
+
+ KateBookmarks::Sorting sorting() { return m_sorting; };
+ void setSorting( Sorting s ) { m_sorting = s; };
+
+ protected:
+ void insertBookmarks( QPopupMenu& menu);
+
+ private slots:
+ void toggleBookmark();
+ void clearBookmarks();
+
+ void slotViewGotFocus( Kate::View * );
+ void slotViewLostFocus( Kate::View * );
+
+ void bookmarkMenuAboutToShow();
+ void bookmarkMenuAboutToHide();
+
+ void goNext();
+ void goPrevious();
+
+ void marksChanged ();
+
+ private:
+ KateView* m_view;
+ KToggleAction* m_bookmarkToggle;
+ KAction* m_bookmarkClear;
+ KAction* m_goNext;
+ KAction* m_goPrevious;
+
+ Sorting m_sorting;
+ QPopupMenu* m_bookmarksMenu;
+
+ uint _tries;
+};
+
+#endif
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
+// vim: noet ts=2
diff --git a/kate/part/katebuffer.cpp b/kate/part/katebuffer.cpp
new file mode 100644
index 000000000..06c919f96
--- /dev/null
+++ b/kate/part/katebuffer.cpp
@@ -0,0 +1,1660 @@
+/* This file is part of the KDE libraries
+ Copyright (c) 2000 Waldo Bastian <bastian@kde.org>
+ Copyright (C) 2002-2004 Christoph Cullmann <cullmann@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "katebuffer.h"
+#include "katebuffer.moc"
+
+#include "katedocument.h"
+#include "katehighlight.h"
+#include "kateconfig.h"
+#include "katefactory.h"
+#include "kateautoindent.h"
+
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kcharsets.h>
+
+#include <qpopupmenu.h>
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qtimer.h>
+#include <qtextcodec.h>
+#include <qcstring.h>
+#include <qdatetime.h>
+
+/**
+ * loader block size, load 256 kb at once per default
+ * if file size is smaller, fall back to file size
+ */
+static const Q_ULONG KATE_FILE_LOADER_BS = 256 * 1024;
+
+/**
+ * KATE_AVG_BLOCK_SIZE is in characters !
+ * (internaly we calc with approx 80 chars per line !)
+ * block will max contain around BLOCK_SIZE chars or
+ * BLOCK_LINES lines (after load, later that won't be tracked)
+ */
+static const Q_ULONG KATE_AVG_BLOCK_SIZE = 2048 * 80;
+static const Q_ULONG KATE_MAX_BLOCK_LINES = 2048;
+
+/**
+ * hl will look at the next KATE_HL_LOOKAHEAD lines
+ * or until the current block ends if a line is requested
+ * will avoid to run doHighlight too often
+ */
+static const uint KATE_HL_LOOKAHEAD = 64;
+
+/**
+ * KATE_MAX_BLOCKS_LOADED should be at least 4, as some
+ * methodes will cause heavy trashing, if not at least the
+ * latest 2-3 used blocks are alive
+ */
+uint KateBuffer::m_maxLoadedBlocks = 16;
+
+/**
+ * Initial value for m_maxDynamicContexts
+ */
+static const uint KATE_MAX_DYNAMIC_CONTEXTS = 512;
+
+void KateBuffer::setMaxLoadedBlocks (uint count)
+{
+ m_maxLoadedBlocks = kMax (4U, count);
+}
+
+class KateFileLoader
+{
+ public:
+ KateFileLoader (const QString &filename, QTextCodec *codec, bool removeTrailingSpaces)
+ : m_file (filename)
+ , m_buffer (kMin (m_file.size(), KATE_FILE_LOADER_BS))
+ , m_codec (codec)
+ , m_decoder (m_codec->makeDecoder())
+ , m_position (0)
+ , m_lastLineStart (0)
+ , m_eof (false) // default to not eof
+ , lastWasEndOfLine (true) // at start of file, we had a virtual newline
+ , lastWasR (false) // we have not found a \r as last char
+ , m_eol (-1) // no eol type detected atm
+ , m_twoByteEncoding (QString(codec->name()) == "ISO-10646-UCS-2")
+ , m_binary (false)
+ , m_removeTrailingSpaces (removeTrailingSpaces)
+ {
+ kdDebug (13020) << "OPEN USES ENCODING: " << m_codec->name() << endl;
+ }
+
+ ~KateFileLoader ()
+ {
+ delete m_decoder;
+ }
+
+ /**
+ * open file, read first chunk of data, detect eol
+ */
+ bool open ()
+ {
+ if (m_file.open (IO_ReadOnly))
+ {
+ int c = m_file.readBlock (m_buffer.data(), m_buffer.size());
+
+ if (c > 0)
+ {
+ // fix utf16 LE, stolen from khtml ;)
+ if ((c >= 2) && (m_codec->mibEnum() == 1000) && (m_buffer[1] == 0x00))
+ {
+ // utf16LE, we need to put the decoder in LE mode
+ char reverseUtf16[3] = {0xFF, 0xFE, 0x00};
+ m_decoder->toUnicode(reverseUtf16, 2);
+ }
+
+ processNull (c);
+ m_text = m_decoder->toUnicode (m_buffer, c);
+ }
+
+ m_eof = (c == -1) || (c == 0) || (m_text.length() == 0) || m_file.atEnd();
+
+ for (uint i=0; i < m_text.length(); i++)
+ {
+ if (m_text[i] == '\n')
+ {
+ m_eol = KateDocumentConfig::eolUnix;
+ break;
+ }
+ else if ((m_text[i] == '\r'))
+ {
+ if (((i+1) < m_text.length()) && (m_text[i+1] == '\n'))
+ {
+ m_eol = KateDocumentConfig::eolDos;
+ break;
+ }
+ else
+ {
+ m_eol = KateDocumentConfig::eolMac;
+ break;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ // no new lines around ?
+ inline bool eof () const { return m_eof && !lastWasEndOfLine && (m_lastLineStart == m_text.length()); }
+
+ // eol mode ? autodetected on open(), -1 for no eol found in the first block!
+ inline int eol () const { return m_eol; }
+
+ // binary ?
+ inline bool binary () const { return m_binary; }
+
+ // should spaces be ignored at end of line?
+ inline bool removeTrailingSpaces () const { return m_removeTrailingSpaces; }
+
+ // internal unicode data array
+ inline const QChar *unicode () const { return m_text.unicode(); }
+
+ // read a line, return length + offset in unicode data
+ void readLine (uint &offset, uint &length)
+ {
+ length = 0;
+ offset = 0;
+
+ while (m_position <= m_text.length())
+ {
+ if (m_position == m_text.length())
+ {
+ // try to load more text if something is around
+ if (!m_eof)
+ {
+ int c = m_file.readBlock (m_buffer.data(), m_buffer.size());
+
+ uint readString = 0;
+ if (c > 0)
+ {
+ processNull (c);
+
+ QString str (m_decoder->toUnicode (m_buffer, c));
+ readString = str.length();
+
+ m_text = m_text.mid (m_lastLineStart, m_position-m_lastLineStart)
+ + str;
+ }
+ else
+ m_text = m_text.mid (m_lastLineStart, m_position-m_lastLineStart);
+
+ // is file completly read ?
+ m_eof = (c == -1) || (c == 0) || (readString == 0) || m_file.atEnd();
+
+ // recalc current pos and last pos
+ m_position -= m_lastLineStart;
+ m_lastLineStart = 0;
+ }
+
+ // oh oh, end of file, escape !
+ if (m_eof && (m_position == m_text.length()))
+ {
+ lastWasEndOfLine = false;
+
+ // line data
+ offset = m_lastLineStart;
+ length = m_position-m_lastLineStart;
+
+ m_lastLineStart = m_position;
+
+ return;
+ }
+ }
+
+ if (m_text[m_position] == '\n')
+ {
+ lastWasEndOfLine = true;
+
+ if (lastWasR)
+ {
+ m_lastLineStart++;
+ lastWasR = false;
+ }
+ else
+ {
+ // line data
+ offset = m_lastLineStart;
+ length = m_position-m_lastLineStart;
+
+ m_lastLineStart = m_position+1;
+ m_position++;
+
+ return;
+ }
+ }
+ else if (m_text[m_position] == '\r')
+ {
+ lastWasEndOfLine = true;
+ lastWasR = true;
+
+ // line data
+ offset = m_lastLineStart;
+ length = m_position-m_lastLineStart;
+
+ m_lastLineStart = m_position+1;
+ m_position++;
+
+ return;
+ }
+ else
+ {
+ lastWasEndOfLine = false;
+ lastWasR = false;
+ }
+
+ m_position++;
+ }
+ }
+
+ // this nice methode will kill all 0 bytes (or double bytes)
+ // and remember if this was a binary or not ;)
+ void processNull (uint length)
+ {
+ if (m_twoByteEncoding)
+ {
+ for (uint i=1; i < length; i+=2)
+ {
+ if ((m_buffer[i] == 0) && (m_buffer[i-1] == 0))
+ {
+ m_binary = true;
+ m_buffer[i] = ' ';
+ }
+ }
+ }
+ else
+ {
+ for (uint i=0; i < length; i++)
+ {
+ if (m_buffer[i] == 0)
+ {
+ m_binary = true;
+ m_buffer[i] = ' ';
+ }
+ }
+ }
+ }
+
+ private:
+ QFile m_file;
+ QByteArray m_buffer;
+ QTextCodec *m_codec;
+ QTextDecoder *m_decoder;
+ QString m_text;
+ uint m_position;
+ uint m_lastLineStart;
+ bool m_eof;
+ bool lastWasEndOfLine;
+ bool lastWasR;
+ int m_eol;
+ bool m_twoByteEncoding;
+ bool m_binary;
+ bool m_removeTrailingSpaces;
+};
+
+/**
+ * Create an empty buffer. (with one block with one empty line)
+ */
+KateBuffer::KateBuffer(KateDocument *doc)
+ : QObject (doc),
+ editSessionNumber (0),
+ editIsRunning (false),
+ editTagLineStart (0xffffffff),
+ editTagLineEnd (0),
+ editTagLineFrom (false),
+ editChangesDone (false),
+ m_doc (doc),
+ m_lines (0),
+ m_lastInSyncBlock (0),
+ m_lastFoundBlock (0),
+ m_cacheReadError(false),
+ m_cacheWriteError(false),
+ m_loadingBorked (false),
+ m_binary (false),
+ m_highlight (0),
+ m_regionTree (this),
+ m_tabWidth (8),
+ m_lineHighlightedMax (0),
+ m_lineHighlighted (0),
+ m_maxDynamicContexts (KATE_MAX_DYNAMIC_CONTEXTS)
+{
+ clear();
+}
+
+/**
+ * Cleanup on destruction
+ */
+KateBuffer::~KateBuffer()
+{
+ // DELETE ALL BLOCKS, will free mem
+ for (uint i=0; i < m_blocks.size(); i++)
+ delete m_blocks[i];
+
+ // release HL
+ if (m_highlight)
+ m_highlight->release();
+}
+
+void KateBuffer::editStart ()
+{
+ editSessionNumber++;
+
+ if (editSessionNumber > 1)
+ return;
+
+ editIsRunning = true;
+
+ editTagLineStart = 0xffffffff;
+ editTagLineEnd = 0;
+ editTagLineFrom = false;
+
+ editChangesDone = false;
+}
+
+void KateBuffer::editEnd ()
+{
+ if (editSessionNumber == 0)
+ return;
+
+ editSessionNumber--;
+
+ if (editSessionNumber > 0)
+ return;
+
+ if (editChangesDone)
+ {
+ // hl update !!!
+ if ( m_highlight && !m_highlight->noHighlighting()
+ && (editTagLineStart <= editTagLineEnd)
+ && (editTagLineEnd <= m_lineHighlighted))
+ {
+ // look one line too far, needed for linecontinue stuff
+ editTagLineEnd++;
+
+ // look one line before, needed nearly 100% only for indentation based folding !
+ if (editTagLineStart > 0)
+ editTagLineStart--;
+
+ KateBufBlock *buf2 = 0;
+ bool needContinue = false;
+ while ((buf2 = findBlock(editTagLineStart)))
+ {
+ needContinue = doHighlight (buf2,
+ (editTagLineStart > buf2->startLine()) ? editTagLineStart : buf2->startLine(),
+ (editTagLineEnd > buf2->endLine()) ? buf2->endLine() : editTagLineEnd,
+ true);
+
+ editTagLineStart = (editTagLineEnd > buf2->endLine()) ? buf2->endLine() : editTagLineEnd;
+
+ if ((editTagLineStart >= m_lines) || (editTagLineStart >= editTagLineEnd))
+ break;
+ }
+
+ if (needContinue)
+ m_lineHighlighted = editTagLineStart;
+
+ if (editTagLineStart > m_lineHighlightedMax)
+ m_lineHighlightedMax = editTagLineStart;
+ }
+ else if (editTagLineStart < m_lineHighlightedMax)
+ m_lineHighlightedMax = editTagLineStart;
+ }
+
+ editIsRunning = false;
+}
+
+void KateBuffer::clear()
+{
+ m_regionTree.clear();
+
+ // cleanup the blocks
+ for (uint i=0; i < m_blocks.size(); i++)
+ delete m_blocks[i];
+
+ m_blocks.clear ();
+
+ // create a bufblock with one line, we need that, only in openFile we won't have that
+ KateBufBlock *block = new KateBufBlock(this, 0, 0);
+ m_blocks.append (block);
+
+ // reset the state
+ m_lines = block->lines();
+ m_lastInSyncBlock = 0;
+ m_lastFoundBlock = 0;
+ m_cacheWriteError = false;
+ m_cacheReadError = false;
+ m_loadingBorked = false;
+ m_binary = false;
+
+ m_lineHighlightedMax = 0;
+ m_lineHighlighted = 0;
+}
+
+bool KateBuffer::openFile (const QString &m_file)
+{
+ KateFileLoader file (m_file, m_doc->config()->codec(), m_doc->configFlags() & KateDocument::cfRemoveSpaces);
+
+ bool ok = false;
+ struct stat sbuf;
+ if (stat(QFile::encodeName(m_file), &sbuf) == 0)
+ {
+ if (S_ISREG(sbuf.st_mode) && file.open())
+ ok = true;
+ }
+
+ if (!ok)
+ {
+ clear();
+ return false; // Error
+ }
+
+ // set eol mode, if a eol char was found in the first 256kb block and we allow this at all!
+ if (m_doc->config()->allowEolDetection() && (file.eol() != -1))
+ m_doc->config()->setEol (file.eol());
+
+ // flush current content
+ clear ();
+
+ // cleanup the blocks
+ for (uint i=0; i < m_blocks.size(); i++)
+ delete m_blocks[i];
+
+ m_blocks.clear ();
+
+ // do the real work
+ KateBufBlock *block = 0;
+ m_lines = 0;
+ while (!file.eof() && !m_cacheWriteError)
+ {
+ block = new KateBufBlock (this, block, 0, &file);
+
+ m_lines = block->endLine ();
+
+ if (m_cacheWriteError || (block->lines() == 0))
+ {
+ delete block;
+ break;
+ }
+ else
+ m_blocks.append (block);
+ }
+
+ // we had a cache write error, this load is really borked !
+ if (m_cacheWriteError)
+ m_loadingBorked = true;
+
+ if (m_blocks.isEmpty() || (m_lines == 0))
+ {
+ // file was really empty, clean the buffers + emit the line changed
+ // loadingBorked will be false for such files, not matter what happened
+ // before
+ clear ();
+ }
+ else
+ {
+ // fix region tree
+ m_regionTree.fixRoot (m_lines);
+ }
+
+ // if we have no hl or the "None" hl activated, whole file is correct highlighted
+ // after loading, which wonder ;)
+ if (!m_highlight || m_highlight->noHighlighting())
+ {
+ m_lineHighlighted = m_lines;
+ m_lineHighlightedMax = m_lines;
+ }
+
+ // binary?
+ m_binary = file.binary ();
+
+ kdDebug (13020) << "LOADING DONE" << endl;
+
+ return !m_loadingBorked;
+}
+
+bool KateBuffer::canEncode ()
+{
+ QTextCodec *codec = m_doc->config()->codec();
+
+ kdDebug(13020) << "ENC NAME: " << codec->name() << endl;
+
+ // hardcode some unicode encodings which can encode all chars
+ if ((QString(codec->name()) == "UTF-8") || (QString(codec->name()) == "ISO-10646-UCS-2"))
+ return true;
+
+ for (uint i=0; i < m_lines; i++)
+ {
+ if (!codec->canEncode (plainLine(i)->string()))
+ {
+ kdDebug(13020) << "STRING LINE: " << plainLine(i)->string() << endl;
+ kdDebug(13020) << "ENC WORKING: FALSE" << endl;
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool KateBuffer::saveFile (const QString &m_file)
+{
+ QFile file (m_file);
+ QTextStream stream (&file);
+
+ if ( !file.open( IO_WriteOnly ) )
+ {
+ return false; // Error
+ }
+
+ QTextCodec *codec = m_doc->config()->codec();
+
+ // disable Unicode headers
+ stream.setEncoding(QTextStream::RawUnicode);
+
+ // this line sets the mapper to the correct codec
+ stream.setCodec(codec);
+
+ // our loved eol string ;)
+ QString eol = m_doc->config()->eolString ();
+
+ // should we strip spaces?
+ bool removeTrailingSpaces = m_doc->configFlags() & KateDocument::cfRemoveSpaces;
+
+ // just dump the lines out ;)
+ for (uint i=0; i < m_lines; i++)
+ {
+ KateTextLine::Ptr textline = plainLine(i);
+
+ // strip spaces
+ if (removeTrailingSpaces)
+ {
+ int lastChar = textline->lastChar();
+
+ if (lastChar > -1)
+ {
+ stream << QConstString (textline->text(), lastChar+1).string();
+ }
+ }
+ else // simple, dump the line
+ stream << textline->string();
+
+ if ((i+1) < m_lines)
+ stream << eol;
+ }
+
+ file.close ();
+
+ m_loadingBorked = false;
+
+ return (file.status() == IO_Ok);
+}
+
+KateTextLine::Ptr KateBuffer::line_internal (KateBufBlock *buf, uint i)
+{
+ // update hl until this line + max KATE_HL_LOOKAHEAD
+ KateBufBlock *buf2 = 0;
+ while ((i >= m_lineHighlighted) && (buf2 = findBlock(m_lineHighlighted)))
+ {
+ uint end = kMin(i + KATE_HL_LOOKAHEAD, buf2->endLine());
+
+ doHighlight ( buf2,
+ kMax(m_lineHighlighted, buf2->startLine()),
+ end,
+ false );
+
+ m_lineHighlighted = end;
+ }
+
+ // update hl max
+ if (m_lineHighlighted > m_lineHighlightedMax)
+ m_lineHighlightedMax = m_lineHighlighted;
+
+ return buf->line (i - buf->startLine());
+}
+
+KateBufBlock *KateBuffer::findBlock_internal (uint i, uint *index)
+{
+ uint lastLine = m_blocks[m_lastInSyncBlock]->endLine ();
+
+ if (lastLine > i) // we are in a allready known area !
+ {
+ while (true)
+ {
+ KateBufBlock *buf = m_blocks[m_lastFoundBlock];
+
+ if ( (buf->startLine() <= i)
+ && (buf->endLine() > i) )
+ {
+ if (index)
+ (*index) = m_lastFoundBlock;
+
+ return m_blocks[m_lastFoundBlock];
+ }
+
+ if (i < buf->startLine())
+ m_lastFoundBlock--;
+ else
+ m_lastFoundBlock++;
+ }
+ }
+ else // we need first to resync the startLines !
+ {
+ if ((m_lastInSyncBlock+1) < m_blocks.size())
+ m_lastInSyncBlock++;
+ else
+ return 0;
+
+ for (; m_lastInSyncBlock < m_blocks.size(); m_lastInSyncBlock++)
+ {
+ // get next block
+ KateBufBlock *buf = m_blocks[m_lastInSyncBlock];
+
+ // sync startLine !
+ buf->setStartLine (lastLine);
+
+ // is it allready the searched block ?
+ if ((i >= lastLine) && (i < buf->endLine()))
+ {
+ // remember this block as last found !
+ m_lastFoundBlock = m_lastInSyncBlock;
+
+ if (index)
+ (*index) = m_lastFoundBlock;
+
+ return buf;
+ }
+
+ // increase lastLine with blocklinecount
+ lastLine += buf->lines ();
+ }
+ }
+
+ // no block found !
+ // index will not be set to any useful value in this case !
+ return 0;
+}
+
+void KateBuffer::changeLine(uint i)
+{
+ KateBufBlock *buf = findBlock(i);
+
+ if (!buf)
+ return;
+
+ // mark this block dirty
+ buf->markDirty ();
+
+ // mark buffer changed
+ editChangesDone = true;
+
+ // tag this line as changed
+ if (i < editTagLineStart)
+ editTagLineStart = i;
+
+ if (i > editTagLineEnd)
+ editTagLineEnd = i;
+}
+
+void KateBuffer::insertLine(uint i, KateTextLine::Ptr line)
+{
+ uint index = 0;
+ KateBufBlock *buf;
+ if (i == m_lines)
+ buf = findBlock(i-1, &index);
+ else
+ buf = findBlock(i, &index);
+
+ if (!buf)
+ return;
+
+ buf->insertLine(i - buf->startLine(), line);
+
+ if (m_lineHighlightedMax > i)
+ m_lineHighlightedMax++;
+
+ if (m_lineHighlighted > i)
+ m_lineHighlighted++;
+
+ m_lines++;
+
+ // last sync block adjust
+ if (m_lastInSyncBlock > index)
+ m_lastInSyncBlock = index;
+
+ // last found
+ if (m_lastInSyncBlock < m_lastFoundBlock)
+ m_lastFoundBlock = m_lastInSyncBlock;
+
+ // mark buffer changed
+ editChangesDone = true;
+
+ // tag this line as inserted
+ if (i < editTagLineStart)
+ editTagLineStart = i;
+
+ if (i <= editTagLineEnd)
+ editTagLineEnd++;
+
+ if (i > editTagLineEnd)
+ editTagLineEnd = i;
+
+ // line inserted
+ editTagLineFrom = true;
+
+ m_regionTree.lineHasBeenInserted (i);
+}
+
+void KateBuffer::removeLine(uint i)
+{
+ uint index = 0;
+ KateBufBlock *buf = findBlock(i, &index);
+
+ if (!buf)
+ return;
+
+ buf->removeLine(i - buf->startLine());
+
+ if (m_lineHighlightedMax > i)
+ m_lineHighlightedMax--;
+
+ if (m_lineHighlighted > i)
+ m_lineHighlighted--;
+
+ m_lines--;
+
+ // trash away a empty block
+ if (buf->lines() == 0)
+ {
+ // we need to change which block is last in sync
+ if (m_lastInSyncBlock >= index)
+ {
+ m_lastInSyncBlock = index;
+
+ if (buf->next())
+ {
+ if (buf->prev())
+ buf->next()->setStartLine (buf->prev()->endLine());
+ else
+ buf->next()->setStartLine (0);
+ }
+ }
+
+ // cu block !
+ delete buf;
+ m_blocks.erase (m_blocks.begin()+index);
+
+ // make sure we don't keep a pointer to the deleted block
+ if( m_lastInSyncBlock >= index )
+ m_lastInSyncBlock = index - 1;
+ }
+ else
+ {
+ // last sync block adjust
+ if (m_lastInSyncBlock > index)
+ m_lastInSyncBlock = index;
+ }
+
+ // last found
+ if (m_lastInSyncBlock < m_lastFoundBlock)
+ m_lastFoundBlock = m_lastInSyncBlock;
+
+ // mark buffer changed
+ editChangesDone = true;
+
+ // tag this line as removed
+ if (i < editTagLineStart)
+ editTagLineStart = i;
+
+ if (i < editTagLineEnd)
+ editTagLineEnd--;
+
+ if (i > editTagLineEnd)
+ editTagLineEnd = i;
+
+ // line removed
+ editTagLineFrom = true;
+
+ m_regionTree.lineHasBeenRemoved (i);
+}
+
+void KateBuffer::setTabWidth (uint w)
+{
+ if ((m_tabWidth != w) && (m_tabWidth > 0))
+ {
+ m_tabWidth = w;
+
+ if (m_highlight && m_highlight->foldingIndentationSensitive())
+ invalidateHighlighting();
+ }
+}
+
+void KateBuffer::setHighlight(uint hlMode)
+{
+ KateHighlighting *h = KateHlManager::self()->getHl(hlMode);
+
+ // aha, hl will change
+ if (h != m_highlight)
+ {
+ bool invalidate = !h->noHighlighting();
+
+ if (m_highlight)
+ {
+ m_highlight->release();
+ invalidate = true;
+ }
+
+ h->use();
+
+ // Clear code folding tree (see bug #124102)
+ m_regionTree.clear();
+ m_regionTree.fixRoot(m_lines);
+
+ // try to set indentation
+ if (!h->indentation().isEmpty())
+ m_doc->config()->setIndentationMode (KateAutoIndent::modeNumber(h->indentation()));
+
+ m_highlight = h;
+
+ if (invalidate)
+ invalidateHighlighting();
+
+ // inform the document that the hl was really changed
+ // needed to update attributes and more ;)
+ m_doc->bufferHlChanged ();
+ }
+}
+
+void KateBuffer::invalidateHighlighting()
+{
+ m_lineHighlightedMax = 0;
+ m_lineHighlighted = 0;
+}
+
+
+void KateBuffer::updatePreviousNotEmptyLine(KateBufBlock *blk,uint current_line,bool addindent,uint deindent)
+{
+ KateTextLine::Ptr textLine;
+ do {
+ if (current_line>0) current_line--;
+ else
+ {
+ uint line=blk->startLine()+current_line;
+ if (line==0) return;
+ line--;
+ blk=findBlock(line);
+ if (!blk) {
+ kdDebug(13020)<<"updatePreviousNotEmptyLine: block not found, this must not happen"<<endl;
+ return;
+ }
+ current_line=line-blk->startLine();
+ }
+ textLine = blk->line(current_line);
+ } while (textLine->firstChar()==-1);
+ kdDebug(13020)<<"updatePreviousNotEmptyLine: updating line:"<<(blk->startLine()+current_line)<<endl;
+ QMemArray<uint> foldingList=textLine->foldingListArray();
+ while ( (foldingList.size()>0) && ( abs(foldingList[foldingList.size()-2])==1)) {
+ foldingList.resize(foldingList.size()-2,QGArray::SpeedOptim);
+ }
+ addIndentBasedFoldingInformation(foldingList,addindent,deindent);
+ textLine->setFoldingList(foldingList);
+ bool retVal_folding = false;
+ m_regionTree.updateLine (current_line + blk->startLine(), &foldingList, &retVal_folding, true,false);
+ emit tagLines (blk->startLine()+current_line, blk->startLine()+current_line);
+}
+
+void KateBuffer::addIndentBasedFoldingInformation(QMemArray<uint> &foldingList,bool addindent,uint deindent)
+{
+ if (addindent) {
+ //kdDebug(13020)<<"adding indent for line :"<<current_line + buf->startLine()<<" textLine->noIndentBasedFoldingAtStart"<<textLine->noIndentBasedFoldingAtStart()<<endl;
+ kdDebug(13020)<<"adding ident"<<endl;
+ foldingList.resize (foldingList.size() + 2, QGArray::SpeedOptim);
+ foldingList[foldingList.size()-2] = 1;
+ foldingList[foldingList.size()-1] = 0;
+ }
+ kdDebug(13020)<<"DEINDENT: "<<deindent<<endl;
+ if (deindent > 0)
+ {
+ foldingList.resize (foldingList.size() + (deindent*2), QGArray::SpeedOptim);
+
+ for (uint z= foldingList.size()-(deindent*2); z < foldingList.size(); z=z+2)
+ {
+ foldingList[z] = -1;
+ foldingList[z+1] = 0;
+ }
+ }
+}
+
+bool KateBuffer::doHighlight (KateBufBlock *buf, uint startLine, uint endLine, bool invalidate)
+{
+ // no hl around, no stuff to do
+ if (!m_highlight)
+ return false;
+
+ /*if (m_highlight->foldingIndentationSensitive())
+ {
+ startLine=0;
+ endLine=50;
+ }*/
+
+ // we tried to start in a line behind this buf block !
+ if (startLine >= (buf->startLine()+buf->lines()))
+ return false;
+
+ //QTime t;
+ //t.start();
+ //kdDebug (13020) << "HIGHLIGHTED START --- NEED HL, LINESTART: " << startLine << " LINEEND: " << endLine << endl;
+ //kdDebug (13020) << "HL UNTIL LINE: " << m_lineHighlighted << " MAX: " << m_lineHighlightedMax << endl;
+ //kdDebug (13020) << "HL DYN COUNT: " << KateHlManager::self()->countDynamicCtxs() << " MAX: " << m_maxDynamicContexts << endl;
+
+ // see if there are too many dynamic contexts; if yes, invalidate HL of all documents
+ if (KateHlManager::self()->countDynamicCtxs() >= m_maxDynamicContexts)
+ {
+ {
+ if (KateHlManager::self()->resetDynamicCtxs())
+ {
+ kdDebug (13020) << "HL invalidated - too many dynamic contexts ( >= " << m_maxDynamicContexts << ")" << endl;
+
+ // avoid recursive invalidation
+ KateHlManager::self()->setForceNoDCReset(true);
+
+ for (KateDocument *doc = KateFactory::self()->documents()->first(); doc; doc = KateFactory::self()->documents()->next())
+ doc->makeAttribs();
+
+ // doHighlight *shall* do his work. After invalidation, some highlight has
+ // been recalculated, but *maybe not* until endLine ! So we shall force it manually...
+ KateBufBlock *buf = 0;
+ while ((endLine > m_lineHighlighted) && (buf = findBlock(m_lineHighlighted)))
+ {
+ uint end = kMin(endLine, buf->endLine());
+
+ doHighlight ( buf,
+ kMax(m_lineHighlighted, buf->startLine()),
+ end,
+ false );
+
+ m_lineHighlighted = end;
+ }
+
+ KateHlManager::self()->setForceNoDCReset(false);
+
+ return false;
+ }
+ else
+ {
+ m_maxDynamicContexts *= 2;
+ kdDebug (13020) << "New dynamic contexts limit: " << m_maxDynamicContexts << endl;
+ }
+ }
+ }
+
+ // get the previous line, if we start at the beginning of this block
+ // take the last line of the previous block
+ KateTextLine::Ptr prevLine = 0;
+
+ if ((startLine == buf->startLine()) && buf->prev() && (buf->prev()->lines() > 0))
+ prevLine = buf->prev()->line (buf->prev()->lines() - 1);
+ else if ((startLine > buf->startLine()) && (startLine <= buf->endLine()))
+ prevLine = buf->line(startLine - buf->startLine() - 1);
+ else
+ prevLine = new KateTextLine ();
+
+ // does we need to emit a signal for the folding changes ?
+ bool codeFoldingUpdate = false;
+
+ // here we are atm, start at start line in the block
+ uint current_line = startLine - buf->startLine();
+
+ // do we need to continue
+ bool stillcontinue=false;
+ bool indentContinueWhitespace=false;
+ bool indentContinueNextWhitespace=false;
+ // loop over the lines of the block, from startline to endline or end of block
+ // if stillcontinue forces us to do so
+ while ( (current_line < buf->lines())
+ && (stillcontinue || ((current_line + buf->startLine()) <= endLine)) )
+ {
+ // current line
+ KateTextLine::Ptr textLine = buf->line(current_line);
+
+ QMemArray<uint> foldingList;
+ bool ctxChanged = false;
+
+ m_highlight->doHighlight (prevLine, textLine, &foldingList, &ctxChanged);
+
+ //
+ // indentation sensitive folding
+ //
+ bool indentChanged = false;
+ if (m_highlight->foldingIndentationSensitive())
+ {
+ // get the indentation array of the previous line to start with !
+ QMemArray<unsigned short> indentDepth;
+ indentDepth.duplicate (prevLine->indentationDepthArray());
+
+ // current indentation of this line
+ uint iDepth = textLine->indentDepth(m_tabWidth);
+ if ((current_line+buf->startLine())==0)
+ {
+ indentDepth.resize (1, QGArray::SpeedOptim);
+ indentDepth[0] = iDepth;
+ }
+
+ textLine->setNoIndentBasedFoldingAtStart(prevLine->noIndentBasedFolding());
+ // this line is empty, beside spaces, or has indentaion based folding disabled, use indentation depth of the previous line !
+ kdDebug(13020)<<"current_line:"<<current_line + buf->startLine()<<" textLine->noIndentBasedFoldingAtStart"<<textLine->noIndentBasedFoldingAtStart()<<endl;
+ if ( (textLine->firstChar() == -1) || textLine->noIndentBasedFoldingAtStart())
+ {
+ // do this to get skipped empty lines indent right, which was given in the indenation array
+ if (!prevLine->indentationDepthArray().isEmpty())
+ {
+ iDepth = (prevLine->indentationDepthArray())[prevLine->indentationDepthArray().size()-1];
+ kdDebug(13020)<<"reusing old depth as current"<<endl;
+ }
+ else
+ {
+ iDepth = prevLine->indentDepth(m_tabWidth);
+ kdDebug(13020)<<"creating indentdepth for previous line"<<endl;
+ }
+ }
+
+ kdDebug(13020)<<"iDepth:"<<iDepth<<endl;
+
+ // query the next line indentation, if we are at the end of the block
+ // use the first line of the next buf block
+ uint nextLineIndentation = 0;
+ bool nextLineIndentationValid=true;
+ indentContinueNextWhitespace=false;
+ if ((current_line+1) < buf->lines())
+ {
+ if (buf->line(current_line+1)->firstChar() == -1)
+ {
+ nextLineIndentation = iDepth;
+ indentContinueNextWhitespace=true;
+ }
+ else
+ nextLineIndentation = buf->line(current_line+1)->indentDepth(m_tabWidth);
+ }
+ else
+ {
+ KateBufBlock *blk = buf->next();
+
+ if (blk && (blk->lines() > 0))
+ {
+ if (blk->line (0)->firstChar() == -1)
+ {
+ nextLineIndentation = iDepth;
+ indentContinueNextWhitespace=true;
+ }
+ else
+ nextLineIndentation = blk->line (0)->indentDepth(m_tabWidth);
+ }
+ else nextLineIndentationValid=false;
+ }
+
+ if (!textLine->noIndentBasedFoldingAtStart()) {
+
+ if ((iDepth > 0) && (indentDepth.isEmpty() || (indentDepth[indentDepth.size()-1] < iDepth)))
+ {
+ kdDebug(13020)<<"adding depth to \"stack\":"<<iDepth<<endl;
+ indentDepth.resize (indentDepth.size()+1, QGArray::SpeedOptim);
+ indentDepth[indentDepth.size()-1] = iDepth;
+ } else {
+ if (!indentDepth.isEmpty())
+ {
+ for (int z=indentDepth.size()-1; z > -1; z--)
+ if (indentDepth[z]>iDepth)
+ indentDepth.resize(z, QGArray::SpeedOptim);
+ if ((iDepth > 0) && (indentDepth.isEmpty() || (indentDepth[indentDepth.size()-1] < iDepth)))
+ {
+ kdDebug(13020)<<"adding depth to \"stack\":"<<iDepth<<endl;
+ indentDepth.resize (indentDepth.size()+1, QGArray::SpeedOptim);
+ indentDepth[indentDepth.size()-1] = iDepth;
+ if (prevLine->firstChar()==-1) {
+
+ }
+ }
+ }
+ }
+ }
+
+ if (!textLine->noIndentBasedFolding())
+ {
+ if (nextLineIndentationValid)
+ {
+ //if (textLine->firstChar()!=-1)
+ {
+ kdDebug(13020)<<"nextLineIndentation:"<<nextLineIndentation<<endl;
+ bool addindent=false;
+ uint deindent=0;
+ if (!indentDepth.isEmpty())
+ kdDebug()<<"indentDepth[indentDepth.size()-1]:"<<indentDepth[indentDepth.size()-1]<<endl;
+ if ((nextLineIndentation>0) && ( indentDepth.isEmpty() || (indentDepth[indentDepth.size()-1]<nextLineIndentation)))
+ {
+ kdDebug(13020)<<"addindent==true"<<endl;
+ addindent=true;
+ } else {
+ if ((!indentDepth.isEmpty()) && (indentDepth[indentDepth.size()-1]>nextLineIndentation))
+ {
+ kdDebug(13020)<<"...."<<endl;
+ for (int z=indentDepth.size()-1; z > -1; z--)
+ {
+ kdDebug(13020)<<indentDepth[z]<<" "<<nextLineIndentation<<endl;
+ if (indentDepth[z]>nextLineIndentation)
+ deindent++;
+ }
+ }
+ }
+/* }
+ if (textLine->noIndentBasedFolding()) kdDebug(13020)<<"=============================indentation based folding disabled======================"<<endl;
+ if (!textLine->noIndentBasedFolding()) {*/
+ if ((textLine->firstChar()==-1)) {
+ updatePreviousNotEmptyLine(buf,current_line,addindent,deindent);
+ codeFoldingUpdate=true;
+ }
+ else
+ {
+ addIndentBasedFoldingInformation(foldingList,addindent,deindent);
+ }
+ }
+ }
+ }
+ indentChanged = !(indentDepth == textLine->indentationDepthArray());
+
+ // assign the new array to the textline !
+ if (indentChanged)
+ textLine->setIndentationDepth (indentDepth);
+
+ indentContinueWhitespace=textLine->firstChar()==-1;
+ }
+ bool foldingColChanged=false;
+ bool foldingChanged = false; //!(foldingList == textLine->foldingListArray());
+ if (foldingList.size()!=textLine->foldingListArray().size()) {
+ foldingChanged=true;
+ } else {
+ QMemArray<uint>::ConstIterator it=foldingList.begin();
+ QMemArray<uint>::ConstIterator it1=textLine->foldingListArray();
+ bool markerType=true;
+ for(;it!=foldingList.end();++it,++it1) {
+ if (markerType) {
+ if ( ((*it)!=(*it1))) {
+ foldingChanged=true;
+ foldingColChanged=false;
+ break;
+ }
+ } else {
+ if ((*it)!=(*it1)) {
+ foldingColChanged=true;
+ }
+ }
+ markerType=!markerType;
+ }
+ }
+
+ if (foldingChanged || foldingColChanged) {
+ textLine->setFoldingList(foldingList);
+ if (foldingChanged==false){
+ textLine->setFoldingColumnsOutdated(textLine->foldingColumnsOutdated() | foldingColChanged);
+ } else textLine->setFoldingColumnsOutdated(false);
+ }
+ bool retVal_folding = false;
+ //perhaps make en enums out of the change flags
+ m_regionTree.updateLine (current_line + buf->startLine(), &foldingList, &retVal_folding, foldingChanged,foldingColChanged);
+
+ codeFoldingUpdate = codeFoldingUpdate | retVal_folding;
+
+ // need we to continue ?
+ stillcontinue = ctxChanged || indentChanged || indentContinueWhitespace || indentContinueNextWhitespace;
+
+ // move around the lines
+ prevLine = textLine;
+
+ // increment line
+ current_line++;
+ }
+
+ buf->markDirty ();
+
+ // tag the changed lines !
+ if (invalidate)
+ emit tagLines (startLine, current_line + buf->startLine());
+
+ // emit that we have changed the folding
+ if (codeFoldingUpdate)
+ emit codeFoldingUpdated();
+
+ //kdDebug (13020) << "HIGHLIGHTED END --- NEED HL, LINESTART: " << startLine << " LINEEND: " << endLine << endl;
+ //kdDebug (13020) << "HL UNTIL LINE: " << m_lineHighlighted << " MAX: " << m_lineHighlightedMax << endl;
+ //kdDebug (13020) << "HL DYN COUNT: " << KateHlManager::self()->countDynamicCtxs() << " MAX: " << m_maxDynamicContexts << endl;
+ //kdDebug (13020) << "TIME TAKEN: " << t.elapsed() << endl;
+
+ // if we are at the last line of the block + we still need to continue
+ // return the need of that !
+ return stillcontinue && ((current_line+1) == buf->lines());
+}
+
+void KateBuffer::codeFoldingColumnUpdate(unsigned int lineNr) {
+ KateTextLine::Ptr line=plainLine(lineNr);
+ if (!line) return;
+ if (line->foldingColumnsOutdated()) {
+ line->setFoldingColumnsOutdated(false);
+ bool tmp;
+ QMemArray<uint> folding=line->foldingListArray();
+ m_regionTree.updateLine(lineNr,&folding,&tmp,true,false);
+ }
+}
+
+//BEGIN KateBufBlock
+
+KateBufBlock::KateBufBlock ( KateBuffer *parent, KateBufBlock *prev, KateBufBlock *next,
+ KateFileLoader *stream )
+: m_state (KateBufBlock::stateDirty),
+ m_startLine (0),
+ m_lines (0),
+ m_vmblock (0),
+ m_vmblockSize (0),
+ m_parent (parent),
+ m_prev (prev),
+ m_next (next),
+ list (0),
+ listPrev (0),
+ listNext (0)
+{
+ // init startline + the next pointers of the neighbour blocks
+ if (m_prev)
+ {
+ m_startLine = m_prev->endLine ();
+ m_prev->m_next = this;
+ }
+
+ if (m_next)
+ m_next->m_prev = this;
+
+ // we have a stream, use it to fill the block !
+ // this can lead to 0 line blocks which are invalid !
+ if (stream)
+ {
+ // this we lead to either dirty or swapped state
+ fillBlock (stream);
+ }
+ else // init the block if no stream given !
+ {
+ // fill in one empty line !
+ KateTextLine::Ptr textLine = new KateTextLine ();
+ m_stringList.push_back (textLine);
+ m_lines++;
+
+ // if we have allready enough blocks around, swap one
+ if (m_parent->m_loadedBlocks.count() >= KateBuffer::maxLoadedBlocks())
+ m_parent->m_loadedBlocks.first()->swapOut();
+
+ // we are a new nearly empty dirty block
+ m_state = KateBufBlock::stateDirty;
+ m_parent->m_loadedBlocks.append (this);
+ }
+}
+
+KateBufBlock::~KateBufBlock ()
+{
+ // sync prev/next pointers
+ if (m_prev)
+ m_prev->m_next = m_next;
+
+ if (m_next)
+ m_next->m_prev = m_prev;
+
+ // if we have some swapped data allocated, free it now or never
+ if (m_vmblock)
+ KateFactory::self()->vm()->free(m_vmblock);
+
+ // remove me from the list I belong
+ KateBufBlockList::remove (this);
+}
+
+void KateBufBlock::fillBlock (KateFileLoader *stream)
+{
+ // is allready too much stuff around in mem ?
+ bool swap = m_parent->m_loadedBlocks.count() >= KateBuffer::maxLoadedBlocks();
+
+ QByteArray rawData;
+
+ // calcs the approx size for KATE_AVG_BLOCK_SIZE chars !
+ if (swap)
+ rawData.resize ((KATE_AVG_BLOCK_SIZE * sizeof(QChar)) + ((KATE_AVG_BLOCK_SIZE/80) * 8));
+
+ char *buf = rawData.data ();
+ uint size = 0;
+ uint blockSize = 0;
+ while (!stream->eof() && (blockSize < KATE_AVG_BLOCK_SIZE) && (m_lines < KATE_MAX_BLOCK_LINES))
+ {
+ uint offset = 0, length = 0;
+ stream->readLine(offset, length);
+ const QChar *unicodeData = stream->unicode () + offset;
+
+ // strip spaces at end of line
+ if ( stream->removeTrailingSpaces() )
+ {
+ while (length > 0)
+ {
+ if (unicodeData[length-1].isSpace())
+ --length;
+ else
+ break;
+ }
+ }
+
+ blockSize += length;
+
+ if (swap)
+ {
+ // create the swapped data on the fly, no need to waste time
+ // via going over the textline classes and dump them !
+ char attr = KateTextLine::flagNoOtherData;
+ uint pos = size;
+
+ // calc new size
+ size = size + 1 + sizeof(uint) + (sizeof(QChar)*length);
+
+ if (size > rawData.size ())
+ {
+ rawData.resize (size);
+ buf = rawData.data ();
+ }
+
+ memcpy(buf+pos, (char *) &attr, 1);
+ pos += 1;
+
+ memcpy(buf+pos, (char *) &length, sizeof(uint));
+ pos += sizeof(uint);
+
+ memcpy(buf+pos, (char *) unicodeData, sizeof(QChar)*length);
+ pos += sizeof(QChar)*length;
+ }
+ else
+ {
+ KateTextLine::Ptr textLine = new KateTextLine ();
+ textLine->insertText (0, length, unicodeData);
+ m_stringList.push_back (textLine);
+ }
+
+ m_lines++;
+ }
+
+ if (swap)
+ {
+ m_vmblock = KateFactory::self()->vm()->allocate(size);
+ m_vmblockSize = size;
+
+ if (!rawData.isEmpty())
+ {
+ if (!KateFactory::self()->vm()->copyBlock(m_vmblock, rawData.data(), 0, size))
+ {
+ if (m_vmblock)
+ KateFactory::self()->vm()->free(m_vmblock);
+
+ m_vmblock = 0;
+ m_vmblockSize = 0;
+
+ m_parent->m_cacheWriteError = true;
+ }
+ }
+
+ // fine, we are swapped !
+ m_state = KateBufBlock::stateSwapped;
+ }
+ else
+ {
+ // we are a new dirty block without any swap data
+ m_state = KateBufBlock::stateDirty;
+ m_parent->m_loadedBlocks.append (this);
+ }
+
+ kdDebug (13020) << "A BLOCK LOADED WITH LINES: " << m_lines << endl;
+}
+
+KateTextLine::Ptr KateBufBlock::line(uint i)
+{
+ // take care that the string list is around !!!
+ if (m_state == KateBufBlock::stateSwapped)
+ swapIn ();
+
+ // LRU
+ if (!m_parent->m_loadedBlocks.isLast(this))
+ m_parent->m_loadedBlocks.append (this);
+
+ return m_stringList[i];
+}
+
+void KateBufBlock::insertLine(uint i, KateTextLine::Ptr line)
+{
+ // take care that the string list is around !!!
+ if (m_state == KateBufBlock::stateSwapped)
+ swapIn ();
+
+ m_stringList.insert (m_stringList.begin()+i, line);
+ m_lines++;
+
+ markDirty ();
+}
+
+void KateBufBlock::removeLine(uint i)
+{
+ // take care that the string list is around !!!
+ if (m_state == KateBufBlock::stateSwapped)
+ swapIn ();
+
+ m_stringList.erase (m_stringList.begin()+i);
+ m_lines--;
+
+ markDirty ();
+}
+
+void KateBufBlock::markDirty ()
+{
+ if (m_state != KateBufBlock::stateSwapped)
+ {
+ // LRU
+ if (!m_parent->m_loadedBlocks.isLast(this))
+ m_parent->m_loadedBlocks.append (this);
+
+ if (m_state == KateBufBlock::stateClean)
+ {
+ // if we have some swapped data allocated which is dirty, free it now
+ if (m_vmblock)
+ KateFactory::self()->vm()->free(m_vmblock);
+
+ m_vmblock = 0;
+ m_vmblockSize = 0;
+
+ // we are dirty
+ m_state = KateBufBlock::stateDirty;
+ }
+ }
+}
+
+void KateBufBlock::swapIn ()
+{
+ if (m_state != KateBufBlock::stateSwapped)
+ return;
+
+ QByteArray rawData (m_vmblockSize);
+
+ // what to do if that fails ?
+ if (!KateFactory::self()->vm()->copyBlock(rawData.data(), m_vmblock, 0, rawData.size()))
+ m_parent->m_cacheReadError = true;
+
+ // reserve mem, keep realloc away on push_back
+ m_stringList.reserve (m_lines);
+
+ char *buf = rawData.data();
+ for (uint i=0; i < m_lines; i++)
+ {
+ KateTextLine::Ptr textLine = new KateTextLine ();
+ buf = textLine->restore (buf);
+ m_stringList.push_back (textLine);
+ }
+
+ // if we have allready enough blocks around, swap one
+ if (m_parent->m_loadedBlocks.count() >= KateBuffer::maxLoadedBlocks())
+ m_parent->m_loadedBlocks.first()->swapOut();
+
+ // fine, we are now clean again, save state + append to clean list
+ m_state = KateBufBlock::stateClean;
+ m_parent->m_loadedBlocks.append (this);
+}
+
+void KateBufBlock::swapOut ()
+{
+ if (m_state == KateBufBlock::stateSwapped)
+ return;
+
+ if (m_state == KateBufBlock::stateDirty)
+ {
+ bool haveHl = m_parent->m_highlight && !m_parent->m_highlight->noHighlighting();
+
+ // Calculate size.
+ uint size = 0;
+ for (uint i=0; i < m_lines; i++)
+ size += m_stringList[i]->dumpSize (haveHl);
+
+ QByteArray rawData (size);
+ char *buf = rawData.data();
+
+ // Dump textlines
+ for (uint i=0; i < m_lines; i++)
+ buf = m_stringList[i]->dump (buf, haveHl);
+
+ m_vmblock = KateFactory::self()->vm()->allocate(rawData.size());
+ m_vmblockSize = rawData.size();
+
+ if (!rawData.isEmpty())
+ {
+ if (!KateFactory::self()->vm()->copyBlock(m_vmblock, rawData.data(), 0, rawData.size()))
+ {
+ if (m_vmblock)
+ KateFactory::self()->vm()->free(m_vmblock);
+
+ m_vmblock = 0;
+ m_vmblockSize = 0;
+
+ m_parent->m_cacheWriteError = true;
+
+ return;
+ }
+ }
+ }
+
+ m_stringList.clear();
+
+ // we are now swapped out, set state + remove us out of the lists !
+ m_state = KateBufBlock::stateSwapped;
+ KateBufBlockList::remove (this);
+}
+
+//END KateBufBlock
+
+//BEGIN KateBufBlockList
+
+KateBufBlockList::KateBufBlockList ()
+ : m_count (0),
+ m_first (0),
+ m_last (0)
+{
+}
+
+void KateBufBlockList::append (KateBufBlock *buf)
+{
+ if (buf->list)
+ buf->list->removeInternal (buf);
+
+ m_count++;
+
+ // append a element
+ if (m_last)
+ {
+ m_last->listNext = buf;
+
+ buf->listPrev = m_last;
+ buf->listNext = 0;
+
+ m_last = buf;
+
+ buf->list = this;
+
+ return;
+ }
+
+ // insert the first element
+ m_last = buf;
+ m_first = buf;
+
+ buf->listPrev = 0;
+ buf->listNext = 0;
+
+ buf->list = this;
+}
+
+void KateBufBlockList::removeInternal (KateBufBlock *buf)
+{
+ if (buf->list != this)
+ return;
+
+ m_count--;
+
+ if ((buf == m_first) && (buf == m_last))
+ {
+ // last element removed !
+ m_first = 0;
+ m_last = 0;
+ }
+ else if (buf == m_first)
+ {
+ // first element removed
+ m_first = buf->listNext;
+ m_first->listPrev = 0;
+ }
+ else if (buf == m_last)
+ {
+ // last element removed
+ m_last = buf->listPrev;
+ m_last->listNext = 0;
+ }
+ else
+ {
+ buf->listPrev->listNext = buf->listNext;
+ buf->listNext->listPrev = buf->listPrev;
+ }
+
+ buf->listPrev = 0;
+ buf->listNext = 0;
+
+ buf->list = 0;
+}
+
+//END KateBufBlockList
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katebuffer.h b/kate/part/katebuffer.h
new file mode 100644
index 000000000..6b2d48ead
--- /dev/null
+++ b/kate/part/katebuffer.h
@@ -0,0 +1,709 @@
+/* This file is part of the KDE libraries
+ Copyright (c) 2000 Waldo Bastian <bastian@kde.org>
+ Copyright (C) 2002-2004 Christoph Cullmann <cullmann@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KATE_BUFFER_H__
+#define __KATE_BUFFER_H__
+
+#include "katetextline.h"
+#include "katecodefoldinghelpers.h"
+
+#include <kvmallocator.h>
+
+#include <qptrlist.h>
+#include <qobject.h>
+#include <qtimer.h>
+#include <qvaluevector.h>
+
+class KateLineInfo;
+class KateDocument;
+class KateHighlighting;
+class KateBufBlockList;
+class KateBuffer;
+class KateFileLoader;
+
+class QTextCodec;
+
+/**
+ * The KateBufBlock class contains an amount of data representing
+ * a certain number of lines.
+ *
+ * @author Waldo Bastian <bastian@kde.org>
+ * @author Christoph Cullmann <cullmann@kde.org>
+ */
+class KateBufBlock
+{
+ friend class KateBufBlockList;
+
+ public:
+ /**
+ * Create an empty block. (empty == ONE line)
+ * @param parent buffer the block belongs to
+ * @param prev previous bufblock in the list
+ * @param next next bufblock in the list
+ * @param stream stream to load the content from, if any given
+ */
+ KateBufBlock ( KateBuffer *parent, KateBufBlock *prev = 0, KateBufBlock *next = 0,
+ KateFileLoader *stream = 0 );
+
+ /**
+ * destroy this block and take care of freeing all mem
+ */
+ ~KateBufBlock ();
+
+ private:
+ /**
+ * fill the block with the lines from the given stream
+ * @param stream stream to load data from
+ */
+ void fillBlock (KateFileLoader *stream);
+
+ public:
+ /**
+ * state flags
+ */
+ enum State
+ {
+ stateSwapped = 0,
+ stateClean = 1,
+ stateDirty = 2
+ };
+
+ /**
+ * returns the current state of this block
+ * @return state
+ */
+ State state () const { return m_state; }
+
+ public:
+ /**
+ * return line @p i
+ * The first line of this block is line 0.
+ * if you modifiy this line, please mark the block as dirty
+ * @param i line to return
+ * @return line pointer
+ */
+ KateTextLine::Ptr line(uint i);
+
+ /**
+ * insert @p line in front of line @p i
+ * marks the block dirty
+ * @param i where to insert
+ * @param line line pointer
+ */
+ void insertLine(uint i, KateTextLine::Ptr line);
+
+ /**
+ * remove line @p i
+ * marks the block dirty
+ * @param i line to remove
+ */
+ void removeLine(uint i);
+
+ /**
+ * mark this block as dirty, will invalidate the swap data
+ * insert/removeLine will mark the block dirty itself
+ */
+ void markDirty ();
+
+ public:
+ /**
+ * startLine
+ * @return first line in block
+ */
+ inline uint startLine () const { return m_startLine; };
+
+ /**
+ * update the first line, needed to keep it up to date
+ * @param line new startLine
+ */
+ inline void setStartLine (uint line) { m_startLine = line; }
+
+ /**
+ * first line behind this block
+ * @return line behind block
+ */
+ inline uint endLine () const { return m_startLine + m_lines; }
+
+ /**
+ * lines in this block
+ * @return lines
+ */
+ inline uint lines () const { return m_lines; }
+
+ /**
+ * prev block
+ * @return previous block
+ */
+ inline KateBufBlock *prev () { return m_prev; }
+
+ /**
+ * next block
+ * @return next block
+ */
+ inline KateBufBlock *next () { return m_next; }
+
+ /**
+ * methodes to swap in/out
+ */
+ private:
+ /**
+ * swap in the kvmallocater data, create string list
+ */
+ void swapIn ();
+
+ /**
+ * swap our string list out, delete it !
+ */
+ void swapOut ();
+
+ private:
+ /**
+ * VERY IMPORTANT, state of this block
+ * this uchar indicates if the block is swapped, loaded, clean or dirty
+ */
+ KateBufBlock::State m_state;
+
+ /**
+ * IMPORTANT, start line
+ */
+ uint m_startLine;
+
+ /**
+ * IMPORTANT, line count
+ */
+ uint m_lines;
+
+ /**
+ * here we swap our stuff
+ */
+ KVMAllocator::Block *m_vmblock;
+
+ /**
+ * swapped size
+ */
+ uint m_vmblockSize;
+
+ /**
+ * list of textlines
+ */
+ QValueVector<KateTextLine::Ptr> m_stringList;
+
+ /**
+ * parent buffer.
+ */
+ KateBuffer* m_parent;
+
+ /**
+ * prev block
+ */
+ KateBufBlock *m_prev;
+
+ /**
+ * next block
+ */
+ KateBufBlock *m_next;
+
+ private:
+ /**
+ * list pointer, to which list I belong
+ * list element pointers for the KateBufBlockList ONLY !!!
+ */
+ KateBufBlockList *list;
+
+ /**
+ * prev list item
+ */
+ KateBufBlock *listPrev;
+
+ /**
+ * next list item
+ */
+ KateBufBlock *listNext;
+};
+
+/**
+ * list which allows O(1) inserts/removes
+ * will not delete the elements on remove
+ * will use the next/prevNode pointers in the KateBufBlocks !
+ * internal use: loaded/clean/dirty block lists
+ *
+ * @author Christoph Cullmann <cullmann@kde.org>
+ */
+class KateBufBlockList
+{
+ public:
+ /**
+ * Default Constructor
+ */
+ KateBufBlockList ();
+
+ public:
+ /**
+ * count of blocks in this list
+ * @return count of blocks
+ */
+ inline uint count() const { return m_count; }
+
+ /**
+ * first block in this list or 0
+ * @return head of list
+ */
+ inline KateBufBlock *first () { return m_first; };
+
+ /**
+ * last block in this list or 0
+ * @return end of list
+ */
+ inline KateBufBlock *last () { return m_last; };
+
+ /**
+ * is buf the last block?
+ * @param buf block to test
+ * @return is this block the first one?
+ */
+ inline bool isFirst (KateBufBlock *buf) { return m_first == buf; };
+
+ /**
+ * is buf the last block?
+ * @param buf block to test
+ * @return is this block the last one?
+ */
+ inline bool isLast (KateBufBlock *buf) { return m_last == buf; };
+
+ /**
+ * append a block to this list !
+ * will remove it from the list it belonged before !
+ * @param buf block to append
+ */
+ void append (KateBufBlock *buf);
+
+ /**
+ * remove the block from the list it belongs to !
+ * @param buf block to remove
+ */
+ inline static void remove (KateBufBlock *buf)
+ {
+ if (buf->list)
+ buf->list->removeInternal (buf);
+ }
+
+ private:
+ /**
+ * internal helper for remove
+ * @param buf block to remove
+ */
+ void removeInternal (KateBufBlock *buf);
+
+ private:
+ /**
+ * count of blocks in list
+ */
+ uint m_count;
+
+ /**
+ * first block
+ */
+ KateBufBlock *m_first;
+
+ /**
+ * last block
+ */
+ KateBufBlock *m_last;
+};
+
+/**
+ * The KateBuffer class maintains a collections of lines.
+ * It allows to maintain state information in a lazy way.
+ * It handles swapping out of data using secondary storage.
+ *
+ * It is designed to handle large amounts of text-data efficiently
+ * with respect to CPU and memory usage.
+ *
+ * @author Waldo Bastian <bastian@kde.org>
+ * @author Christoph Cullmann <cullmann@kde.org>
+ */
+class KateBuffer : public QObject
+{
+ Q_OBJECT
+
+ friend class KateBufBlock;
+
+ public:
+ /**
+ * maximal loaded block count
+ * @return max loaded blocks
+ */
+ inline static uint maxLoadedBlocks () { return m_maxLoadedBlocks; }
+
+ /**
+ * modifier for max loaded blocks limit
+ * @param count new limit
+ */
+ static void setMaxLoadedBlocks (uint count);
+
+ private:
+ /**
+ * global max loaded blocks limit
+ */
+ static uint m_maxLoadedBlocks;
+
+ public:
+ /**
+ * Create an empty buffer.
+ * @param doc parent document
+ */
+ KateBuffer (KateDocument *doc);
+
+ /**
+ * Goodbye buffer
+ */
+ ~KateBuffer ();
+
+ public:
+ /**
+ * start some editing action
+ */
+ void editStart ();
+
+ /**
+ * finish some editing action
+ */
+ void editEnd ();
+
+ /**
+ * were there changes in the current running
+ * editing session?
+ * @return changes done?
+ */
+ inline bool editChanged () const { return editChangesDone; }
+
+ /**
+ * dirty lines start
+ * @return start line
+ */
+ inline uint editTagStart () const { return editTagLineStart; }
+
+ /**
+ * dirty lines end
+ * @return end line
+ */
+ inline uint editTagEnd () const { return editTagLineEnd; }
+
+ /**
+ * line inserted/removed?
+ * @return line inserted/removed?
+ */
+ inline bool editTagFrom () const { return editTagLineFrom; }
+
+ private:
+ /**
+ * edit session recursion
+ */
+ uint editSessionNumber;
+
+ /**
+ * is a edit session running
+ */
+ bool editIsRunning;
+
+ /**
+ * dirty lines start at line
+ */
+ uint editTagLineStart;
+
+ /**
+ * dirty lines end at line
+ */
+ uint editTagLineEnd;
+
+ /**
+ * a line was inserted or removed
+ */
+ bool editTagLineFrom;
+
+ /**
+ * changes done?
+ */
+ bool editChangesDone;
+
+ public:
+ /**
+ * Clear the buffer.
+ */
+ void clear();
+
+ /**
+ * Open a file, use the given filename
+ * @param m_file filename to open
+ * @return success
+ */
+ bool openFile (const QString &m_file);
+
+ /**
+ * was the last loading broken because of not enough tmp disk space ?
+ * (will be reseted on successful save of the file, user gets warning if he really wants to do it)
+ * @return was loading borked?
+ */
+ bool loadingBorked () const { return m_loadingBorked; }
+
+ /**
+ * is this file a binary?
+ * @return binary file?
+ */
+ bool binary () const { return m_binary; }
+
+ /**
+ * Can the current codec handle all chars
+ * @return chars can be encoded
+ */
+ bool canEncode ();
+
+ /**
+ * Save the buffer to a file, use the given filename + codec + end of line chars (internal use of qtextstream)
+ * @param m_file filename to save to
+ * @return success
+ */
+ bool saveFile (const QString &m_file);
+
+ public:
+ /**
+ * Return line @p i
+ */
+ inline KateTextLine::Ptr line(uint i)
+ {
+ KateBufBlock *buf = findBlock(i);
+ if (!buf)
+ return 0;
+
+ if (i < m_lineHighlighted)
+ return buf->line (i - buf->startLine());
+
+ return line_internal (buf, i);
+ }
+
+ private:
+ /**
+ * line needs hl
+ */
+ KateTextLine::Ptr line_internal (KateBufBlock *buf, uint i);
+
+ inline void addIndentBasedFoldingInformation(QMemArray<uint> &foldingList,bool addindent,uint deindent);
+ inline void updatePreviousNotEmptyLine(KateBufBlock *blk,uint current_line,bool addindent,uint deindent);
+ public:
+ /**
+ * Return line @p i without triggering highlighting
+ */
+ inline KateTextLine::Ptr plainLine(uint i)
+ {
+ KateBufBlock *buf = findBlock(i);
+ if (!buf)
+ return 0;
+
+ return buf->line(i - buf->startLine());
+ }
+
+ /**
+ * Return the total number of lines in the buffer.
+ */
+ inline uint count() const { return m_lines; }
+
+ private:
+ /**
+ * Find the block containing line @p i
+ * index pointer gets filled with index of block in m_blocks
+ * index only valid if returned block != 0 !
+ */
+ KateBufBlock *findBlock (uint i, uint *index = 0)
+ {
+ // out of range !
+ if (i >= m_lines)
+ return 0;
+
+ if ((m_blocks[m_lastFoundBlock]->startLine() <= i) && (m_blocks[m_lastFoundBlock]->endLine() > i))
+ {
+ if (index)
+ (*index) = m_lastFoundBlock;
+
+ return m_blocks[m_lastFoundBlock];
+ }
+
+ return findBlock_internal (i, index);
+ }
+
+ KateBufBlock *findBlock_internal (uint i, uint *index = 0);
+
+ public:
+ /**
+ * Mark line @p i as changed !
+ */
+ void changeLine(uint i);
+
+ /**
+ * Insert @p line in front of line @p i
+ */
+ void insertLine(uint i, KateTextLine::Ptr line);
+
+ /**
+ * Remove line @p i
+ */
+ void removeLine(uint i);
+
+ public:
+ inline uint countVisible () { return m_lines - m_regionTree.getHiddenLinesCount(m_lines); }
+
+ inline uint lineNumber (uint visibleLine) { return m_regionTree.getRealLine (visibleLine); }
+
+ inline uint lineVisibleNumber (uint line) { return m_regionTree.getVirtualLine (line); }
+
+ inline void lineInfo (KateLineInfo *info, unsigned int line) { m_regionTree.getLineInfo(info,line); }
+
+ inline uint tabWidth () const { return m_tabWidth; }
+
+ public:
+ void setTabWidth (uint w);
+
+ /**
+ * Use @p highlight for highlighting
+ *
+ * @p highlight may be 0 in which case highlighting
+ * will be disabled.
+ */
+ void setHighlight (uint hlMode);
+
+ KateHighlighting *highlight () { return m_highlight; };
+
+ /**
+ * Invalidate highlighting of whole buffer.
+ */
+ void invalidateHighlighting();
+
+ KateCodeFoldingTree *foldingTree () { return &m_regionTree; };
+
+ public slots:
+ void codeFoldingColumnUpdate(unsigned int lineNr);
+
+ private:
+ /**
+ * Highlight information needs to be updated.
+ *
+ * @param buf The buffer being processed.
+ * @param startState highlighting state of last line before range
+ * @param from first line in range
+ * @param to last line in range
+ * @param invalidat should the rehighlighted lines be tagged ?
+ *
+ * @returns true when the highlighting in the next block needs to be updated,
+ * false otherwise.
+ */
+ bool doHighlight (KateBufBlock *buf, uint from, uint to, bool invalidate);
+
+ signals:
+ /**
+ * Emittend if codefolding returned with a changed list
+ */
+ void codeFoldingUpdated();
+
+ /**
+ * Emitted when the highlighting of a certain range has
+ * changed.
+ */
+ void tagLines(int start, int end);
+
+ private:
+ /**
+ * document we belong too
+ */
+ KateDocument *m_doc;
+
+ /**
+ * current line count
+ */
+ uint m_lines;
+
+ /**
+ * ALL blocks
+ * in order of linenumbers
+ */
+ QValueVector<KateBufBlock*> m_blocks;
+
+ /**
+ * last block where the start/end line is in sync with real life
+ */
+ uint m_lastInSyncBlock;
+
+ /**
+ * last block found by findBlock, there to make searching faster
+ */
+ uint m_lastFoundBlock;
+
+ /**
+ * status of the cache read/write errors
+ * write errors get handled, read errors not really atm
+ */
+ bool m_cacheReadError;
+ bool m_cacheWriteError;
+
+ /**
+ * had we cache error while loading ?
+ */
+ bool m_loadingBorked;
+
+ /**
+ * binary file loaded ?
+ */
+ bool m_binary;
+
+ /**
+ * highlighting & folding relevant stuff
+ */
+ private:
+ /**
+ * current highlighting mode or 0
+ */
+ KateHighlighting *m_highlight;
+
+ /**
+ * folding tree
+ */
+ KateCodeFoldingTree m_regionTree;
+
+ // for the scrapty indent sensitive langs
+ uint m_tabWidth;
+
+ uint m_lineHighlightedMax;
+ uint m_lineHighlighted;
+
+ /**
+ * number of dynamic contexts causing a full invalidation
+ */
+ uint m_maxDynamicContexts;
+
+ /**
+ * only used from the KateBufBlocks !
+ */
+ private:
+ /**
+ * all not swapped blocks !
+ */
+ KateBufBlockList m_loadedBlocks;
+};
+
+#endif
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katecmds.cpp b/kate/part/katecmds.cpp
new file mode 100644
index 000000000..17846dd7d
--- /dev/null
+++ b/kate/part/katecmds.cpp
@@ -0,0 +1,605 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2003 - 2005 Anders Lund <anders@alweb.dk>
+ Copyright (C) 2001-2004 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2001 Charles Samuels <charles@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "katecmds.h"
+
+#include "katedocument.h"
+#include "kateview.h"
+#include "kateconfig.h"
+#include "kateautoindent.h"
+#include "katetextline.h"
+#include "katefactory.h"
+#include "katejscript.h"
+#include "katerenderer.h"
+
+#include "../interfaces/katecmd.h"
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kurl.h>
+#include <kshellcompletion.h>
+
+#include <qregexp.h>
+
+
+//BEGIN CoreCommands
+// syncs a config flag in the document with a boolean value
+static void setDocFlag( KateDocumentConfig::ConfigFlags flag, bool enable,
+ KateDocument *doc )
+{
+ doc->config()->setConfigFlags( flag, enable );
+}
+
+// this returns wheather the string s could be converted to
+// a bool value, one of on|off|1|0|true|false. the argument val is
+// set to the extracted value in case of success
+static bool getBoolArg( QString s, bool *val )
+{
+ bool res( false );
+ s = s.lower();
+ res = (s == "on" || s == "1" || s == "true");
+ if ( res )
+ {
+ *val = true;
+ return true;
+ }
+ res = (s == "off" || s == "0" || s == "false");
+ if ( res )
+ {
+ *val = false;
+ return true;
+ }
+ return false;
+}
+
+QStringList KateCommands::CoreCommands::cmds()
+{
+ QStringList l;
+ l << "indent" << "unindent" << "cleanindent"
+ << "comment" << "uncomment" << "goto" << "kill-line"
+ << "set-tab-width" << "set-replace-tabs" << "set-show-tabs"
+ << "set-remove-trailing-space"
+ << "set-indent-spaces" << "set-indent-width" << "set-mixed-indent"
+ << "set-indent-mode" << "set-auto-indent"
+ << "set-line-numbers" << "set-folding-markers" << "set-icon-border"
+ << "set-wrap-cursor"
+ << "set-word-wrap" << "set-word-wrap-column"
+ << "set-replace-tabs-save" << "set-remove-trailing-space-save"
+ << "set-highlight" << "run-myself" << "set-show-indent";
+ return l;
+}
+
+bool KateCommands::CoreCommands::exec(Kate::View *view,
+ const QString &_cmd,
+ QString &errorMsg)
+{
+#define KCC_ERR(s) { errorMsg=s; return false; }
+ // cast it hardcore, we know that it is really a kateview :)
+ KateView *v = (KateView*) view;
+
+ if ( ! v )
+ KCC_ERR( i18n("Could not access view") );
+
+ //create a list of args
+ QStringList args( QStringList::split( QRegExp("\\s+"), _cmd ) );
+ QString cmd ( args.first() );
+ args.remove( args.first() );
+
+ // ALL commands that takes no arguments.
+ if ( cmd == "indent" )
+ {
+ v->indent();
+ return true;
+ }
+ else if ( cmd == "run-myself" )
+ {
+#ifndef Q_WS_WIN //todo
+ return KateFactory::self()->jscript()->execute(v, v->doc()->text(), errorMsg);
+#else
+ return 0;
+#endif
+ }
+ else if ( cmd == "unindent" )
+ {
+ v->unIndent();
+ return true;
+ }
+ else if ( cmd == "cleanindent" )
+ {
+ v->cleanIndent();
+ return true;
+ }
+ else if ( cmd == "comment" )
+ {
+ v->comment();
+ return true;
+ }
+ else if ( cmd == "uncomment" )
+ {
+ v->uncomment();
+ return true;
+ }
+ else if ( cmd == "kill-line" )
+ {
+ v->killLine();
+ return true;
+ }
+ else if ( cmd == "set-indent-mode" )
+ {
+ bool ok(false);
+ int val ( args.first().toInt( &ok ) );
+ if ( ok )
+ {
+ if ( val < 0 )
+ KCC_ERR( i18n("Mode must be at least 0.") );
+ v->doc()->config()->setIndentationMode( val );
+ }
+ else
+ v->doc()->config()->setIndentationMode( KateAutoIndent::modeNumber( args.first() ) );
+ return true;
+ }
+ else if ( cmd == "set-highlight" )
+ {
+ QString val = _cmd.section( ' ', 1 ).lower();
+ for ( uint i=0; i < v->doc()->hlModeCount(); i++ )
+ {
+ if ( v->doc()->hlModeName( i ).lower() == val )
+ {
+ v->doc()->setHlMode( i );
+ return true;
+ }
+ }
+ KCC_ERR( i18n("No such highlight '%1'").arg( args.first() ) );
+ }
+
+ // ALL commands that takes exactly one integer argument.
+ else if ( cmd == "set-tab-width" ||
+ cmd == "set-indent-width" ||
+ cmd == "set-word-wrap-column" ||
+ cmd == "goto" )
+ {
+ // find a integer value > 0
+ if ( ! args.count() )
+ KCC_ERR( i18n("Missing argument. Usage: %1 <value>").arg( cmd ) );
+ bool ok;
+ int val ( args.first().toInt( &ok ) );
+ if ( !ok )
+ KCC_ERR( i18n("Failed to convert argument '%1' to integer.")
+ .arg( args.first() ) );
+
+ if ( cmd == "set-tab-width" )
+ {
+ if ( val < 1 )
+ KCC_ERR( i18n("Width must be at least 1.") );
+ v->setTabWidth( val );
+ }
+ else if ( cmd == "set-indent-width" )
+ {
+ if ( val < 1 )
+ KCC_ERR( i18n("Width must be at least 1.") );
+ v->doc()->config()->setIndentationWidth( val );
+ }
+ else if ( cmd == "set-word-wrap-column" )
+ {
+ if ( val < 2 )
+ KCC_ERR( i18n("Column must be at least 1.") );
+ v->doc()->setWordWrapAt( val );
+ }
+ else if ( cmd == "goto" )
+ {
+ if ( val < 1 )
+ KCC_ERR( i18n("Line must be at least 1") );
+ if ( (uint)val > v->doc()->numLines() )
+ KCC_ERR( i18n("There is not that many lines in this document") );
+ v->gotoLineNumber( val - 1 );
+ }
+ return true;
+ }
+
+ // ALL commands that takes 1 boolean argument.
+ else if ( cmd == "set-icon-border" ||
+ cmd == "set-folding-markers" ||
+ cmd == "set-line-numbers" ||
+ cmd == "set-replace-tabs" ||
+ cmd == "set-remove-trailing-space" ||
+ cmd == "set-show-tabs" ||
+ cmd == "set-indent-spaces" ||
+ cmd == "set-mixed-indent" ||
+ cmd == "set-word-wrap" ||
+ cmd == "set-wrap-cursor" ||
+ cmd == "set-replace-tabs-save" ||
+ cmd == "set-remove-trailing-space-save" ||
+ cmd == "set-show-indent" )
+ {
+ if ( ! args.count() )
+ KCC_ERR( i18n("Usage: %1 on|off|1|0|true|false").arg( cmd ) );
+ bool enable;
+ if ( getBoolArg( args.first(), &enable ) )
+ {
+ if ( cmd == "set-icon-border" )
+ v->setIconBorder( enable );
+ else if (cmd == "set-folding-markers")
+ v->setFoldingMarkersOn( enable );
+ else if ( cmd == "set-line-numbers" )
+ v->setLineNumbersOn( enable );
+ else if ( cmd == "set-show-indent" )
+ v->renderer()->setShowIndentLines( enable );
+ else if ( cmd == "set-replace-tabs" )
+ setDocFlag( KateDocumentConfig::cfReplaceTabsDyn, enable, v->doc() );
+ else if ( cmd == "set-remove-trailing-space" )
+ setDocFlag( KateDocumentConfig::cfRemoveTrailingDyn, enable, v->doc() );
+ else if ( cmd == "set-show-tabs" )
+ setDocFlag( KateDocumentConfig::cfShowTabs, enable, v->doc() );
+ else if ( cmd == "set-indent-spaces" )
+ setDocFlag( KateDocumentConfig::cfSpaceIndent, enable, v->doc() );
+ else if ( cmd == "set-mixed-indent" )
+ {
+ // this is special, in that everything is set up -- space-indent is enabled,
+ // and a indent-width is set if it is 0 (to tabwidth/2)
+ setDocFlag( KateDocumentConfig::cfMixedIndent, enable, v->doc() );
+ if ( enable )
+ {
+ setDocFlag( KateDocumentConfig::cfSpaceIndent, enable, v->doc() );
+ if ( ! v->doc()->config()->indentationWidth() )
+ v->doc()->config()->setIndentationWidth( v->tabWidth()/2 );
+ }
+ }
+ else if ( cmd == "set-word-wrap" )
+ v->doc()->setWordWrap( enable );
+ else if ( cmd == "set-remove-trailing-space-save" )
+ setDocFlag( KateDocumentConfig::cfRemoveSpaces, enable, v->doc() );
+ else if ( cmd == "set-wrap-cursor" )
+ setDocFlag( KateDocumentConfig::cfWrapCursor, enable, v->doc() );
+
+ return true;
+ }
+ else
+ KCC_ERR( i18n("Bad argument '%1'. Usage: %2 on|off|1|0|true|false")
+ .arg( args.first() ).arg( cmd ) );
+ }
+
+ // unlikely..
+ KCC_ERR( i18n("Unknown command '%1'").arg(cmd) );
+}
+
+KCompletion *KateCommands::CoreCommands::completionObject( const QString &cmd, Kate::View *view )
+{
+ if ( cmd == "set-highlight" )
+ {
+ KateView *v = (KateView*)view;
+ QStringList l;
+ for ( uint i = 0; i < v->doc()->hlModeCount(); i++ )
+ l << v->doc()->hlModeName( i );
+
+ KateCmdShellCompletion *co = new KateCmdShellCompletion();
+ co->setItems( l );
+ co->setIgnoreCase( true );
+ return co;
+ }
+ return 0L;
+}
+//END CoreCommands
+
+//BEGIN SedReplace
+static void replace(QString &s, const QString &needle, const QString &with)
+{
+ int pos=0;
+ while (1)
+ {
+ pos=s.find(needle, pos);
+ if (pos==-1) break;
+ s.replace(pos, needle.length(), with);
+ pos+=with.length();
+ }
+
+}
+
+static int backslashString(const QString &haystack, const QString &needle, int index)
+{
+ int len=haystack.length();
+ int searchlen=needle.length();
+ bool evenCount=true;
+ while (index<len)
+ {
+ if (haystack[index]=='\\')
+ {
+ evenCount=!evenCount;
+ }
+ else
+ { // isn't a slash
+ if (!evenCount)
+ {
+ if (haystack.mid(index, searchlen)==needle)
+ return index-1;
+ }
+ evenCount=true;
+ }
+ index++;
+
+ }
+
+ return -1;
+}
+
+// exchange "\t" for the actual tab character, for example
+static void exchangeAbbrevs(QString &str)
+{
+ // the format is (findreplace)*[nullzero]
+ const char *magic="a\x07t\tn\n";
+
+ while (*magic)
+ {
+ int index=0;
+ char replace=magic[1];
+ while ((index=backslashString(str, QChar(*magic), index))!=-1)
+ {
+ str.replace(index, 2, QChar(replace));
+ index++;
+ }
+ magic++;
+ magic++;
+ }
+}
+
+int KateCommands::SedReplace::sedMagic( KateDocument *doc, int &line,
+ const QString &find, const QString &repOld, const QString &delim,
+ bool noCase, bool repeat,
+ uint startcol, int endcol )
+{
+ KateTextLine *ln = doc->kateTextLine( line );
+ if ( ! ln || ! ln->length() ) return 0;
+
+ // HANDLING "\n"s in PATTERN
+ // * Create a list of patterns, splitting PATTERN on (unescaped) "\n"
+ // * insert $s and ^s to match line ends/beginnings
+ // * When matching patterhs after the first one, replace \N with the captured
+ // text.
+ // * If all patterns in the list match sequentiel lines, there is a match, so
+ // * remove line/start to line + patterns.count()-1/patterns.last.length
+ // * handle capatures by putting them in one list.
+ // * the existing insertion is fine, including the line calculation.
+
+ QStringList patterns = QStringList::split( QRegExp("(^\\\\n|(?![^\\\\])\\\\n)"), find, true );
+
+ if ( patterns.count() > 1 )
+ {
+ for ( uint i = 0; i < patterns.count(); i++ )
+ {
+ if ( i < patterns.count() - 1 )
+ patterns[i].append("$");
+ if ( i )
+ patterns[i].prepend("^");
+
+ kdDebug(13025)<<"patterns["<<i<<"] ="<<patterns[i]<<endl;
+ }
+ }
+
+ QRegExp matcher(patterns[0], noCase);
+
+ uint len;
+ int matches = 0;
+
+ while ( ln->searchText( startcol, matcher, &startcol, &len ) )
+ {
+
+ if ( endcol >= 0 && startcol + len > (uint)endcol )
+ break;
+
+ matches++;
+
+
+ QString rep=repOld;
+
+ // now set the backreferences in the replacement
+ QStringList backrefs=matcher.capturedTexts();
+ int refnum=1;
+
+ QStringList::Iterator i = backrefs.begin();
+ ++i;
+
+ for (; i!=backrefs.end(); ++i)
+ {
+ // I need to match "\\" or "", but not "\"
+ QString number=QString::number(refnum);
+
+ int index=0;
+ while (index!=-1)
+ {
+ index=backslashString(rep, number, index);
+ if (index>=0)
+ {
+ rep.replace(index, 2, *i);
+ index+=(*i).length();
+ }
+ }
+
+ refnum++;
+ }
+
+ replace(rep, "\\\\", "\\");
+ replace(rep, "\\" + delim, delim);
+
+ doc->removeText( line, startcol, line, startcol + len );
+ doc->insertText( line, startcol, rep );
+
+ // TODO if replace contains \n,
+ // change the line number and
+ // check for text that needs be searched behind the last inserted newline.
+ int lns = rep.contains('\n');
+ if ( lns )
+ {
+ line += lns;
+
+ if ( doc->lineLength( line ) > 0 && ( endcol < 0 || (uint)endcol >= startcol + len ) )
+ {
+ // if ( endcol >= startcol + len )
+ endcol -= (startcol + len);
+ uint sc = rep.length() - rep.findRev('\n') - 1;
+ matches += sedMagic( doc, line, find, repOld, delim, noCase, repeat, sc, endcol );
+ }
+ }
+
+ if (!repeat) break;
+ startcol+=rep.length();
+
+ // sanity check -- avoid infinite loops eg with %s,.*,,g ;)
+ uint ll = ln->length();
+ if ( ! ll || startcol > ll )
+ break;
+ }
+
+ return matches;
+}
+
+bool KateCommands::SedReplace::exec (Kate::View *view, const QString &cmd, QString &msg)
+{
+ kdDebug(13025)<<"SedReplace::execCmd( "<<cmd<<" )"<<endl;
+
+ QRegExp delim("^[$%]?s\\s*([^\\w\\s])");
+ if ( delim.search( cmd ) < 0 ) return false;
+
+ bool fullFile=cmd[0]=='%';
+ bool noCase=cmd[cmd.length()-1]=='i' || cmd[cmd.length()-2]=='i';
+ bool repeat=cmd[cmd.length()-1]=='g' || cmd[cmd.length()-2]=='g';
+ bool onlySelect=cmd[0]=='$';
+
+ QString d = delim.cap(1);
+ kdDebug(13025)<<"SedReplace: delimiter is '"<<d<<"'"<<endl;
+
+ QRegExp splitter( QString("^[$%]?s\\s*") + d + "((?:[^\\\\\\" + d + "]|\\\\.)*)\\" + d +"((?:[^\\\\\\" + d + "]|\\\\.)*)\\" + d + "[ig]{0,2}$" );
+ if (splitter.search(cmd)<0) return false;
+
+ QString find=splitter.cap(1);
+ kdDebug(13025)<< "SedReplace: find=" << find.latin1() <<endl;
+
+ QString replace=splitter.cap(2);
+ exchangeAbbrevs(replace);
+ kdDebug(13025)<< "SedReplace: replace=" << replace.latin1() <<endl;
+
+ if ( find.contains("\\n") )
+ {
+ msg = i18n("Sorry, but Kate is not able to replace newlines, yet");
+ return false;
+ }
+
+ KateDocument *doc = ((KateView*)view)->doc();
+ if ( ! doc ) return false;
+
+ doc->editStart();
+
+ int res = 0;
+
+ if (fullFile)
+ {
+ uint numLines=doc->numLines();
+ for (int line=0; (uint)line < numLines; line++)
+ {
+ res += sedMagic( doc, line, find, replace, d, !noCase, repeat );
+ if ( ! repeat && res ) break;
+ }
+ }
+ else if (onlySelect)
+ {
+ int startline = doc->selStartLine();
+ uint startcol = doc->selStartCol();
+ int endcol = -1;
+ do {
+ if ( startline == doc->selEndLine() )
+ endcol = doc->selEndCol();
+
+ res += sedMagic( doc, startline, find, replace, d, !noCase, repeat, startcol, endcol );
+
+ /*if ( startcol )*/ startcol = 0;
+
+ startline++;
+ } while ( (int)startline <= doc->selEndLine() );
+ }
+ else // just this line
+ {
+ int line=view->cursorLine();
+ res += sedMagic(doc, line, find, replace, d, !noCase, repeat);
+ }
+
+ msg = i18n("1 replacement done", "%n replacements done",res );
+
+ doc->editEnd();
+
+ return true;
+}
+//END SedReplace
+
+//BEGIN Character
+bool KateCommands::Character::exec (Kate::View *view, const QString &_cmd, QString &)
+{
+ QString cmd = _cmd;
+
+ // hex, octal, base 9+1
+ QRegExp num("^char *(0?x[0-9A-Fa-f]{1,4}|0[0-7]{1,6}|[0-9]{1,3})$");
+ if (num.search(cmd)==-1) return false;
+
+ cmd=num.cap(1);
+
+ // identify the base
+
+ unsigned short int number=0;
+ int base=10;
+ if (cmd[0]=='x' || cmd.left(2)=="0x")
+ {
+ cmd.replace(QRegExp("^0?x"), "");
+ base=16;
+ }
+ else if (cmd[0]=='0')
+ base=8;
+ bool ok;
+ number=cmd.toUShort(&ok, base);
+ if (!ok || number==0) return false;
+ if (number<=255)
+ {
+ char buf[2];
+ buf[0]=(char)number;
+ buf[1]=0;
+ view->insertText(QString(buf));
+ }
+ else
+ { // do the unicode thing
+ QChar c(number);
+ view->insertText(QString(&c, 1));
+ }
+
+ return true;
+}
+//END Character
+
+//BEGIN Date
+bool KateCommands::Date::exec (Kate::View *view, const QString &cmd, QString &)
+{
+ if (cmd.left(4) != "date")
+ return false;
+
+ if (QDateTime::currentDateTime().toString(cmd.mid(5, cmd.length()-5)).length() > 0)
+ view->insertText(QDateTime::currentDateTime().toString(cmd.mid(5, cmd.length()-5)));
+ else
+ view->insertText(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"));
+
+ return true;
+}
+//END Date
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katecmds.h b/kate/part/katecmds.h
new file mode 100644
index 000000000..84f7919d8
--- /dev/null
+++ b/kate/part/katecmds.h
@@ -0,0 +1,178 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2003 Anders Lund <anders@alweb.dk>
+ Copyright (C) 2001-2004 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2001 Charles Samuels <charles@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KATE_CMDS_H__
+#define __KATE_CMDS_H__
+
+#include "../interfaces/document.h"
+#include "../interfaces/view.h"
+
+class KateDocument;
+class KCompletion;
+
+namespace KateCommands
+{
+
+/**
+ * This Kate::Command provides access to a lot of the core functionality
+ * of kate part, settings, utilities, navigation etc.
+ * it needs to get a kateview pointer, it will cast the kate::view pointer
+ * hard to kateview
+ */
+class CoreCommands : public Kate::Command, public Kate::CommandExtension
+{
+ public:
+ /**
+ * execute command
+ * @param view view to use for execution
+ * @param cmd cmd string
+ * @param errorMsg error to return if no success
+ * @return success
+ */
+ bool exec( class Kate::View *view, const QString &cmd, QString &errorMsg );
+
+ bool help( class Kate::View *, const QString &, QString & ) {return false;};
+
+ /**
+ * supported commands as prefixes
+ * @return prefix list
+ */
+ QStringList cmds();
+
+ /**
+ * override completionObject from interfaces/document.h .
+ */
+ KCompletion *completionObject( const QString &cmd, Kate::View *view );
+};
+
+/**
+ * -- Charles Samuels <charles@kde.org>
+ * Support vim/sed find and replace
+ * s/search/replace/ find search, replace with replace on this line
+ * %s/search/replace/ do the same to the whole file
+ * s/search/replace/i do the S. and R., but case insensitively
+ * $s/search/replace/ do the search are replacement to the selection only
+ *
+ * $s/// is currently unsupported
+ **/
+class SedReplace : public Kate::Command
+{
+ public:
+ /**
+ * execute command
+ * @param view view to use for execution
+ * @param cmd cmd string
+ * @param errorMsg error to return if no success
+ * @return success
+ */
+ bool exec (class Kate::View *view, const QString &cmd, QString &errorMsg);
+
+ bool help (class Kate::View *, const QString &, QString &) { return false; };
+
+ /**
+ * supported commands as prefixes
+ * @return prefix list
+ */
+ QStringList cmds () { QStringList l("s"); l << "%s" << "$s"; return l; };
+
+ private:
+ /**
+ * Searches one line and does the replacement in the document.
+ * If @p replace contains any newline characters, the reamaining part of the
+ * line is searched, and the @p line set to the last line number searched.
+ * @return the number of replacements performed.
+ * @param doc a pointer to the document to work on
+ * @param line the number of the line to search. This may be changed by the
+ * function, if newlines are inserted.
+ * @param find A regular expression pattern to use for searching
+ * @param replace a template for replacement. Backspaced integers are
+ * replaced with captured texts from the regular expression.
+ * @param delim the delimiter character from the command. In the replacement
+ * text backsplashes preceeding this character are removed.
+ * @param nocase parameter for matching the reqular expression.
+ * @param repeat If false, the search is stopped after the first match.
+ * @param startcol The position in the line to start the search.
+ * @param endcol The last collumn in the line allowed in a match.
+ * If it is -1, the whole line is used.
+ */
+ static int sedMagic(KateDocument *doc, int &line,
+ const QString &find, const QString &replace, const QString &delim,
+ bool noCase, bool repeat,
+ uint startcol=0, int endcol=-1);
+};
+
+/**
+ * insert a unicode or ascii character
+ * base 9+1: 1234
+ * hex: 0x1234 or x1234
+ * octal: 01231
+ *
+ * prefixed with "char:"
+ **/
+class Character : public Kate::Command
+{
+ public:
+ /**
+ * execute command
+ * @param view view to use for execution
+ * @param cmd cmd string
+ * @param errorMsg error to return if no success
+ * @return success
+ */
+ bool exec (class Kate::View *view, const QString &cmd, QString &errorMsg);
+
+ bool help (class Kate::View *, const QString &, QString &) { return false; };
+
+ /**
+ * supported commands as prefixes
+ * @return prefix list
+ */
+ QStringList cmds () { return QStringList("char"); };
+};
+
+/**
+ * insert the current date/time in the given format
+ */
+class Date : public Kate::Command
+{
+ public:
+ /**
+ * execute command
+ * @param view view to use for execution
+ * @param cmd cmd string
+ * @param errorMsg error to return if no success
+ * @return success
+ */
+ bool exec (class Kate::View *view, const QString &cmd, QString &errorMsg);
+
+ bool help (class Kate::View *, const QString &, QString &) { return false; };
+
+ /**
+ * supported commands as prefixes
+ * @return prefix list
+ */
+ QStringList cmds () { return QStringList("date"); };
+};
+
+
+} // namespace KateCommands
+#endif
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katecodecompletion.cpp b/kate/part/katecodecompletion.cpp
new file mode 100644
index 000000000..bbc34dfca
--- /dev/null
+++ b/kate/part/katecodecompletion.cpp
@@ -0,0 +1,566 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
+ Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org>
+ Copyright (C) 2001 by Victor Röder <Victor_Roeder@GMX.de>
+ Copyright (C) 2002 by Roberto Raggi <roberto@kdevelop.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+/******** Partly based on the ArgHintWidget of Qt3 by Trolltech AS *********/
+/* Trolltech doesn't mind, if we license that piece of code as LGPL, because there isn't much
+ * left from the desigener code */
+
+#include "katecodecompletion.h"
+#include "katecodecompletion.moc"
+
+#include "katedocument.h"
+#include "kateview.h"
+#include "katerenderer.h"
+#include "kateconfig.h"
+#include "katefont.h"
+
+#include <kdebug.h>
+
+#include <qwhatsthis.h>
+#include <qvbox.h>
+#include <qlistbox.h>
+#include <qtimer.h>
+#include <qtooltip.h>
+#include <qapplication.h>
+#include <qsizegrip.h>
+#include <qfontmetrics.h>
+#include <qlayout.h>
+#include <qregexp.h>
+
+/**
+ * This class is used as the codecompletion listbox. It can be resized according to its contents,
+ * therfor the needed size is provided by sizeHint();
+ *@short Listbox showing codecompletion
+ *@author Jonas B. Jacobi <j.jacobi@gmx.de>
+ */
+class KateCCListBox : public QListBox
+{
+ public:
+ /**
+ @short Create a new CCListBox
+ */
+ KateCCListBox (QWidget* parent = 0, const char* name = 0, WFlags f = 0):QListBox(parent, name, f)
+ {
+ }
+
+ QSize sizeHint() const
+ {
+ int count = this->count();
+ int height = 20;
+ int tmpwidth = 8;
+ //FIXME the height is for some reasons at least 3 items heigh, even if there is only one item in the list
+ if (count > 0)
+ if(count < 11)
+ height = count * itemHeight(0);
+ else {
+ height = 10 * itemHeight(0);
+ tmpwidth += verticalScrollBar()->width();
+ }
+
+ int maxcount = 0, tmpcount = 0;
+ for (int i = 0; i < count; ++i)
+ if ( (tmpcount = fontMetrics().width(text(i)) ) > maxcount)
+ maxcount = tmpcount;
+
+ if (maxcount > QApplication::desktop()->width()){
+ tmpwidth = QApplication::desktop()->width() - 5;
+ height += horizontalScrollBar()->height();
+ } else
+ tmpwidth += maxcount;
+ return QSize(tmpwidth,height);
+
+ }
+};
+
+class KateCompletionItem : public QListBoxText
+{
+ public:
+ KateCompletionItem( QListBox* lb, KTextEditor::CompletionEntry entry )
+ : QListBoxText( lb )
+ , m_entry( entry )
+ {
+ if( entry.postfix == "()" ) { // should be configurable
+ setText( entry.prefix + " " + entry.text + entry.postfix );
+ } else {
+ setText( entry.prefix + " " + entry.text + " " + entry.postfix);
+ }
+ }
+
+ KTextEditor::CompletionEntry m_entry;
+};
+
+
+KateCodeCompletion::KateCodeCompletion( KateView* view )
+ : QObject( view, "Kate Code Completion" )
+ , m_view( view )
+ , m_commentLabel( 0 )
+{
+ m_completionPopup = new QVBox( 0, 0, WType_Popup );
+ m_completionPopup->setFrameStyle( QFrame::Box | QFrame::Plain );
+ m_completionPopup->setLineWidth( 1 );
+
+ m_completionListBox = new KateCCListBox( m_completionPopup );
+ m_completionListBox->setFrameStyle( QFrame::NoFrame );
+ //m_completionListBox->setCornerWidget( new QSizeGrip( m_completionListBox) );
+ m_completionListBox->setFocusProxy( m_view->m_viewInternal );
+
+ m_completionListBox->installEventFilter( this );
+
+ m_completionPopup->resize(m_completionListBox->sizeHint() + QSize(2,2));
+ m_completionPopup->installEventFilter( this );
+ m_completionPopup->setFocusProxy( m_view->m_viewInternal );
+
+ m_pArgHint = new KateArgHint( m_view );
+ connect( m_pArgHint, SIGNAL(argHintHidden()),
+ this, SIGNAL(argHintHidden()) );
+
+ connect( m_view, SIGNAL(cursorPositionChanged()),
+ this, SLOT(slotCursorPosChanged()) );
+}
+
+KateCodeCompletion::~KateCodeCompletion()
+{
+ delete m_completionPopup;
+}
+
+bool KateCodeCompletion::codeCompletionVisible () {
+ return m_completionPopup->isVisible();
+}
+
+void KateCodeCompletion::showCompletionBox(
+ QValueList<KTextEditor::CompletionEntry> complList, int offset, bool casesensitive )
+{
+ kdDebug(13035) << "showCompletionBox " << endl;
+
+ if ( codeCompletionVisible() ) return;
+
+ m_caseSensitive = casesensitive;
+ m_complList = complList;
+ m_offset = offset;
+ m_view->cursorPositionReal( &m_lineCursor, &m_colCursor );
+ m_colCursor -= offset;
+
+ updateBox( true );
+}
+
+bool KateCodeCompletion::eventFilter( QObject *o, QEvent *e )
+{
+ if ( o != m_completionPopup &&
+ o != m_completionListBox &&
+ o != m_completionListBox->viewport() )
+ return false;
+
+ if( e->type() == QEvent::Hide )
+ {
+ //don't use abortCompletion() as aborting here again will send abort signal
+ //even on successfull completion we will emit completionAborted() twice...
+ m_completionPopup->hide();
+ delete m_commentLabel;
+ m_commentLabel = 0;
+ return false;
+ }
+
+
+ if ( e->type() == QEvent::MouseButtonDblClick ) {
+ doComplete();
+ return false;
+ }
+
+ if ( e->type() == QEvent::MouseButtonPress ) {
+ QTimer::singleShot(0, this, SLOT(showComment()));
+ return false;
+ }
+
+ return false;
+}
+
+void KateCodeCompletion::handleKey (QKeyEvent *e)
+{
+ // close completion if you move out of range
+ if ((e->key() == Key_Up) && (m_completionListBox->currentItem() == 0))
+ {
+ abortCompletion();
+ m_view->setFocus();
+ return;
+ }
+
+ // keyboard movement
+ if( (e->key() == Key_Up) || (e->key() == Key_Down ) ||
+ (e->key() == Key_Home ) || (e->key() == Key_End) ||
+ (e->key() == Key_Prior) || (e->key() == Key_Next ))
+ {
+ QTimer::singleShot(0,this,SLOT(showComment()));
+ QApplication::sendEvent( m_completionListBox, (QEvent*)e );
+ return;
+ }
+
+ // update the box
+ updateBox();
+}
+
+void KateCodeCompletion::doComplete()
+{
+ KateCompletionItem* item = static_cast<KateCompletionItem*>(
+ m_completionListBox->item(m_completionListBox->currentItem()));
+
+ if( item == 0 )
+ return;
+
+ QString text = item->m_entry.text;
+ QString currentLine = m_view->currentTextLine();
+ int len = m_view->cursorColumnReal() - m_colCursor;
+ QString currentComplText = currentLine.mid(m_colCursor,len);
+ QString add = text.mid(currentComplText.length());
+ if( item->m_entry.postfix == "()" )
+ add += "(";
+
+ emit filterInsertString(&(item->m_entry),&add);
+ m_view->insertText(add);
+
+ complete( item->m_entry );
+ m_view->setFocus();
+}
+
+void KateCodeCompletion::abortCompletion()
+{
+ m_completionPopup->hide();
+ delete m_commentLabel;
+ m_commentLabel = 0;
+ emit completionAborted();
+}
+
+void KateCodeCompletion::complete( KTextEditor::CompletionEntry entry )
+{
+ m_completionPopup->hide();
+ delete m_commentLabel;
+ m_commentLabel = 0;
+ emit completionDone( entry );
+ emit completionDone();
+}
+
+void KateCodeCompletion::updateBox( bool )
+{
+ if( m_colCursor > m_view->cursorColumnReal() ) {
+ // the cursor is too far left
+ kdDebug(13035) << "Aborting Codecompletion after sendEvent" << endl;
+ kdDebug(13035) << m_view->cursorColumnReal() << endl;
+ abortCompletion();
+ m_view->setFocus();
+ return;
+ }
+
+ m_completionListBox->clear();
+
+ QString currentLine = m_view->currentTextLine();
+ int len = m_view->cursorColumnReal() - m_colCursor;
+ QString currentComplText = currentLine.mid(m_colCursor,len);
+/* No-one really badly wants those, or?
+ kdDebug(13035) << "Column: " << m_colCursor << endl;
+ kdDebug(13035) << "Line: " << currentLine << endl;
+ kdDebug(13035) << "CurrentColumn: " << m_view->cursorColumnReal() << endl;
+ kdDebug(13035) << "Len: " << len << endl;
+ kdDebug(13035) << "Text: '" << currentComplText << "'" << endl;
+ kdDebug(13035) << "Count: " << m_complList.count() << endl;
+*/
+ QValueList<KTextEditor::CompletionEntry>::Iterator it;
+ if( m_caseSensitive ) {
+ for( it = m_complList.begin(); it != m_complList.end(); ++it ) {
+ if( (*it).text.startsWith(currentComplText) ) {
+ new KateCompletionItem(m_completionListBox,*it);
+ }
+ }
+ } else {
+ currentComplText = currentComplText.upper();
+ for( it = m_complList.begin(); it != m_complList.end(); ++it ) {
+ if( (*it).text.upper().startsWith(currentComplText) ) {
+ new KateCompletionItem(m_completionListBox,*it);
+ }
+ }
+ }
+
+ if( m_completionListBox->count() == 0 ||
+ ( m_completionListBox->count() == 1 && // abort if we equaled the last item
+ currentComplText == m_completionListBox->text(0).stripWhiteSpace() ) ) {
+ abortCompletion();
+ m_view->setFocus();
+ return;
+ }
+
+ kdDebug(13035)<<"KateCodeCompletion::updateBox: Resizing widget"<<endl;
+ m_completionPopup->resize(m_completionListBox->sizeHint() + QSize(2,2));
+ QPoint p = m_view->mapToGlobal( m_view->cursorCoordinates() );
+ int x = p.x();
+ int y = p.y() ;
+ if ( y + m_completionPopup->height() + m_view->renderer()->config()->fontMetrics( )->height() > QApplication::desktop()->height() )
+ y -= (m_completionPopup->height() );
+ else
+ y += m_view->renderer()->config()->fontMetrics( )->height();
+
+ if (x + m_completionPopup->width() > QApplication::desktop()->width())
+ x = QApplication::desktop()->width() - m_completionPopup->width();
+
+ m_completionPopup->move( QPoint(x,y) );
+
+ m_completionListBox->setCurrentItem( 0 );
+ m_completionListBox->setSelected( 0, true );
+ m_completionListBox->setFocus();
+ m_completionPopup->show();
+
+ QTimer::singleShot(0,this,SLOT(showComment()));
+}
+
+void KateCodeCompletion::showArgHint ( QStringList functionList, const QString& strWrapping, const QString& strDelimiter )
+{
+ unsigned int line, col;
+ m_view->cursorPositionReal( &line, &col );
+ m_pArgHint->reset( line, col );
+ m_pArgHint->setArgMarkInfos( strWrapping, strDelimiter );
+
+ int nNum = 0;
+ QStringList::Iterator end(functionList.end());
+ for( QStringList::Iterator it = functionList.begin(); it != end; ++it )
+ {
+ kdDebug(13035) << "Insert function text: " << *it << endl;
+
+ m_pArgHint->addFunction( nNum, ( *it ) );
+
+ nNum++;
+ }
+
+ m_pArgHint->move(m_view->mapToGlobal(m_view->cursorCoordinates() + QPoint(0,m_view->renderer()->config()->fontMetrics( )->height())) );
+ m_pArgHint->show();
+}
+
+void KateCodeCompletion::slotCursorPosChanged()
+{
+ m_pArgHint->cursorPositionChanged ( m_view, m_view->cursorLine(), m_view->cursorColumnReal() );
+}
+
+void KateCodeCompletion::showComment()
+{
+ if (!m_completionPopup->isVisible())
+ return;
+
+ KateCompletionItem* item = static_cast<KateCompletionItem*>(m_completionListBox->item(m_completionListBox->currentItem()));
+
+ if( !item )
+ return;
+
+ if( item->m_entry.comment.isEmpty() )
+ return;
+
+ delete m_commentLabel;
+ m_commentLabel = new KateCodeCompletionCommentLabel( 0, item->m_entry.comment );
+ m_commentLabel->setFont(QToolTip::font());
+ m_commentLabel->setPalette(QToolTip::palette());
+
+ QPoint rightPoint = m_completionPopup->mapToGlobal(QPoint(m_completionPopup->width(),0));
+ QPoint leftPoint = m_completionPopup->mapToGlobal(QPoint(0,0));
+ QRect screen = QApplication::desktop()->screenGeometry ( m_commentLabel );
+ QPoint finalPoint;
+ if (rightPoint.x()+m_commentLabel->width() > screen.x() + screen.width())
+ finalPoint.setX(leftPoint.x()-m_commentLabel->width());
+ else
+ finalPoint.setX(rightPoint.x());
+
+ m_completionListBox->ensureCurrentVisible();
+
+ finalPoint.setY(
+ m_completionListBox->viewport()->mapToGlobal(m_completionListBox->itemRect(
+ m_completionListBox->item(m_completionListBox->currentItem())).topLeft()).y());
+
+ m_commentLabel->move(finalPoint);
+ m_commentLabel->show();
+}
+
+KateArgHint::KateArgHint( KateView* parent, const char* name )
+ : QFrame( parent, name, WType_Popup )
+{
+ setBackgroundColor( black );
+ setPaletteForegroundColor( Qt::black );
+
+ labelDict.setAutoDelete( true );
+ layout = new QVBoxLayout( this, 1, 2 );
+ layout->setAutoAdd( true );
+ editorView = parent;
+
+ m_markCurrentFunction = true;
+
+ setFocusPolicy( StrongFocus );
+ setFocusProxy( parent );
+
+ reset( -1, -1 );
+}
+
+KateArgHint::~KateArgHint()
+{
+}
+
+void KateArgHint::setArgMarkInfos( const QString& wrapping, const QString& delimiter )
+{
+ m_wrapping = wrapping;
+ m_delimiter = delimiter;
+ m_markCurrentFunction = true;
+}
+
+void KateArgHint::reset( int line, int col )
+{
+ m_functionMap.clear();
+ m_currentFunction = -1;
+ labelDict.clear();
+
+ m_currentLine = line;
+ m_currentCol = col - 1;
+}
+
+void KateArgHint::slotDone(bool completed)
+{
+ hide();
+
+ m_currentLine = m_currentCol = -1;
+
+ emit argHintHidden();
+ if (completed)
+ emit argHintCompleted();
+ else
+ emit argHintAborted();
+}
+
+void KateArgHint::cursorPositionChanged( KateView* view, int line, int col )
+{
+ if( m_currentCol == -1 || m_currentLine == -1 ){
+ slotDone(false);
+ return;
+ }
+
+ int nCountDelimiter = 0;
+ int count = 0;
+
+ QString currentTextLine = view->doc()->textLine( line );
+ QString text = currentTextLine.mid( m_currentCol, col - m_currentCol );
+ QRegExp strconst_rx( "\"[^\"]*\"" );
+ QRegExp chrconst_rx( "'[^']*'" );
+
+ text = text
+ .replace( strconst_rx, "\"\"" )
+ .replace( chrconst_rx, "''" );
+
+ int index = 0;
+ while( index < (int)text.length() ){
+ if( text[index] == m_wrapping[0] ){
+ ++count;
+ } else if( text[index] == m_wrapping[1] ){
+ --count;
+ } else if( count > 0 && text[index] == m_delimiter[0] ){
+ ++nCountDelimiter;
+ }
+ ++index;
+ }
+
+ if( (m_currentLine > 0 && m_currentLine != line) || (m_currentLine < col) || (count == 0) ){
+ slotDone(count == 0);
+ return;
+ }
+
+ // setCurArg ( nCountDelimiter + 1 );
+
+}
+
+void KateArgHint::addFunction( int id, const QString& prot )
+{
+ m_functionMap[ id ] = prot;
+ QLabel* label = new QLabel( prot.stripWhiteSpace().simplifyWhiteSpace(), this );
+ label->setBackgroundColor( QColor(255, 255, 238) );
+ label->show();
+ labelDict.insert( id, label );
+
+ if( m_currentFunction < 0 )
+ setCurrentFunction( id );
+}
+
+void KateArgHint::setCurrentFunction( int currentFunction )
+{
+ if( m_currentFunction != currentFunction ){
+
+ if( currentFunction < 0 )
+ currentFunction = (int)m_functionMap.size() - 1;
+
+ if( currentFunction > (int)m_functionMap.size()-1 )
+ currentFunction = 0;
+
+ if( m_markCurrentFunction && m_currentFunction >= 0 ){
+ QLabel* label = labelDict[ m_currentFunction ];
+ label->setFont( font() );
+ }
+
+ m_currentFunction = currentFunction;
+
+ if( m_markCurrentFunction ){
+ QLabel* label = labelDict[ currentFunction ];
+ QFont fnt( font() );
+ fnt.setBold( true );
+ label->setFont( fnt );
+ }
+
+ adjustSize();
+ }
+}
+
+void KateArgHint::show()
+{
+ QFrame::show();
+ adjustSize();
+}
+
+bool KateArgHint::eventFilter( QObject*, QEvent* e )
+{
+ if( isVisible() && e->type() == QEvent::KeyPress ){
+ QKeyEvent* ke = static_cast<QKeyEvent*>( e );
+ if( (ke->state() & ControlButton) && ke->key() == Key_Left ){
+ setCurrentFunction( currentFunction() - 1 );
+ ke->accept();
+ return true;
+ } else if( ke->key() == Key_Escape ){
+ slotDone(false);
+ return false;
+ } else if( (ke->state() & ControlButton) && ke->key() == Key_Right ){
+ setCurrentFunction( currentFunction() + 1 );
+ ke->accept();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void KateArgHint::adjustSize( )
+{
+ QRect screen = QApplication::desktop()->screenGeometry( pos() );
+
+ QFrame::adjustSize();
+ if( width() > screen.width() )
+ resize( screen.width(), height() );
+
+ if( x() + width() > screen.x() + screen.width() )
+ move( screen.x() + screen.width() - width(), y() );
+}
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katecodecompletion.h b/kate/part/katecodecompletion.h
new file mode 100644
index 000000000..81279d929
--- /dev/null
+++ b/kate/part/katecodecompletion.h
@@ -0,0 +1,164 @@
+/* This file is part of the KDE libraries
+
+ Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
+ Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org>
+ Copyright (C) 2001 by Victor Röder <Victor_Roeder@GMX.de>
+ Copyright (C) 2002 by Roberto Raggi <roberto@kdevelop.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+/******** Partly based on the ArgHintWidget of Qt3 by Trolltech AS *********/
+/* Trolltech doesn't mind, if we license that piece of code as LGPL, because there isn't much
+ * left from the desigener code */
+
+
+#ifndef __KateCodeCompletion_H__
+#define __KateCodeCompletion_H__
+
+#include <ktexteditor/codecompletioninterface.h>
+
+#include <qvaluelist.h>
+#include <qstringlist.h>
+#include <qlabel.h>
+#include <qframe.h>
+#include <qmap.h>
+#include <qintdict.h>
+
+class KateView;
+class KateArgHint;
+class KateCCListBox;
+
+class QLayout;
+class QVBox;
+
+class KateCodeCompletionCommentLabel : public QLabel
+{
+ Q_OBJECT
+
+ public:
+ KateCodeCompletionCommentLabel( QWidget* parent, const QString& text) : QLabel( parent, "toolTipTip",
+ WStyle_StaysOnTop | WStyle_Customize | WStyle_NoBorder | WStyle_Tool | WX11BypassWM )
+ {
+ setMargin(1);
+ setIndent(0);
+ setAutoMask( false );
+ setFrameStyle( QFrame::Plain | QFrame::Box );
+ setLineWidth( 1 );
+ setAlignment( AlignAuto | AlignTop );
+ polish();
+ setText(text);
+ adjustSize();
+ }
+};
+
+class KateCodeCompletion : public QObject
+{
+ Q_OBJECT
+
+ friend class KateViewInternal;
+
+ public:
+ KateCodeCompletion(KateView *view);
+ ~KateCodeCompletion();
+
+ bool codeCompletionVisible ();
+
+ void showArgHint(
+ QStringList functionList, const QString& strWrapping, const QString& strDelimiter );
+ void showCompletionBox(
+ QValueList<KTextEditor::CompletionEntry> entries, int offset = 0, bool casesensitive = true );
+ bool eventFilter( QObject* o, QEvent* e );
+
+ void handleKey (QKeyEvent *e);
+
+ public slots:
+ void slotCursorPosChanged();
+ void showComment();
+ void updateBox () { updateBox(false); }
+
+ signals:
+ void completionAborted();
+ void completionDone();
+ void argHintHidden();
+ void completionDone(KTextEditor::CompletionEntry);
+ void filterInsertString(KTextEditor::CompletionEntry*,QString *);
+
+ private:
+ void doComplete();
+ void abortCompletion();
+ void complete( KTextEditor::CompletionEntry );
+ void updateBox( bool newCoordinate );
+
+ KateArgHint* m_pArgHint;
+ KateView* m_view;
+ QVBox* m_completionPopup;
+ KateCCListBox* m_completionListBox;
+ QValueList<KTextEditor::CompletionEntry> m_complList;
+ uint m_lineCursor;
+ uint m_colCursor;
+ int m_offset;
+ bool m_caseSensitive;
+ KateCodeCompletionCommentLabel* m_commentLabel;
+};
+
+class KateArgHint: public QFrame
+{
+ Q_OBJECT
+
+ public:
+ KateArgHint( KateView* =0, const char* =0 );
+ virtual ~KateArgHint();
+
+ virtual void setCurrentFunction( int );
+ virtual int currentFunction() const { return m_currentFunction; }
+
+ void setArgMarkInfos( const QString&, const QString& );
+
+ virtual void addFunction( int, const QString& );
+ QString functionAt( int id ) const { return m_functionMap[ id ]; }
+
+ virtual void show();
+ virtual void adjustSize();
+ virtual bool eventFilter( QObject*, QEvent* );
+
+ signals:
+ void argHintHidden();
+ void argHintCompleted();
+ void argHintAborted();
+
+ public slots:
+ virtual void reset( int, int );
+ virtual void cursorPositionChanged( KateView*, int, int );
+
+ private slots:
+ void slotDone(bool completed);
+
+ private:
+ QMap<int, QString> m_functionMap;
+ int m_currentFunction;
+ QString m_wrapping;
+ QString m_delimiter;
+ bool m_markCurrentFunction;
+ int m_currentLine;
+ int m_currentCol;
+ KateView* editorView;
+ QIntDict<QLabel> labelDict;
+ QLayout* layout;
+};
+
+#endif
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katecodefoldinghelpers.cpp b/kate/part/katecodefoldinghelpers.cpp
new file mode 100644
index 000000000..49090820b
--- /dev/null
+++ b/kate/part/katecodefoldinghelpers.cpp
@@ -0,0 +1,1662 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2002 Joseph Wenninger <jowenn@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "katecodefoldinghelpers.h"
+#include "katecodefoldinghelpers.moc"
+
+#include "katebuffer.h"
+#include "katecursor.h"
+#include <kdebug.h>
+
+#include <qstring.h>
+
+#define JW_DEBUG 0
+
+bool KateCodeFoldingTree::trueVal = true;
+
+KateCodeFoldingNode::KateCodeFoldingNode() :
+ parentNode(0),
+ startLineRel(0),
+ endLineRel(0),
+ startCol(0),
+ endCol(0),
+ startLineValid(false),
+ endLineValid(false),
+ type(0),
+ visible(true),
+ deleteOpening(false),
+ deleteEnding(false)
+{
+}//the endline fields should be initialised to not valid
+
+KateCodeFoldingNode::KateCodeFoldingNode(KateCodeFoldingNode *par, signed char typ, unsigned int sLRel):
+ parentNode(par),
+ startLineRel(sLRel),
+ endLineRel(10000),
+ startCol(0),
+ endCol(0),
+ startLineValid(true),
+ endLineValid(false),
+ type(typ),
+ visible(true),
+ deleteOpening(false),
+ deleteEnding(false)
+{
+}//the endline fields should be initialised to not valid
+
+KateCodeFoldingNode::~KateCodeFoldingNode()
+{
+ // delete all child nodes
+ clearChildren ();
+}
+
+bool KateCodeFoldingNode::getBegin(KateCodeFoldingTree *tree, KateTextCursor* begin) {
+ if (!startLineValid) return false;
+ unsigned int line=startLineRel;
+ for (KateCodeFoldingNode *n=parentNode;n;n=n->parentNode)
+ line+=n->startLineRel;
+
+ tree->m_buffer->codeFoldingColumnUpdate(line);
+ begin->setLine(line);
+ begin->setCol(startCol);
+
+ return true;
+}
+
+bool KateCodeFoldingNode::getEnd(KateCodeFoldingTree *tree, KateTextCursor *end) {
+ if (!endLineValid) return false;
+ unsigned int line=startLineRel+endLineRel;
+ for (KateCodeFoldingNode *n=parentNode;n;n=n->parentNode)
+ line+=n->startLineRel;
+
+ tree->m_buffer->codeFoldingColumnUpdate(line);
+ end->setLine(line);
+ end->setCol(endCol);
+
+ return true;
+}
+
+int KateCodeFoldingNode::cmpPos(KateCodeFoldingTree *tree, uint line,uint col) {
+ KateTextCursor cur(line,col);
+ KateTextCursor start,end;
+ kdDebug(13000)<<"KateCodeFoldingNode::cmpPos (1)"<<endl;
+ bool startValid=getBegin(tree, &start);
+ kdDebug(13000)<<"KateCodeFoldingNode::cmpPos (2)"<<endl;
+ bool endValid=getEnd(tree, &end);
+ kdDebug(13000)<<"KateCodeFoldingNode::cmpPos (3)"<<endl;
+ if ((!endValid) && startValid) {
+ return ((start>cur)?-1:0);
+ }
+ if ((!startValid) && endValid) {
+ return ((cur>end)?1:0);
+ }
+ //here both have to be valid, both invalid must not happen
+ Q_ASSERT(startValid && endValid);
+ return ( (cur<start)?(-1):( (cur>end) ? 1:0));
+}
+
+void KateCodeFoldingNode::insertChild (uint index, KateCodeFoldingNode *node)
+{
+ uint s = m_children.size ();
+
+ if (index > s)
+ return;
+
+ m_children.resize (++s);
+
+ for (uint i=s-1; i > index; --i)
+ m_children[i] = m_children[i-1];
+
+ m_children[index] = node;
+}
+
+KateCodeFoldingNode *KateCodeFoldingNode::takeChild (uint index)
+{
+ uint s = m_children.size ();
+
+ if (index >= s)
+ return 0;
+
+ KateCodeFoldingNode *n = m_children[index];
+
+ for (uint i=index; (i+1) < s; ++i)
+ m_children[i] = m_children[i+1];
+
+ m_children.resize (s-1);
+
+ return n;
+}
+
+void KateCodeFoldingNode::clearChildren ()
+{
+ for (uint i=0; i < m_children.size(); ++i)
+ delete m_children[i];
+
+ m_children.resize (0);
+}
+
+KateCodeFoldingTree::KateCodeFoldingTree(KateBuffer *buffer): QObject(buffer), m_buffer (buffer)
+{
+ clear();
+}
+
+void KateCodeFoldingTree::fixRoot(int endLRel)
+{
+ m_root.endLineRel = endLRel;
+}
+
+void KateCodeFoldingTree::clear()
+{
+ m_root.clearChildren();
+
+ // initialize the root "special" node
+ m_root.startLineValid=true;
+ m_root.endLineValid=true; // temporary, should be false;
+ m_root.endLineRel=1; // temporary;
+
+ hiddenLinesCountCacheValid=false;
+ lineMapping.setAutoDelete(true);
+ hiddenLines.clear();
+ lineMapping.clear();
+ nodesForLine.clear();
+ markedForDeleting.clear();
+ dontIgnoreUnchangedLines.clear();
+}
+
+KateCodeFoldingTree::~KateCodeFoldingTree()
+{
+}
+
+bool KateCodeFoldingTree::isTopLevel(unsigned int line)
+{
+ if (m_root.noChildren())
+ return true; // no childs
+
+ // look if a given lines belongs to a sub node
+ for ( uint i=0; i < m_root.childCount(); ++i )
+ {
+ KateCodeFoldingNode *node = m_root.child(i);
+
+ if ((node->startLineRel<=line) && (line<=node->startLineRel+node->endLineRel))
+ return false; // the line is within the range of a subnode -> return toplevel=false
+ }
+
+ return true; // the root node is the only node containing the given line, return toplevel=true
+}
+
+void KateCodeFoldingTree::getLineInfo(KateLineInfo *info, unsigned int line)
+{
+ // Initialze the returned structure, this will also be returned if the root node has no child nodes
+ // or the line is not within a childnode's range.
+ info->topLevel = true;
+ info->startsVisibleBlock = false;
+ info->startsInVisibleBlock = false;
+ info->endsBlock = false;
+ info->invalidBlockEnd = false;
+
+ if (m_root.noChildren())
+ return;
+
+ //let's look for some information
+ for ( uint i=0; i < m_root.childCount(); ++i )
+ {
+ KateCodeFoldingNode *node = m_root.child(i);
+
+ if ((node->startLineRel<=line) && (line<=node->startLineRel+node->endLineRel)) // we found a node, which contains the given line -> do a complete lookup
+ {
+ info->topLevel = false; //we are definitly not toplevel
+ findAllNodesOpenedOrClosedAt(line); //lookup all nodes, which start or and at the given line
+
+ for ( KateCodeFoldingNode *node = nodesForLine.first(); node; node = nodesForLine.next() )
+ {
+ uint startLine = getStartLine(node);
+
+ // type<0 means, that a region has been closed, but not opened
+ // eg. parantheses missmatch
+ if (node->type < 0)
+ info->invalidBlockEnd=true;
+ else
+ {
+ if (startLine != line) // does the region we look at not start at the given line
+ info->endsBlock = true; // than it has to be an ending
+ else
+ {
+ // The line starts a new region, now determine, if it's a visible or a hidden region
+ if (node->visible)
+ info->startsVisibleBlock=true;
+ else
+ info->startsInVisibleBlock=true;
+ }
+ }
+ }
+
+ return;
+ }
+ }
+
+ return;
+}
+
+
+KateCodeFoldingNode *KateCodeFoldingTree::findNodeForLine(unsigned int line)
+{
+ if (m_root.noChildren()) // does we have child list + nodes ?
+ return &m_root;
+
+ // lets look, if given line is within a subnode range, and then return the deepest one.
+ for ( uint i=0; i < m_root.childCount(); ++i )
+ {
+ KateCodeFoldingNode *node = m_root.child(i);
+
+ if ((node->startLineRel<=line) && (line<=node->startLineRel+node->endLineRel))
+ {
+ // a region surounds the line, look in the next deeper hierarchy step
+ return findNodeForLineDescending(node,line,0);
+ }
+ }
+
+ return &m_root;
+}
+
+
+KateCodeFoldingNode *KateCodeFoldingTree::findNodeForLineDescending ( KateCodeFoldingNode *node,
+ unsigned int line, unsigned int offset, bool oneStepOnly )
+{
+ if (node->noChildren())
+ return node;
+
+ // calculate the offset, between a subnodes real start line and its relative start
+ offset += node->startLineRel;
+
+ for ( uint i=0; i < node->childCount(); ++i )
+ {
+ KateCodeFoldingNode *subNode = node->child(i);
+
+ if ((subNode->startLineRel+offset<=line) && (line<=subNode->endLineRel+subNode->startLineRel+offset)) //warning fix me for invalid ends
+ {
+ // a subnode contains the line.
+ // if oneStepOnly is true, we don't want to search for the deepest node, just return the found one
+
+ if (oneStepOnly)
+ return subNode;
+ else
+ return findNodeForLineDescending (subNode,line,offset); // look into the next deeper hierarchy step
+ }
+ }
+
+ return node; // the current node has no sub nodes, or the line couldn'te be found within a subregion
+}
+
+KateCodeFoldingNode *KateCodeFoldingTree::findNodeForPosition(unsigned int line, unsigned int column)
+{
+ KateCodeFoldingNode *node=findNodeForLine(line);
+
+ if (node==&m_root) return &m_root;
+
+ kdDebug(13000)<<"initial cmpPos"<<endl;
+
+ KateCodeFoldingNode *tmp;
+ int leq=node->cmpPos(this, line,column);
+ while (true) {
+ switch (leq) {
+ case 0: {
+ if (node->noChildren())
+ return node;
+ else
+ {
+ tmp=node;
+ for ( uint i=0; i < node->childCount(); ++i )
+ {
+ KateCodeFoldingNode *subNode = node->child(i);
+ kdDebug(13000)<<"cmdPos(case0):calling"<<endl;
+ leq=subNode->cmpPos(this, line,column);
+ kdDebug(13000)<<"cmdPos(case0):returned"<<endl;
+ if (leq==0) {
+ tmp=subNode;
+ break;
+ } else if (leq==-1) break;
+ }
+ if (tmp!=node) node=tmp; else return node;
+ }
+ break;
+ }
+ //this could be optimized a littlebit
+ case -1:
+ case 1: {
+ if (!(node->parentNode)) return &m_root;
+ kdDebug(13000)<<"current node type"<<node->type<<endl;
+ node=node->parentNode;
+ kdDebug(13000)<<"cmdPos(case-1/1):calling:"<<node<<endl;
+ leq=node->cmpPos(this, line,column);
+ kdDebug(13000)<<"cmdPos(case-1/1):returned"<<endl;
+ break;
+ }
+ }
+
+ }
+ Q_ASSERT(false);
+ return &m_root;
+}
+
+void KateCodeFoldingTree::debugDump()
+{
+ //dump all nodes for debugging
+ kdDebug(13000)<<"The parsed region/block tree for code folding"<<endl;
+ dumpNode(&m_root, "");
+}
+
+void KateCodeFoldingTree::dumpNode(KateCodeFoldingNode *node, const QString &prefix)
+{
+ //output node properties
+ kdDebug(13000)<<prefix<<QString("Type: %1, startLineValid %2, startLineRel %3, endLineValid %4, endLineRel %5, visible %6").
+ arg(node->type).arg(node->startLineValid).arg(node->startLineRel).arg(node->endLineValid).
+ arg(node->endLineRel).arg(node->visible)<<endl;
+
+ //output child node properties recursive
+ if (node->noChildren())
+ return;
+
+ QString newprefix(prefix + " ");
+ for ( uint i=0; i < node->childCount(); ++i )
+ dumpNode (node->child(i),newprefix);
+}
+
+/*
+ That's one of the most important functions ;)
+*/
+void KateCodeFoldingTree::updateLine(unsigned int line,
+ QMemArray<uint> *regionChanges, bool *updated,bool changed,bool colsChanged)
+{
+ if ( (!changed) || colsChanged)
+ {
+ if (dontIgnoreUnchangedLines.isEmpty())
+ return;
+
+ if (dontIgnoreUnchangedLines[line])
+ dontIgnoreUnchangedLines.remove(line);
+ else
+ return;
+ }
+
+ something_changed = false;
+
+ findAndMarkAllNodesforRemovalOpenedOrClosedAt(line);
+
+ if (regionChanges->isEmpty())
+ {
+ // KateCodeFoldingNode *node=findNodeForLine(line);
+ // if (node->type!=0)
+ // if (getStartLine(node)+node->endLineRel==line) removeEnding(node,line);
+ }
+ else
+ {
+ for (unsigned int i=0;i<regionChanges->size() / 4;i++)
+ {
+ signed char tmp=(*regionChanges)[regionChanges->size()-2-i*2];
+ uint tmppos=(*regionChanges)[regionChanges->size()-1-i*2];
+ (*regionChanges)[regionChanges->size()-2-i*2]=(*regionChanges)[i*2];
+ (*regionChanges)[regionChanges->size()-1-i*2]=(*regionChanges)[i*2+1];
+ (*regionChanges)[i*2]=tmp;
+ (*regionChanges)[i*2+1]=tmppos;
+ }
+
+
+ signed char data= (*regionChanges)[regionChanges->size()-2];
+ uint charPos=(*regionChanges)[regionChanges->size()-1];
+ regionChanges->resize (regionChanges->size()-2);
+
+ int insertPos=-1;
+ KateCodeFoldingNode *node = findNodeForLine(line);
+
+ if (data<0)
+ {
+ // if (insertPos==-1)
+ {
+ unsigned int tmpLine=line-getStartLine(node);
+
+ for ( uint i=0; i < node->childCount(); ++i )
+ {
+ if (node->child(i)->startLineRel >= tmpLine)
+ {
+ insertPos=i;
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ for (; (node->parentNode) && (getStartLine(node->parentNode)==line) && (node->parentNode->type!=0); node=node->parentNode);
+
+ if ((getStartLine(node)==line) && (node->type!=0))
+ {
+ insertPos=node->parentNode->findChild(node);
+ node = node->parentNode;
+ }
+ else
+ {
+ for ( uint i=0; i < node->childCount(); ++i )
+ {
+ if (getStartLine(node->child(i))>=line)
+ {
+ insertPos=i;
+ break;
+ }
+ }
+ }
+ }
+
+ do
+ {
+ if (data<0)
+ {
+ if (correctEndings(data,node,line,charPos,insertPos))
+ {
+ insertPos=node->parentNode->findChild(node)+1;
+ node=node->parentNode;
+ }
+ else
+ {
+ if (insertPos!=-1) insertPos++;
+ }
+ }
+ else
+ {
+ int startLine=getStartLine(node);
+ if ((insertPos==-1) || (insertPos>=(int)node->childCount()))
+ {
+ KateCodeFoldingNode *newNode = new KateCodeFoldingNode (node,data,line-startLine);
+ something_changed = true;
+ node->appendChild(newNode);
+ addOpening(newNode, data, regionChanges, line,charPos);
+ insertPos = node->findChild(newNode)+1;
+ }
+ else
+ {
+ if (node->child(insertPos)->startLineRel == line-startLine)
+ {
+ addOpening(node->child(insertPos), data, regionChanges, line,charPos);
+ insertPos++;
+ }
+ else
+ {
+// kdDebug(13000)<<"ADDING NODE "<<endl;
+ KateCodeFoldingNode *newNode = new KateCodeFoldingNode (node,data,line-startLine);
+ something_changed = true;
+ node->insertChild(insertPos, newNode);
+ addOpening(newNode, data, regionChanges, line,charPos);
+ insertPos++;
+ }
+ }
+ }
+
+ if (regionChanges->isEmpty())
+ data = 0;
+ else
+ {
+ data = (*regionChanges)[regionChanges->size()-2];
+ charPos=(*regionChanges)[regionChanges->size()-1];
+ regionChanges->resize (regionChanges->size()-2);
+ }
+ } while (data!=0);
+ }
+
+ cleanupUnneededNodes(line);
+// if (something_changed) emit regionBeginEndAddedRemoved(line);
+ (*updated) = something_changed;
+}
+
+
+bool KateCodeFoldingTree::removeOpening(KateCodeFoldingNode *node,unsigned int line)
+{
+ signed char type;
+ if ((type=node->type) == 0)
+ {
+ dontDeleteOpening(node);
+ dontDeleteEnding(node);
+ return false;
+ }
+
+ if (!node->visible)
+ {
+ toggleRegionVisibility(getStartLine(node));
+ }
+
+ KateCodeFoldingNode *parent = node->parentNode;
+ int mypos = parent->findChild(node);
+
+ if (mypos > -1)
+ {
+ //move childnodes() up
+ for(; node->childCount()>0 ;)
+ {
+ KateCodeFoldingNode *tmp;
+ parent->insertChild(mypos, tmp=node->takeChild(0));
+ tmp->parentNode = parent;
+ tmp->startLineRel += node->startLineRel;
+ mypos++;
+ }
+
+ // remove the node
+ //mypos = parent->findChild(node);
+ bool endLineValid = node->endLineValid;
+ int endLineRel = node->endLineRel;
+ uint endCol=node->endCol;
+
+ // removes + deletes
+ KateCodeFoldingNode *child = parent->takeChild(mypos);
+ markedForDeleting.removeRef(child);
+ delete child;
+
+ if ((type>0) && (endLineValid))
+ correctEndings(-type, parent, line+endLineRel/*+1*/,endCol, mypos); // why the hell did I add a +1 here ?
+ }
+
+ return true;
+}
+
+bool KateCodeFoldingTree::removeEnding(KateCodeFoldingNode *node,unsigned int /* line */)
+{
+ KateCodeFoldingNode *parent = node->parentNode;
+
+ if (!parent)
+ return false;
+
+ if (node->type == 0)
+ return false;
+
+ if (node->type < 0)
+ {
+ // removes + deletes
+ int i = parent->findChild (node);
+ if (i >= 0)
+ {
+ KateCodeFoldingNode *child = parent->takeChild(i);
+ markedForDeleting.removeRef(child);
+ delete child;
+ }
+
+ return true;
+ }
+
+ int mypos = parent->findChild(node);
+ int count = parent->childCount();
+
+ for (int i=mypos+1; i<count; i++)
+ {
+ if (parent->child(i)->type == -node->type)
+ {
+ node->endLineValid = true;
+ node->endLineRel = parent->child(i)->startLineRel - node->startLineRel;
+
+ KateCodeFoldingNode *child = parent->takeChild(i);
+ markedForDeleting.removeRef(child);
+ delete child;
+
+ count = i-mypos-1;
+ if (count > 0)
+ {
+ for (int i=0; i<count; i++)
+ {
+ KateCodeFoldingNode *tmp = parent->takeChild(mypos+1);
+ tmp->startLineRel -= node->startLineRel;
+ tmp->parentNode = node; //should help 16.04.2002
+ node->appendChild(tmp);
+ }
+ }
+ return false;
+ }
+ }
+
+ if ( (parent->type == node->type) || /*temporary fix */ (!parent->parentNode))
+ {
+ for (int i=mypos+1; i<(int)parent->childCount(); i++)
+ {
+ KateCodeFoldingNode *tmp = parent->takeChild(mypos+1);
+ tmp->startLineRel -= node->startLineRel;
+ tmp->parentNode = node; // SHOULD HELP 16.04.2002
+ node->appendChild(tmp);
+ }
+
+ // this should fix the bug of wrongly closed nodes
+ if (!parent->parentNode)
+ node->endLineValid=false;
+ else
+ node->endLineValid = parent->endLineValid;
+
+ node->endLineRel = parent->endLineRel-node->startLineRel;
+
+ if (node->endLineValid)
+ return removeEnding(parent, getStartLine(parent)+parent->endLineRel);
+
+ return false;
+ }
+
+ node->endLineValid = false;
+ node->endLineRel = parent->endLineRel - node->startLineRel;
+
+ return false;
+}
+
+
+bool KateCodeFoldingTree::correctEndings(signed char data, KateCodeFoldingNode *node,unsigned int line,unsigned int endCol,int insertPos)
+{
+// if (node->type==0) {kdError()<<"correct Ending should never be called with the root node"<<endl; return true;}
+ uint startLine = getStartLine(node);
+ if (data != -node->type)
+ {
+#if JW_DEBUG
+ kdDebug(13000)<<"data!=-node->type (correctEndings)"<<endl;
+#endif
+ //invalid close -> add to unopend list
+ dontDeleteEnding(node);
+ if (data == node->type) {
+ node->endCol=endCol;
+ return false;
+ }
+ KateCodeFoldingNode *newNode = new KateCodeFoldingNode (node,data,line-startLine);
+ something_changed = true;
+ newNode->startLineValid = false;
+ newNode->endLineValid = true;
+ newNode->endLineRel = 0;
+ newNode->endCol=endCol;
+
+ if ((insertPos==-1) || (insertPos==(int)node->childCount()))
+ node->appendChild(newNode);
+ else
+ node->insertChild(insertPos,newNode);
+
+ // find correct position
+ return false;
+ }
+ else
+ {
+ something_changed = true;
+ dontDeleteEnding(node);
+
+ // valid closing region
+ if (!node->endLineValid)
+ {
+ node->endLineValid = true;
+ node->endLineRel = line - startLine;
+ node->endCol=endCol;
+ //moving
+
+ moveSubNodesUp(node);
+ }
+ else
+ {
+#if JW_DEBUG
+ kdDebug(13000)<<"Closing a node which had already a valid end"<<endl;
+#endif
+ // block has already an ending
+ if (startLine+node->endLineRel == line)
+ {
+ node->endCol=endCol;
+ // we won, just skip
+#if JW_DEBUG
+ kdDebug(13000)<< "We won, just skipping (correctEndings)"<<endl;
+#endif
+ }
+ else
+ {
+ int bakEndLine = node->endLineRel+startLine;
+ uint bakEndCol = node->endCol;
+ node->endLineRel = line-startLine;
+ node->endCol=endCol;
+
+#if JW_DEBUG
+ kdDebug(13000)<< "reclosed node had childnodes()"<<endl;
+ kdDebug(13000)<<"It could be, that childnodes() need to be moved up"<<endl;
+#endif
+ moveSubNodesUp(node);
+
+ if (node->parentNode)
+ {
+ correctEndings(data,node->parentNode,bakEndLine, bakEndCol,node->parentNode->findChild(node)+1); // ????
+ }
+ else
+ {
+ //add to unopened list (bakEndLine)
+ }
+ }
+ }
+ }
+ return true;
+}
+
+void KateCodeFoldingTree::moveSubNodesUp(KateCodeFoldingNode *node)
+{
+ int mypos = node->parentNode->findChild(node);
+ int removepos=-1;
+ int count = node->childCount();
+ for (int i=0; i<count; i++)
+ if (node->child(i)->startLineRel >= node->endLineRel)
+ {
+ removepos=i;
+ break;
+ }
+#if JW_DEBUG
+ kdDebug(13000)<<QString("remove pos: %1").arg(removepos)<<endl;
+#endif
+ if (removepos>-1)
+ {
+#if JW_DEBUG
+ kdDebug(13000)<<"Children need to be moved"<<endl;
+#endif
+ KateCodeFoldingNode *moveNode;
+ if (mypos == (int)node->parentNode->childCount()-1)
+ {
+ while (removepos<(int)node->childCount())
+ {
+ node->parentNode->appendChild(moveNode=node->takeChild(removepos));
+ moveNode->parentNode = node->parentNode;
+ moveNode->startLineRel += node->startLineRel;
+ }
+ }
+ else
+ {
+ int insertPos=mypos;
+ while (removepos < (int)node->childCount())
+ {
+ insertPos++;
+ node->parentNode->insertChild(insertPos, moveNode=node->takeChild(removepos));
+ moveNode->parentNode = node->parentNode; // That should solve a crash
+ moveNode->startLineRel += node->startLineRel;
+ }
+ }
+ }
+
+}
+
+
+
+void KateCodeFoldingTree::addOpening(KateCodeFoldingNode *node,signed char nType, QMemArray<uint>* list,unsigned int line,unsigned int charPos)
+{
+ uint startLine = getStartLine(node);
+ if ((startLine==line) && (node->type!=0))
+ {
+#if JW_DEBUG
+ kdDebug(13000)<<"startLine equals line"<<endl;
+#endif
+ if (nType == node->type)
+ {
+#if JW_DEBUG
+ kdDebug(13000)<<"Node exists"<<endl;
+#endif
+ node->deleteOpening = false;
+ node->startCol=charPos;
+ KateCodeFoldingNode *parent = node->parentNode;
+
+ if (!node->endLineValid)
+ {
+ int current = parent->findChild(node);
+ int count = parent->childCount()-(current+1);
+ node->endLineRel = parent->endLineRel - node->startLineRel;
+
+// EXPERIMENTAL TEST BEGIN
+// move this afte the test for unopened, but closed regions within the parent node, or if there are no siblings, bubble up
+ if (parent)
+ if (parent->type == node->type)
+ {
+ if (parent->endLineValid)
+ {
+ removeEnding(parent, line);
+ node->endLineValid = true;
+ }
+ }
+
+// EXPERIMENTAL TEST BEGIN
+
+ if (current != (int)parent->childCount()-1)
+ {
+ //search for an unopened but closed region, even if the parent is of the same type
+#ifdef __GNUC__
+#warning "FIXME: why does this seem to work?"
+#endif
+// if (node->type != parent->type)
+ {
+ for (int i=current+1; i<(int)parent->childCount(); i++)
+ {
+ if (parent->child(i)->type == -node->type)
+ {
+ count = (i-current-1);
+ node->endLineValid = true;
+ node->endLineRel = getStartLine(parent->child(i))-line;
+ node->endCol = parent->child(i)->endCol;
+ KateCodeFoldingNode *child = parent->takeChild(i);
+ markedForDeleting.removeRef( child );
+ delete child;
+ break;
+ }
+ }
+ }
+// else
+// {
+// parent->endLineValid = false;
+// parent->endLineRel = 20000;
+// }
+
+ if (count>0)
+ {
+ for (int i=0;i<count;i++)
+ {
+ KateCodeFoldingNode *tmp;
+ node->appendChild(tmp=parent->takeChild(current+1));
+ tmp->startLineRel -= node->startLineRel;
+ tmp->parentNode = node;
+ }
+ }
+ }
+
+ }
+
+ addOpening_further_iterations(node, nType, list, line, 0, startLine,node->startCol);
+
+ } //else ohoh, much work to do same line, but other region type
+ }
+ else
+ { // create a new region
+ KateCodeFoldingNode *newNode = new KateCodeFoldingNode (node,nType,line-startLine);
+ something_changed = true;
+
+ int insert_position=-1;
+ for (int i=0; i<(int)node->childCount(); i++)
+ {
+ if (startLine+node->child(i)->startLineRel > line)
+ {
+ insert_position=i;
+ break;
+ }
+ }
+
+ int current;
+ if (insert_position==-1)
+ {
+ node->appendChild(newNode);
+ current = node->childCount()-1;
+ }
+ else
+ {
+ node->insertChild(insert_position, newNode);
+ current = insert_position;
+ }
+
+// if (node->type==newNode->type)
+// {
+// newNode->endLineValid=true;
+// node->endLineValid=false;
+// newNode->endLineRel=node->endLineRel-newNode->startLineRel;
+// node->endLineRel=20000; //FIXME
+
+ int count = node->childCount() - (current+1);
+ newNode->endLineRel -= newNode->startLineRel;
+ if (current != (int)node->childCount()-1)
+ {
+ if (node->type != newNode->type)
+ {
+ for (int i=current+1; i<(int)node->childCount(); i++)
+ {
+ if (node->child(i)->type == -newNode->type)
+ {
+ count = node->childCount() - i - 1;
+ newNode->endLineValid = true;
+ newNode->endLineRel = line - getStartLine(node->child(i));
+ KateCodeFoldingNode *child = node->takeChild(i);
+ markedForDeleting.removeRef( child );
+ delete child;
+ break;
+ }
+ }
+ }
+ else
+ {
+ node->endLineValid = false;
+ node->endLineRel = 10000;
+ }
+ if (count > 0)
+ {
+ for (int i=0;i<count;i++)
+ {
+ KateCodeFoldingNode *tmp;
+ newNode->appendChild(tmp=node->takeChild(current+1));
+ tmp->parentNode=newNode;
+ }
+ }
+// }
+ }
+
+ addOpening(newNode, nType, list, line,charPos);
+
+ addOpening_further_iterations(node, node->type, list, line, current, startLine,node->startCol);
+ }
+}
+
+
+void KateCodeFoldingTree::addOpening_further_iterations(KateCodeFoldingNode *node,signed char /* nType */, QMemArray<uint>*
+ list,unsigned int line,int current, unsigned int startLine,unsigned int charPos)
+{
+ while (!(list->isEmpty()))
+ {
+ if (list->isEmpty())
+ return;
+ else
+ {
+ signed char data = (*list)[list->size()-2];
+ uint charPos=(*list)[list->size()-1];
+ list->resize (list->size()-2);
+
+ if (data<0)
+ {
+#if JW_DEBUG
+ kdDebug(13000)<<"An ending was found"<<endl;
+#endif
+
+ if (correctEndings(data,node,line,charPos,-1))
+ return; // -1 ?
+
+#if 0
+ if(data == -nType)
+ {
+ if (node->endLineValid)
+ {
+ if (node->endLineRel+startLine==line) // We've won again
+ {
+ //handle next node;
+ }
+ else
+ { // much moving
+ node->endLineRel=line-startLine;
+ node->endLineValid=true;
+ }
+ return; // next higher level should do the rest
+ }
+ else
+ {
+ node->endLineRel=line-startLine;
+ node->endLineValid=true;
+ //much moving
+ }
+ } //else add to unopened list
+#endif
+ }
+ else
+ {
+ bool needNew = true;
+ if (current < (int)node->childCount())
+ {
+ if (getStartLine(node->child(current)) == line)
+ needNew=false;
+ }
+ if (needNew)
+ {
+ something_changed = true;
+ KateCodeFoldingNode *newNode = new KateCodeFoldingNode(node, data, line-startLine);
+ node->insertChild(current, newNode); //find the correct position later
+ }
+
+ addOpening(node->child(current), data, list, line,charPos);
+ current++;
+ //lookup node or create subnode
+ }
+ }
+ } // end while
+}
+
+unsigned int KateCodeFoldingTree::getStartLine(KateCodeFoldingNode *node)
+{
+ unsigned int lineStart=0;
+ for (KateCodeFoldingNode *iter=node; iter->type != 0; iter=iter->parentNode)
+ lineStart += iter->startLineRel;
+
+ return lineStart;
+}
+
+
+void KateCodeFoldingTree::lineHasBeenRemoved(unsigned int line)
+{
+ lineMapping.clear();
+ dontIgnoreUnchangedLines.insert(line, &trueVal);
+ dontIgnoreUnchangedLines.insert(line-1, &trueVal);
+ dontIgnoreUnchangedLines.insert(line+1, &trueVal);
+ hiddenLinesCountCacheValid = false;
+#if JW_DEBUG
+ kdDebug(13000)<<QString("KateCodeFoldingTree::lineHasBeenRemoved: %1").arg(line)<<endl;
+#endif
+
+//line ++;
+ findAndMarkAllNodesforRemovalOpenedOrClosedAt(line); //It's an ugly solution
+ cleanupUnneededNodes(line); //It's an ugly solution
+
+ KateCodeFoldingNode *node = findNodeForLine(line);
+//????? if (node->endLineValid)
+ {
+ int startLine = getStartLine(node);
+ if (startLine == (int)line)
+ node->startLineRel--;
+ else
+ {
+ if (node->endLineRel == 0)
+ node->endLineValid = false;
+ node->endLineRel--;
+ }
+
+ int count = node->childCount();
+ for (int i=0; i<count; i++)
+ {
+ if (node->child(i)->startLineRel+startLine >= line)
+ node->child(i)->startLineRel--;
+ }
+ }
+
+ if (node->parentNode)
+ decrementBy1(node->parentNode, node);
+
+ for (QValueList<KateHiddenLineBlock>::Iterator it=hiddenLines.begin(); it!=hiddenLines.end(); ++it)
+ {
+ if ((*it).start > line)
+ (*it).start--;
+ else if ((*it).start+(*it).length > line)
+ (*it).length--;
+ }
+}
+
+
+void KateCodeFoldingTree::decrementBy1(KateCodeFoldingNode *node, KateCodeFoldingNode *after)
+{
+ if (node->endLineRel == 0)
+ node->endLineValid = false;
+ node->endLineRel--;
+
+ for (uint i=node->findChild(after)+1; i < node->childCount(); ++i)
+ node->child(i)->startLineRel--;
+
+ if (node->parentNode)
+ decrementBy1(node->parentNode,node);
+}
+
+
+void KateCodeFoldingTree::lineHasBeenInserted(unsigned int line)
+{
+ lineMapping.clear();
+ dontIgnoreUnchangedLines.insert(line, &trueVal);
+ dontIgnoreUnchangedLines.insert(line-1, &trueVal);
+ dontIgnoreUnchangedLines.insert(line+1, &trueVal);
+ hiddenLinesCountCacheValid = false;
+//return;
+#if JW_DEBUG
+ kdDebug(13000)<<QString("KateCodeFoldingTree::lineHasBeenInserted: %1").arg(line)<<endl;
+#endif
+
+// findAndMarkAllNodesforRemovalOpenedOrClosedAt(line);
+// cleanupUnneededNodes(line);
+
+ KateCodeFoldingNode *node = findNodeForLine(line);
+// ???????? if (node->endLineValid)
+ {
+ int startLine=getStartLine(node);
+ if (node->type < 0)
+ node->startLineRel++;
+ else
+ node->endLineRel++;
+
+ for (uint i=0; i < node->childCount(); ++i)
+ {
+ KateCodeFoldingNode *iter = node->child(i);
+
+ if (iter->startLineRel+startLine >= line)
+ iter->startLineRel++;
+ }
+ }
+
+ if (node->parentNode)
+ incrementBy1(node->parentNode, node);
+
+ for (QValueList<KateHiddenLineBlock>::Iterator it=hiddenLines.begin(); it!=hiddenLines.end(); ++it)
+ {
+ if ((*it).start > line)
+ (*it).start++;
+ else if ((*it).start+(*it).length > line)
+ (*it).length++;
+ }
+}
+
+void KateCodeFoldingTree::incrementBy1(KateCodeFoldingNode *node, KateCodeFoldingNode *after)
+{
+ node->endLineRel++;
+
+ for (uint i=node->findChild(after)+1; i < node->childCount(); ++i)
+ node->child(i)->startLineRel++;
+
+ if (node->parentNode)
+ incrementBy1(node->parentNode,node);
+}
+
+
+void KateCodeFoldingTree::findAndMarkAllNodesforRemovalOpenedOrClosedAt(unsigned int line)
+{
+#ifdef __GNUC__
+#warning "FIXME: make this multiple region changes per line save";
+#endif
+// return;
+ markedForDeleting.clear();
+ KateCodeFoldingNode *node = findNodeForLine(line);
+ if (node->type == 0)
+ return;
+
+ addNodeToRemoveList(node, line);
+
+ while (((node->parentNode) && (node->parentNode->type!=0)) && (getStartLine(node->parentNode)==line))
+ {
+ node = node->parentNode;
+ addNodeToRemoveList(node, line);
+ }
+#if JW_DEBUG
+ kdDebug(13000)<<" added line to markedForDeleting list"<<endl;
+#endif
+}
+
+
+void KateCodeFoldingTree::addNodeToRemoveList(KateCodeFoldingNode *node,unsigned int line)
+{
+ bool add=false;
+#ifdef __GNUC__
+#warning "FIXME: make this multiple region changes per line save";
+#endif
+ unsigned int startLine=getStartLine(node);
+ if ((startLine==line) && (node->startLineValid))
+ {
+ add=true;
+ node->deleteOpening = true;
+ }
+ if ((startLine+node->endLineRel==line) || ((node->endLineValid==false) && (node->deleteOpening)))
+ {
+ int myPos=node->parentNode->findChild(node); // this has to be implemented nicely
+ if ((int)node->parentNode->childCount()>myPos+1)
+ addNodeToRemoveList(node->parentNode->child(myPos+1),line);
+ add=true;
+ node->deleteEnding = true;
+ }
+
+ if(add)
+ markedForDeleting.append(node);
+
+}
+
+
+void KateCodeFoldingTree::findAllNodesOpenedOrClosedAt(unsigned int line)
+{
+ nodesForLine.clear();
+ KateCodeFoldingNode *node = findNodeForLine(line);
+ if (node->type == 0)
+ return;
+
+ unsigned int startLine = getStartLine(node);
+ if (startLine == line)
+ nodesForLine.append(node);
+ else if ((startLine+node->endLineRel == line))
+ nodesForLine.append(node);
+
+ while (node->parentNode)
+ {
+ addNodeToFoundList(node->parentNode, line, node->parentNode->findChild(node));
+ node = node->parentNode;
+ }
+#if JW_DEBUG
+ kdDebug(13000)<<" added line to nodesForLine list"<<endl;
+#endif
+}
+
+
+void KateCodeFoldingTree::addNodeToFoundList(KateCodeFoldingNode *node,unsigned int line,int childpos)
+{
+ unsigned int startLine = getStartLine(node);
+
+ if ((startLine==line) && (node->type!=0))
+ nodesForLine.append(node);
+ else if ((startLine+node->endLineRel==line) && (node->type!=0))
+ nodesForLine.append(node);
+
+ for (int i=childpos+1; i<(int)node->childCount(); i++)
+ {
+ KateCodeFoldingNode *child = node->child(i);
+
+ if (startLine+child->startLineRel == line)
+ {
+ nodesForLine.append(child);
+ addNodeToFoundList(child, line, 0);
+ }
+ else
+ break;
+ }
+}
+
+
+void KateCodeFoldingTree::cleanupUnneededNodes(unsigned int line)
+{
+#if JW_DEBUG
+ kdDebug(13000)<<"void KateCodeFoldingTree::cleanupUnneededNodes(unsigned int line)"<<endl;
+#endif
+
+// return;
+ if (markedForDeleting.isEmpty())
+ return;
+
+ for (int i=0; i<(int)markedForDeleting.count(); i++)
+ {
+ KateCodeFoldingNode *node = markedForDeleting.at(i);
+ if (node->deleteOpening)
+ kdDebug(13000)<<"DELETE OPENING SET"<<endl;
+ if (node->deleteEnding)
+ kdDebug(13000)<<"DELETE ENDING SET"<<endl;
+
+ if ((node->deleteOpening) && (node->deleteEnding))
+ {
+#if JW_DEBUG
+ kdDebug(13000)<<"Deleting complete node"<<endl;
+#endif
+ if (node->endLineValid) // just delete it, it has been opened and closed on this line
+ {
+ int f = node->parentNode->findChild (node);
+
+ if (f >= 0)
+ delete node->parentNode->takeChild(f);
+ }
+ else
+ {
+ removeOpening(node, line);
+ // the node has subnodes which need to be moved up and this one has to be deleted
+ }
+ something_changed = true;
+ }
+ else
+ {
+ if ((node->deleteOpening) && (node->startLineValid))
+ {
+#if JW_DEBUG
+ kdDebug(13000)<<"calling removeOpening"<<endl;
+#endif
+ removeOpening(node, line);
+ something_changed = true;
+ }
+ else
+ {
+ dontDeleteOpening(node);
+
+ if ((node->deleteEnding) && (node->endLineValid))
+ {
+ dontDeleteEnding(node);
+ removeEnding(node, line);
+ something_changed = true;
+ }
+ else
+ dontDeleteEnding(node);
+ }
+ }
+ }
+}
+
+void KateCodeFoldingTree::dontDeleteEnding(KateCodeFoldingNode* node)
+{
+ node->deleteEnding = false;
+}
+
+
+void KateCodeFoldingTree::dontDeleteOpening(KateCodeFoldingNode* node)
+{
+ node->deleteOpening = false;
+}
+
+
+void KateCodeFoldingTree::toggleRegionVisibility(unsigned int line)
+{
+ // hl whole file
+ m_buffer->line (m_buffer->count()-1);
+
+ lineMapping.clear();
+ hiddenLinesCountCacheValid = false;
+ kdDebug(13000)<<QString("KateCodeFoldingTree::toggleRegionVisibility() %1").arg(line)<<endl;
+
+ findAllNodesOpenedOrClosedAt(line);
+ for (int i=0; i<(int)nodesForLine.count(); i++)
+ {
+ KateCodeFoldingNode *node=nodesForLine.at(i);
+ if ( (!node->startLineValid) || (getStartLine(node) != line) )
+ {
+ nodesForLine.remove(i);
+ i--;
+ }
+ }
+
+ if (nodesForLine.isEmpty())
+ return;
+
+ nodesForLine.at(0)->visible = !nodesForLine.at(0)->visible;
+
+ if (!nodesForLine.at(0)->visible)
+ addHiddenLineBlock(nodesForLine.at(0),line);
+ else
+ {
+ for (QValueList<KateHiddenLineBlock>::Iterator it=hiddenLines.begin(); it!=hiddenLines.end();++it)
+ if ((*it).start == line+1)
+ {
+ hiddenLines.remove(it);
+ break;
+ }
+
+ updateHiddenSubNodes(nodesForLine.at(0));
+ }
+
+ emit regionVisibilityChangedAt(line);
+}
+
+void KateCodeFoldingTree::updateHiddenSubNodes(KateCodeFoldingNode *node)
+{
+ for (uint i=0; i < node->childCount(); ++i)
+ {
+ KateCodeFoldingNode *iter = node->child(i);
+
+ if (!iter->visible)
+ addHiddenLineBlock(iter, getStartLine(iter));
+ else
+ updateHiddenSubNodes(iter);
+ }
+}
+
+void KateCodeFoldingTree::addHiddenLineBlock(KateCodeFoldingNode *node,unsigned int line)
+{
+ KateHiddenLineBlock data;
+ data.start = line+1;
+ data.length = node->endLineRel-(existsOpeningAtLineAfter(line+node->endLineRel,node)?1:0); // without -1;
+ bool inserted = false;
+
+ for (QValueList<KateHiddenLineBlock>::Iterator it=hiddenLines.begin(); it!=hiddenLines.end(); ++it)
+ {
+ if (((*it).start>=data.start) && ((*it).start<=data.start+data.length-1)) // another hidden block starting at the within this block already exits -> adapt new block
+ {
+ // the existing block can't have lines behind the new one, because a newly hidden
+ // block has to encapsulate already hidden ones
+ it=hiddenLines.remove(it);
+ --it;
+ }
+ else
+ {
+ if ((*it).start > line)
+ {
+ hiddenLines.insert(it, data);
+ inserted = true;
+
+ break;
+ }
+ }
+ }
+
+ if (!inserted)
+ hiddenLines.append(data);
+}
+
+bool KateCodeFoldingTree::existsOpeningAtLineAfter(unsigned int line, KateCodeFoldingNode *node)
+{
+ for(KateCodeFoldingNode *tmp = node->parentNode; tmp; tmp=tmp->parentNode)
+ {
+ KateCodeFoldingNode *tmp2;
+ unsigned int startLine=getStartLine(tmp);
+
+ if ((tmp2 = tmp->child(tmp->findChild(node) + 1))
+ && ((tmp2->startLineRel + startLine) == line))
+ return true;
+
+ if ((startLine + tmp->endLineRel) > line)
+ return false;
+ }
+
+ return false;
+}
+
+
+//
+// get the real line number for a virtual line
+//
+unsigned int KateCodeFoldingTree::getRealLine(unsigned int virtualLine)
+{
+ // he, if nothing is hidden, why look at it ;)
+ if (hiddenLines.isEmpty())
+ return virtualLine;
+
+ // kdDebug(13000)<<QString("VirtualLine %1").arg(virtualLine)<<endl;
+
+ unsigned int *real=lineMapping[virtualLine];
+ if (real)
+ return (*real);
+
+ unsigned int tmp = virtualLine;
+ for (QValueList<KateHiddenLineBlock>::ConstIterator it=hiddenLines.begin();it!=hiddenLines.end();++it)
+ {
+ if ((*it).start<=virtualLine)
+ virtualLine += (*it).length;
+ else
+ break;
+ }
+
+ // kdDebug(13000)<<QString("Real Line %1").arg(virtualLine)<<endl;
+
+ lineMapping.insert(tmp, new unsigned int(virtualLine));
+ return virtualLine;
+}
+
+//
+// get the virtual line number for a real line
+//
+unsigned int KateCodeFoldingTree::getVirtualLine(unsigned int realLine)
+{
+ // he, if nothing is hidden, why look at it ;)
+ if (hiddenLines.isEmpty())
+ return realLine;
+
+ // kdDebug(13000)<<QString("RealLine--> %1").arg(realLine)<<endl;
+
+ for (QValueList<KateHiddenLineBlock>::ConstIterator it=hiddenLines.fromLast(); it!=hiddenLines.end(); --it)
+ {
+ if ((*it).start <= realLine)
+ realLine -= (*it).length;
+ // else
+ // break;
+ }
+
+ // kdDebug(13000)<<QString("-->virtual Line %1").arg(realLine)<<endl;
+
+ return realLine;
+}
+
+//
+// get the number of hidden lines
+//
+unsigned int KateCodeFoldingTree::getHiddenLinesCount(unsigned int doclen)
+{
+ // he, if nothing is hidden, why look at it ;)
+ if (hiddenLines.isEmpty())
+ return 0;
+
+ if (hiddenLinesCountCacheValid)
+ return hiddenLinesCountCache;
+
+ hiddenLinesCountCacheValid = true;
+ hiddenLinesCountCache = 0;
+
+ for (QValueList<KateHiddenLineBlock>::ConstIterator it=hiddenLines.begin(); it!=hiddenLines.end(); ++it)
+ {
+ if ((*it).start+(*it).length<=doclen)
+ hiddenLinesCountCache += (*it).length;
+ else
+ {
+ hiddenLinesCountCache += ((*it).length- ((*it).length + (*it).start - doclen));
+ break;
+ }
+ }
+
+ return hiddenLinesCountCache;
+}
+
+void KateCodeFoldingTree::collapseToplevelNodes()
+{
+ // hl whole file
+ m_buffer->line (m_buffer->count()-1);
+
+ if (m_root.noChildren ())
+ return;
+
+ for ( uint i=0; i < m_root.childCount(); ++i )
+ {
+ KateCodeFoldingNode *node = m_root.child(i);
+
+ if (node->visible && node->startLineValid && node->endLineValid)
+ {
+ node->visible=false;
+ lineMapping.clear();
+ hiddenLinesCountCacheValid = false;
+ addHiddenLineBlock(node,node->startLineRel);
+ emit regionVisibilityChangedAt(node->startLineRel);
+ }
+ }
+}
+
+void KateCodeFoldingTree::expandToplevelNodes(int numLines)
+{
+ // hl whole file
+ m_buffer->line (m_buffer->count()-1);
+
+ KateLineInfo line;
+ for (int i = 0; i < numLines; i++) {
+ getLineInfo(&line, i);
+
+ if (line.startsInVisibleBlock)
+ toggleRegionVisibility(i);
+ }
+}
+
+int KateCodeFoldingTree::collapseOne(int realLine)
+{
+ // hl whole file
+ m_buffer->line (m_buffer->count()-1);
+
+ KateLineInfo line;
+ int unrelatedBlocks = 0;
+ for (int i = realLine; i >= 0; i--) {
+ getLineInfo(&line, i);
+
+ if (line.topLevel && !line.endsBlock)
+ // optimisation
+ break;
+
+ if (line.endsBlock && ( line.invalidBlockEnd ) && (i != realLine)) {
+ unrelatedBlocks++;
+ }
+
+ if (line.startsVisibleBlock) {
+ unrelatedBlocks--;
+ if (unrelatedBlocks == -1) {
+ toggleRegionVisibility(i);
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+void KateCodeFoldingTree::expandOne(int realLine, int numLines)
+{
+ // hl whole file
+ m_buffer->line (m_buffer->count()-1);
+
+ KateLineInfo line;
+ int blockTrack = 0;
+ for (int i = realLine; i >= 0; i--) {
+ getLineInfo(&line, i);
+
+ if (line.topLevel)
+ // done
+ break;
+
+ if (line.startsInVisibleBlock && i != realLine) {
+ if (blockTrack == 0)
+ toggleRegionVisibility(i);
+
+ blockTrack--;
+ }
+
+ if (line.endsBlock)
+ blockTrack++;
+
+ if (blockTrack < 0)
+ // too shallow
+ break;
+ }
+
+ blockTrack = 0;
+ for (int i = realLine; i < numLines; i++) {
+ getLineInfo(&line, i);
+
+ if (line.topLevel)
+ // done
+ break;
+
+ if (line.startsInVisibleBlock) {
+ if (blockTrack == 0)
+ toggleRegionVisibility(i);
+
+ blockTrack++;
+ }
+
+ if (line.endsBlock)
+ blockTrack--;
+
+ if (blockTrack < 0)
+ // too shallow
+ break;
+ }
+}
+
+void KateCodeFoldingTree::ensureVisible( uint line )
+{
+ // first have a look, if the line is really hidden
+ bool found=false;
+ for (QValueList<KateHiddenLineBlock>::ConstIterator it=hiddenLines.begin();it!=hiddenLines.end();++it)
+ {
+ if ( ((*it).start<=line) && ((*it).start+(*it).length>line) )
+ {
+ found=true;
+ break;
+ }
+ }
+
+
+ if (!found) return;
+
+ kdDebug(13000)<<"line "<<line<<" is really hidden ->show block"<<endl;
+
+ // it looks like we really have to ensure visibility
+ KateCodeFoldingNode *n = findNodeForLine( line );
+ do {
+ if ( ! n->visible )
+ toggleRegionVisibility( getStartLine( n ) );
+ n = n->parentNode;
+ } while( n );
+
+}
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katecodefoldinghelpers.h b/kate/part/katecodefoldinghelpers.h
new file mode 100644
index 000000000..bd5e5e8d5
--- /dev/null
+++ b/kate/part/katecodefoldinghelpers.h
@@ -0,0 +1,222 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2002 Joseph Wenninger <jowenn@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef _KATE_CODEFOLDING_HELPERS_
+#define _KATE_CODEFOLDING_HELPERS_
+
+//BEGIN INCLUDES + FORWARDS
+#include <qptrlist.h>
+#include <qvaluelist.h>
+#include <qobject.h>
+#include <qintdict.h>
+#include <qmemarray.h>
+
+class KateCodeFoldingTree;
+class KateTextCursor;
+class KateBuffer;
+
+class QString;
+//END
+
+class KateHiddenLineBlock
+{
+ public:
+ unsigned int start;
+ unsigned int length;
+};
+
+class KateLineInfo
+{
+ public:
+ bool topLevel;
+ bool startsVisibleBlock;
+ bool startsInVisibleBlock;
+ bool endsBlock;
+ bool invalidBlockEnd;
+};
+
+class KateCodeFoldingNode
+{
+ friend class KateCodeFoldingTree;
+
+ public:
+ KateCodeFoldingNode ();
+ KateCodeFoldingNode (KateCodeFoldingNode *par, signed char typ, unsigned int sLRel);
+
+ ~KateCodeFoldingNode ();
+
+ inline int nodeType () { return type;}
+
+ inline bool isVisible () {return visible;}
+
+ inline KateCodeFoldingNode *getParentNode () {return parentNode;}
+
+ bool getBegin (KateCodeFoldingTree *tree, KateTextCursor* begin);
+ bool getEnd (KateCodeFoldingTree *tree, KateTextCursor *end);
+
+ /**
+ * accessors for the child nodes
+ */
+ protected:
+ inline bool noChildren () const { return m_children.isEmpty(); }
+
+ inline uint childCount () const { return m_children.size(); }
+
+ inline KateCodeFoldingNode *child (uint index) const { return m_children[index]; }
+
+ inline int findChild (KateCodeFoldingNode *node, uint start = 0) const { return m_children.find (node, start); }
+
+ inline void appendChild (KateCodeFoldingNode *node) { m_children.resize(m_children.size()+1); m_children[m_children.size()-1] = node; }
+
+ void insertChild (uint index, KateCodeFoldingNode *node);
+
+ KateCodeFoldingNode *takeChild (uint index);
+
+ void clearChildren ();
+
+ int cmpPos(KateCodeFoldingTree *tree, uint line, uint col);
+
+ /**
+ * data members
+ */
+ private:
+ KateCodeFoldingNode *parentNode;
+ unsigned int startLineRel;
+ unsigned int endLineRel;
+
+ unsigned int startCol;
+ unsigned int endCol;
+
+ bool startLineValid;
+ bool endLineValid;
+
+ signed char type; // 0 -> toplevel / invalid
+ bool visible;
+ bool deleteOpening;
+ bool deleteEnding;
+
+ QMemArray<KateCodeFoldingNode*> m_children;
+};
+
+class KateCodeFoldingTree : public QObject
+{
+ friend class KateCodeFoldingNode;
+
+ Q_OBJECT
+
+ public:
+ KateCodeFoldingTree (KateBuffer *buffer);
+ ~KateCodeFoldingTree ();
+
+ KateCodeFoldingNode *findNodeForLine (unsigned int line);
+
+ unsigned int getRealLine (unsigned int virtualLine);
+ unsigned int getVirtualLine (unsigned int realLine);
+ unsigned int getHiddenLinesCount (unsigned int docLine);
+
+ bool isTopLevel (unsigned int line);
+
+ void lineHasBeenInserted (unsigned int line);
+ void lineHasBeenRemoved (unsigned int line);
+ void debugDump ();
+ void getLineInfo (KateLineInfo *info,unsigned int line);
+
+ unsigned int getStartLine (KateCodeFoldingNode *node);
+
+ void fixRoot (int endLRel);
+ void clear ();
+
+ KateCodeFoldingNode *findNodeForPosition(unsigned int line, unsigned int column);
+ private:
+
+ KateCodeFoldingNode m_root;
+
+ KateBuffer *m_buffer;
+
+ QIntDict<unsigned int> lineMapping;
+ QIntDict<bool> dontIgnoreUnchangedLines;
+
+ QPtrList<KateCodeFoldingNode> markedForDeleting;
+ QPtrList<KateCodeFoldingNode> nodesForLine;
+ QValueList<KateHiddenLineBlock> hiddenLines;
+
+ unsigned int hiddenLinesCountCache;
+ bool something_changed;
+ bool hiddenLinesCountCacheValid;
+
+ static bool trueVal;
+
+ KateCodeFoldingNode *findNodeForLineDescending (KateCodeFoldingNode *, unsigned int, unsigned int, bool oneStepOnly=false);
+
+ bool correctEndings (signed char data, KateCodeFoldingNode *node, unsigned int line, unsigned int endCol, int insertPos);
+
+ void dumpNode (KateCodeFoldingNode *node, const QString &prefix);
+ void addOpening (KateCodeFoldingNode *node, signed char nType,QMemArray<uint>* list, unsigned int line,unsigned int charPos);
+ void addOpening_further_iterations (KateCodeFoldingNode *node,signed char nType, QMemArray<uint>*
+ list,unsigned int line,int current,unsigned int startLine,unsigned int charPos);
+
+ void incrementBy1 (KateCodeFoldingNode *node, KateCodeFoldingNode *after);
+ void decrementBy1 (KateCodeFoldingNode *node, KateCodeFoldingNode *after);
+
+ void cleanupUnneededNodes (unsigned int line);
+
+ /**
+ * if returns true, this node has been deleted !!
+ */
+ bool removeEnding (KateCodeFoldingNode *node,unsigned int line);
+
+ /**
+ * if returns true, this node has been deleted !!
+ */
+ bool removeOpening (KateCodeFoldingNode *node,unsigned int line);
+
+ void findAndMarkAllNodesforRemovalOpenedOrClosedAt (unsigned int line);
+ void findAllNodesOpenedOrClosedAt (unsigned int line);
+
+ void addNodeToFoundList (KateCodeFoldingNode *node,unsigned int line,int childpos);
+ void addNodeToRemoveList (KateCodeFoldingNode *node,unsigned int line);
+ void addHiddenLineBlock (KateCodeFoldingNode *node,unsigned int line);
+
+ bool existsOpeningAtLineAfter(unsigned int line, KateCodeFoldingNode *node);
+
+ void dontDeleteEnding (KateCodeFoldingNode*);
+ void dontDeleteOpening (KateCodeFoldingNode*);
+
+ void updateHiddenSubNodes (KateCodeFoldingNode *node);
+ void moveSubNodesUp (KateCodeFoldingNode *node);
+
+ public slots:
+ void updateLine (unsigned int line,QMemArray<uint>* regionChanges, bool *updated, bool changed,bool colschanged);
+ void toggleRegionVisibility (unsigned int);
+ void collapseToplevelNodes ();
+ void expandToplevelNodes (int numLines);
+ int collapseOne (int realLine);
+ void expandOne (int realLine, int numLines);
+ /**
+ Ensures that all nodes surrounding @p line are open
+ */
+ void ensureVisible( uint line );
+
+ signals:
+ void regionVisibilityChangedAt (unsigned int);
+ void regionBeginEndAddedRemoved (unsigned int);
+};
+
+#endif
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/kateconfig.cpp b/kate/part/kateconfig.cpp
new file mode 100644
index 000000000..c580ed5c1
--- /dev/null
+++ b/kate/part/kateconfig.cpp
@@ -0,0 +1,1429 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2003 Christoph Cullmann <cullmann@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kateconfig.h"
+
+#include "katefactory.h"
+#include "katerenderer.h"
+#include "kateview.h"
+#include "katedocument.h"
+#include "katefont.h"
+#include "kateschema.h"
+
+#include <math.h>
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kglobalsettings.h>
+#include <kcharsets.h>
+#include <klocale.h>
+#include <kfinddialog.h>
+#include <kreplacedialog.h>
+#include <kinstance.h>
+#include <kstaticdeleter.h>
+
+#include <qpopupmenu.h>
+#include <qtextcodec.h>
+
+#include <kdebug.h>
+
+//BEGIN KateConfig
+KateConfig::KateConfig ()
+ : configSessionNumber (0), configIsRunning (false)
+{
+}
+
+KateConfig::~KateConfig ()
+{
+}
+
+void KateConfig::configStart ()
+{
+ configSessionNumber++;
+
+ if (configSessionNumber > 1)
+ return;
+
+ configIsRunning = true;
+}
+
+void KateConfig::configEnd ()
+{
+ if (configSessionNumber == 0)
+ return;
+
+ configSessionNumber--;
+
+ if (configSessionNumber > 0)
+ return;
+
+ configIsRunning = false;
+
+ updateConfig ();
+}
+//END
+
+//BEGIN KateDocumentConfig
+KateDocumentConfig *KateDocumentConfig::s_global = 0;
+KateViewConfig *KateViewConfig::s_global = 0;
+KateRendererConfig *KateRendererConfig::s_global = 0;
+
+KateDocumentConfig::KateDocumentConfig ()
+ : m_tabWidth (8),
+ m_indentationWidth (2),
+ m_wordWrapAt (80),
+ m_configFlags (0),
+ m_plugins (KateFactory::self()->plugins().count()),
+ m_tabWidthSet (true),
+ m_indentationWidthSet (true),
+ m_indentationModeSet (true),
+ m_wordWrapSet (true),
+ m_wordWrapAtSet (true),
+ m_pageUpDownMovesCursorSet (true),
+ m_undoStepsSet (true),
+ m_configFlagsSet (0xFFFF),
+ m_encodingSet (true),
+ m_eolSet (true),
+ m_allowEolDetectionSet (true),
+ m_backupFlagsSet (true),
+ m_searchDirConfigDepthSet (true),
+ m_backupPrefixSet (true),
+ m_backupSuffixSet (true),
+ m_pluginsSet (m_plugins.size()),
+ m_doc (0)
+{
+ s_global = this;
+
+ // init plugin array
+ m_plugins.fill (false);
+ m_pluginsSet.fill (true);
+
+ // init with defaults from config or really hardcoded ones
+ KConfig *config = kapp->config();
+ config->setGroup("Kate Document Defaults");
+ readConfig (config);
+}
+
+KateDocumentConfig::KateDocumentConfig (KateDocument *doc)
+ : m_configFlags (0),
+ m_plugins (KateFactory::self()->plugins().count()),
+ m_tabWidthSet (false),
+ m_indentationWidthSet (false),
+ m_indentationModeSet (false),
+ m_wordWrapSet (false),
+ m_wordWrapAtSet (false),
+ m_pageUpDownMovesCursorSet (false),
+ m_undoStepsSet (false),
+ m_configFlagsSet (0),
+ m_encodingSet (false),
+ m_eolSet (false),
+ m_allowEolDetectionSet (false),
+ m_backupFlagsSet (false),
+ m_searchDirConfigDepthSet (false),
+ m_backupPrefixSet (false),
+ m_backupSuffixSet (false),
+ m_pluginsSet (m_plugins.size()),
+ m_doc (doc)
+{
+ // init plugin array
+ m_plugins.fill (false);
+ m_pluginsSet.fill (false);
+}
+
+KateDocumentConfig::~KateDocumentConfig ()
+{
+}
+
+void KateDocumentConfig::readConfig (KConfig *config)
+{
+ configStart ();
+
+ setTabWidth (config->readNumEntry("Tab Width", 8));
+
+ setIndentationWidth (config->readNumEntry("Indentation Width", 2));
+
+ setIndentationMode (config->readNumEntry("Indentation Mode", KateDocumentConfig::imNone));
+
+ setWordWrap (config->readBoolEntry("Word Wrap", false));
+ setWordWrapAt (config->readNumEntry("Word Wrap Column", 80));
+ setPageUpDownMovesCursor (config->readBoolEntry("PageUp/PageDown Moves Cursor", false));
+ setUndoSteps(config->readNumEntry("Undo Steps", 0));
+
+ setConfigFlags (config->readNumEntry("Basic Config Flags", KateDocumentConfig::cfTabIndents
+ | KateDocumentConfig::cfKeepIndentProfile
+ | KateDocumentConfig::cfWrapCursor
+ | KateDocumentConfig::cfShowTabs
+ | KateDocumentConfig::cfSmartHome
+ | KateDocumentConfig::cfIndentPastedText));
+
+ setEncoding (config->readEntry("Encoding", ""));
+
+ setEol (config->readNumEntry("End of Line", 0));
+ setAllowEolDetection (config->readBoolEntry("Allow End of Line Detection", true));
+
+ setBackupFlags (config->readNumEntry("Backup Config Flags", 1));
+
+ setSearchDirConfigDepth (config->readNumEntry("Search Dir Config Depth", 3));
+
+ setBackupPrefix (config->readEntry("Backup Prefix", QString ("")));
+
+ setBackupSuffix (config->readEntry("Backup Suffix", QString ("~")));
+
+ // plugins
+ for (uint i=0; i<KateFactory::self()->plugins().count(); i++)
+ setPlugin (i, config->readBoolEntry("KTextEditor Plugin " + (KateFactory::self()->plugins())[i]->library(), false));
+
+ configEnd ();
+}
+
+void KateDocumentConfig::writeConfig (KConfig *config)
+{
+ config->writeEntry("Tab Width", tabWidth());
+
+ config->writeEntry("Indentation Width", indentationWidth());
+ config->writeEntry("Indentation Mode", indentationMode());
+
+ config->writeEntry("Word Wrap", wordWrap());
+ config->writeEntry("Word Wrap Column", wordWrapAt());
+
+ config->writeEntry("PageUp/PageDown Moves Cursor", pageUpDownMovesCursor());
+
+ config->writeEntry("Undo Steps", undoSteps());
+
+ config->writeEntry("Basic Config Flags", configFlags());
+
+ config->writeEntry("Encoding", encoding());
+
+ config->writeEntry("End of Line", eol());
+ config->writeEntry("Allow End of Line Detection", allowEolDetection());
+
+ config->writeEntry("Backup Config Flags", backupFlags());
+
+ config->writeEntry("Search Dir Config Depth", searchDirConfigDepth());
+
+ config->writeEntry("Backup Prefix", backupPrefix());
+
+ config->writeEntry("Backup Suffix", backupSuffix());
+
+ // plugins
+ for (uint i=0; i<KateFactory::self()->plugins().count(); i++)
+ config->writeEntry("KTextEditor Plugin " + (KateFactory::self()->plugins())[i]->library(), plugin(i));
+}
+
+void KateDocumentConfig::updateConfig ()
+{
+ if (m_doc)
+ {
+ m_doc->updateConfig ();
+ return;
+ }
+
+ if (isGlobal())
+ {
+ for (uint z=0; z < KateFactory::self()->documents()->count(); z++)
+ {
+ KateFactory::self()->documents()->at(z)->updateConfig ();
+ }
+ }
+}
+
+int KateDocumentConfig::tabWidth () const
+{
+ if (m_tabWidthSet || isGlobal())
+ return m_tabWidth;
+
+ return s_global->tabWidth();
+}
+
+void KateDocumentConfig::setTabWidth (int tabWidth)
+{
+ if (tabWidth < 1)
+ return;
+
+ configStart ();
+
+ m_tabWidthSet = true;
+ m_tabWidth = tabWidth;
+
+ configEnd ();
+}
+
+int KateDocumentConfig::indentationWidth () const
+{
+ if (m_indentationWidthSet || isGlobal())
+ return m_indentationWidth;
+
+ return s_global->indentationWidth();
+}
+
+void KateDocumentConfig::setIndentationWidth (int indentationWidth)
+{
+ if (indentationWidth < 1)
+ return;
+
+ configStart ();
+
+ m_indentationWidthSet = true;
+ m_indentationWidth = indentationWidth;
+
+ configEnd ();
+}
+
+uint KateDocumentConfig::indentationMode () const
+{
+ if (m_indentationModeSet || isGlobal())
+ return m_indentationMode;
+
+ return s_global->indentationMode();
+}
+
+void KateDocumentConfig::setIndentationMode (uint indentationMode)
+{
+ configStart ();
+
+ m_indentationModeSet = true;
+ m_indentationMode = indentationMode;
+
+ configEnd ();
+}
+
+bool KateDocumentConfig::wordWrap () const
+{
+ if (m_wordWrapSet || isGlobal())
+ return m_wordWrap;
+
+ return s_global->wordWrap();
+}
+
+void KateDocumentConfig::setWordWrap (bool on)
+{
+ configStart ();
+
+ m_wordWrapSet = true;
+ m_wordWrap = on;
+
+ configEnd ();
+}
+
+unsigned int KateDocumentConfig::wordWrapAt () const
+{
+ if (m_wordWrapAtSet || isGlobal())
+ return m_wordWrapAt;
+
+ return s_global->wordWrapAt();
+}
+
+void KateDocumentConfig::setWordWrapAt (unsigned int col)
+{
+ if (col < 1)
+ return;
+
+ configStart ();
+
+ m_wordWrapAtSet = true;
+ m_wordWrapAt = col;
+
+ configEnd ();
+}
+
+uint KateDocumentConfig::undoSteps () const
+{
+ if (m_undoStepsSet || isGlobal())
+ return m_undoSteps;
+
+ return s_global->undoSteps();
+}
+
+void KateDocumentConfig::setUndoSteps (uint undoSteps)
+{
+ configStart ();
+
+ m_undoStepsSet = true;
+ m_undoSteps = undoSteps;
+
+ configEnd ();
+}
+
+bool KateDocumentConfig::pageUpDownMovesCursor () const
+{
+ if (m_pageUpDownMovesCursorSet || isGlobal())
+ return m_pageUpDownMovesCursor;
+
+ return s_global->pageUpDownMovesCursor();
+}
+
+void KateDocumentConfig::setPageUpDownMovesCursor (bool on)
+{
+ configStart ();
+
+ m_pageUpDownMovesCursorSet = true;
+ m_pageUpDownMovesCursor = on;
+
+ configEnd ();
+}
+
+uint KateDocumentConfig::configFlags () const
+{
+ if (isGlobal())
+ return m_configFlags;
+
+ return ((s_global->configFlags() & ~ m_configFlagsSet) | m_configFlags);
+}
+
+void KateDocumentConfig::setConfigFlags (KateDocumentConfig::ConfigFlags flag, bool enable)
+{
+ configStart ();
+
+ m_configFlagsSet |= flag;
+
+ if (enable)
+ m_configFlags = m_configFlags | flag;
+ else
+ m_configFlags = m_configFlags & ~ flag;
+
+ configEnd ();
+}
+
+void KateDocumentConfig::setConfigFlags (uint fullFlags)
+{
+ configStart ();
+
+ m_configFlagsSet = 0xFFFF;
+ m_configFlags = fullFlags;
+
+ configEnd ();
+}
+
+const QString &KateDocumentConfig::encoding () const
+{
+ if (m_encodingSet || isGlobal())
+ return m_encoding;
+
+ return s_global->encoding();
+}
+
+QTextCodec *KateDocumentConfig::codec ()
+{
+ if (m_encodingSet || isGlobal())
+ {
+ if (m_encoding.isEmpty() && isGlobal())
+ return KGlobal::charsets()->codecForName (QString::fromLatin1(KGlobal::locale()->encoding()));
+ else if (m_encoding.isEmpty())
+ return s_global->codec ();
+ else
+ return KGlobal::charsets()->codecForName (m_encoding);
+ }
+
+ return s_global->codec ();
+}
+
+void KateDocumentConfig::setEncoding (const QString &encoding)
+{
+ QString enc = encoding;
+
+ if (!enc.isEmpty())
+ {
+ bool found = false;
+ QTextCodec *codec = KGlobal::charsets()->codecForName (encoding, found);
+
+ if (!found || !codec)
+ return;
+
+ enc = codec->name();
+ }
+
+ configStart ();
+
+ if (isGlobal())
+ KateDocument::setDefaultEncoding (enc);
+
+ m_encodingSet = true;
+ m_encoding = enc;
+
+ configEnd ();
+}
+
+bool KateDocumentConfig::isSetEncoding () const
+{
+ return m_encodingSet;
+}
+
+int KateDocumentConfig::eol () const
+{
+ if (m_eolSet || isGlobal())
+ return m_eol;
+
+ return s_global->eol();
+}
+
+QString KateDocumentConfig::eolString ()
+{
+ if (eol() == KateDocumentConfig::eolUnix)
+ return QString ("\n");
+ else if (eol() == KateDocumentConfig::eolDos)
+ return QString ("\r\n");
+ else if (eol() == KateDocumentConfig::eolMac)
+ return QString ("\r");
+
+ return QString ("\n");
+}
+
+void KateDocumentConfig::setEol (int mode)
+{
+ configStart ();
+
+ m_eolSet = true;
+ m_eol = mode;
+
+ configEnd ();
+}
+
+bool KateDocumentConfig::allowEolDetection () const
+{
+ if (m_allowEolDetectionSet || isGlobal())
+ return m_allowEolDetection;
+
+ return s_global->allowEolDetection();
+}
+
+void KateDocumentConfig::setAllowEolDetection (bool on)
+{
+ configStart ();
+
+ m_allowEolDetectionSet = true;
+ m_allowEolDetection = on;
+
+ configEnd ();
+}
+
+uint KateDocumentConfig::backupFlags () const
+{
+ if (m_backupFlagsSet || isGlobal())
+ return m_backupFlags;
+
+ return s_global->backupFlags();
+}
+
+void KateDocumentConfig::setBackupFlags (uint flags)
+ {
+ configStart ();
+
+ m_backupFlagsSet = true;
+ m_backupFlags = flags;
+
+ configEnd ();
+}
+
+const QString &KateDocumentConfig::backupPrefix () const
+{
+ if (m_backupPrefixSet || isGlobal())
+ return m_backupPrefix;
+
+ return s_global->backupPrefix();
+}
+
+const QString &KateDocumentConfig::backupSuffix () const
+{
+ if (m_backupSuffixSet || isGlobal())
+ return m_backupSuffix;
+
+ return s_global->backupSuffix();
+}
+
+void KateDocumentConfig::setBackupPrefix (const QString &prefix)
+{
+ configStart ();
+
+ m_backupPrefixSet = true;
+ m_backupPrefix = prefix;
+
+ configEnd ();
+}
+
+void KateDocumentConfig::setBackupSuffix (const QString &suffix)
+{
+ configStart ();
+
+ m_backupSuffixSet = true;
+ m_backupSuffix = suffix;
+
+ configEnd ();
+}
+
+bool KateDocumentConfig::plugin (uint index) const
+{
+ if (index >= m_plugins.size())
+ return false;
+
+ if (m_pluginsSet.at(index) || isGlobal())
+ return m_plugins.at(index);
+
+ return s_global->plugin (index);
+}
+
+void KateDocumentConfig::setPlugin (uint index, bool load)
+{
+ if (index >= m_plugins.size())
+ return;
+
+ configStart ();
+
+ m_pluginsSet.setBit(index);
+ m_plugins.setBit(index, load);
+
+ configEnd ();
+}
+
+int KateDocumentConfig::searchDirConfigDepth () const
+{
+ if (m_searchDirConfigDepthSet || isGlobal())
+ return m_searchDirConfigDepth;
+
+ return s_global->searchDirConfigDepth ();
+}
+
+void KateDocumentConfig::setSearchDirConfigDepth (int depth)
+{
+ configStart ();
+
+ m_searchDirConfigDepthSet = true;
+ m_searchDirConfigDepth = depth;
+
+ configEnd ();
+}
+
+//END
+
+//BEGIN KateViewConfig
+KateViewConfig::KateViewConfig ()
+ :
+ m_dynWordWrapSet (true),
+ m_dynWordWrapIndicatorsSet (true),
+ m_dynWordWrapAlignIndentSet (true),
+ m_lineNumbersSet (true),
+ m_scrollBarMarksSet (true),
+ m_iconBarSet (true),
+ m_foldingBarSet (true),
+ m_bookmarkSortSet (true),
+ m_autoCenterLinesSet (true),
+ m_searchFlagsSet (true),
+ m_cmdLineSet (true),
+ m_defaultMarkTypeSet (true),
+ m_persistentSelectionSet (true),
+ m_textToSearchModeSet (true),
+ m_view (0)
+{
+ s_global = this;
+
+ // init with defaults from config or really hardcoded ones
+ KConfig *config = kapp->config();
+ config->setGroup("Kate View Defaults");
+ readConfig (config);
+}
+
+KateViewConfig::KateViewConfig (KateView *view)
+ :
+ m_dynWordWrapSet (false),
+ m_dynWordWrapIndicatorsSet (false),
+ m_dynWordWrapAlignIndentSet (false),
+ m_lineNumbersSet (false),
+ m_scrollBarMarksSet (false),
+ m_iconBarSet (false),
+ m_foldingBarSet (false),
+ m_bookmarkSortSet (false),
+ m_autoCenterLinesSet (false),
+ m_searchFlagsSet (false),
+ m_cmdLineSet (false),
+ m_defaultMarkTypeSet (false),
+ m_persistentSelectionSet (false),
+ m_textToSearchModeSet (false),
+ m_view (view)
+{
+}
+
+KateViewConfig::~KateViewConfig ()
+{
+}
+
+void KateViewConfig::readConfig (KConfig *config)
+{
+ configStart ();
+
+ setDynWordWrap (config->readBoolEntry( "Dynamic Word Wrap", true ));
+ setDynWordWrapIndicators (config->readNumEntry( "Dynamic Word Wrap Indicators", 1 ));
+ setDynWordWrapAlignIndent (config->readNumEntry( "Dynamic Word Wrap Align Indent", 80 ));
+
+ setLineNumbers (config->readBoolEntry( "Line Numbers", false));
+
+ setScrollBarMarks (config->readBoolEntry( "Scroll Bar Marks", false));
+
+ setIconBar (config->readBoolEntry( "Icon Bar", false ));
+
+ setFoldingBar (config->readBoolEntry( "Folding Bar", true));
+
+ setBookmarkSort (config->readNumEntry( "Bookmark Menu Sorting", 0 ));
+
+ setAutoCenterLines (config->readNumEntry( "Auto Center Lines", 0 ));
+
+ setSearchFlags (config->readNumEntry("Search Config Flags", KFindDialog::FromCursor | KFindDialog::CaseSensitive | KReplaceDialog::PromptOnReplace));
+
+ setCmdLine (config->readBoolEntry( "Command Line", false));
+
+ setDefaultMarkType (config->readNumEntry( "Default Mark Type", KTextEditor::MarkInterface::markType01 ));
+
+ setPersistentSelection (config->readNumEntry( "Persistent Selection", false ));
+
+ setTextToSearchMode (config->readNumEntry( "Text To Search Mode", KateViewConfig::SelectionWord));
+
+ configEnd ();
+}
+
+void KateViewConfig::writeConfig (KConfig *config)
+{
+ config->writeEntry( "Dynamic Word Wrap", dynWordWrap() );
+ config->writeEntry( "Dynamic Word Wrap Indicators", dynWordWrapIndicators() );
+ config->writeEntry( "Dynamic Word Wrap Align Indent", dynWordWrapAlignIndent() );
+
+ config->writeEntry( "Line Numbers", lineNumbers() );
+
+ config->writeEntry( "Scroll Bar Marks", scrollBarMarks() );
+
+ config->writeEntry( "Icon Bar", iconBar() );
+
+ config->writeEntry( "Folding Bar", foldingBar() );
+
+ config->writeEntry( "Bookmark Menu Sorting", bookmarkSort() );
+
+ config->writeEntry( "Auto Center Lines", autoCenterLines() );
+
+ config->writeEntry("Search Config Flags", searchFlags());
+
+ config->writeEntry("Command Line", cmdLine());
+
+ config->writeEntry("Default Mark Type", defaultMarkType());
+
+ config->writeEntry("Persistent Selection", persistentSelection());
+
+ config->writeEntry("Text To Search Mode", textToSearchMode());
+}
+
+void KateViewConfig::updateConfig ()
+{
+ if (m_view)
+ {
+ m_view->updateConfig ();
+ return;
+ }
+
+ if (isGlobal())
+ {
+ for (uint z=0; z < KateFactory::self()->views()->count(); z++)
+ {
+ KateFactory::self()->views()->at(z)->updateConfig ();
+ }
+ }
+}
+
+bool KateViewConfig::dynWordWrap () const
+{
+ if (m_dynWordWrapSet || isGlobal())
+ return m_dynWordWrap;
+
+ return s_global->dynWordWrap();
+}
+
+void KateViewConfig::setDynWordWrap (bool wrap)
+{
+ configStart ();
+
+ m_dynWordWrapSet = true;
+ m_dynWordWrap = wrap;
+
+ configEnd ();
+}
+
+int KateViewConfig::dynWordWrapIndicators () const
+{
+ if (m_dynWordWrapIndicatorsSet || isGlobal())
+ return m_dynWordWrapIndicators;
+
+ return s_global->dynWordWrapIndicators();
+}
+
+void KateViewConfig::setDynWordWrapIndicators (int mode)
+{
+ configStart ();
+
+ m_dynWordWrapIndicatorsSet = true;
+ m_dynWordWrapIndicators = kMin(80, kMax(0, mode));
+
+ configEnd ();
+}
+
+int KateViewConfig::dynWordWrapAlignIndent () const
+{
+ if (m_dynWordWrapAlignIndentSet || isGlobal())
+ return m_dynWordWrapAlignIndent;
+
+ return s_global->dynWordWrapAlignIndent();
+}
+
+void KateViewConfig::setDynWordWrapAlignIndent (int indent)
+{
+ configStart ();
+
+ m_dynWordWrapAlignIndentSet = true;
+ m_dynWordWrapAlignIndent = indent;
+
+ configEnd ();
+}
+
+bool KateViewConfig::lineNumbers () const
+{
+ if (m_lineNumbersSet || isGlobal())
+ return m_lineNumbers;
+
+ return s_global->lineNumbers();
+}
+
+void KateViewConfig::setLineNumbers (bool on)
+{
+ configStart ();
+
+ m_lineNumbersSet = true;
+ m_lineNumbers = on;
+
+ configEnd ();
+}
+
+bool KateViewConfig::scrollBarMarks () const
+{
+ if (m_scrollBarMarksSet || isGlobal())
+ return m_scrollBarMarks;
+
+ return s_global->scrollBarMarks();
+}
+
+void KateViewConfig::setScrollBarMarks (bool on)
+{
+ configStart ();
+
+ m_scrollBarMarksSet = true;
+ m_scrollBarMarks = on;
+
+ configEnd ();
+}
+
+bool KateViewConfig::iconBar () const
+{
+ if (m_iconBarSet || isGlobal())
+ return m_iconBar;
+
+ return s_global->iconBar();
+}
+
+void KateViewConfig::setIconBar (bool on)
+{
+ configStart ();
+
+ m_iconBarSet = true;
+ m_iconBar = on;
+
+ configEnd ();
+}
+
+bool KateViewConfig::foldingBar () const
+{
+ if (m_foldingBarSet || isGlobal())
+ return m_foldingBar;
+
+ return s_global->foldingBar();
+}
+
+void KateViewConfig::setFoldingBar (bool on)
+{
+ configStart ();
+
+ m_foldingBarSet = true;
+ m_foldingBar = on;
+
+ configEnd ();
+}
+
+int KateViewConfig::bookmarkSort () const
+{
+ if (m_bookmarkSortSet || isGlobal())
+ return m_bookmarkSort;
+
+ return s_global->bookmarkSort();
+}
+
+void KateViewConfig::setBookmarkSort (int mode)
+{
+ configStart ();
+
+ m_bookmarkSortSet = true;
+ m_bookmarkSort = mode;
+
+ configEnd ();
+}
+
+int KateViewConfig::autoCenterLines () const
+{
+ if (m_autoCenterLinesSet || isGlobal())
+ return m_autoCenterLines;
+
+ return s_global->autoCenterLines();
+}
+
+void KateViewConfig::setAutoCenterLines (int lines)
+{
+ if (lines < 0)
+ return;
+
+ configStart ();
+
+ m_autoCenterLinesSet = true;
+ m_autoCenterLines = lines;
+
+ configEnd ();
+}
+
+long KateViewConfig::searchFlags () const
+{
+ if (m_searchFlagsSet || isGlobal())
+ return m_searchFlags;
+
+ return s_global->searchFlags();
+}
+
+void KateViewConfig::setSearchFlags (long flags)
+ {
+ configStart ();
+
+ m_searchFlagsSet = true;
+ m_searchFlags = flags;
+
+ configEnd ();
+}
+
+bool KateViewConfig::cmdLine () const
+{
+ if (m_cmdLineSet || isGlobal())
+ return m_cmdLine;
+
+ return s_global->cmdLine();
+}
+
+void KateViewConfig::setCmdLine (bool on)
+{
+ configStart ();
+
+ m_cmdLineSet = true;
+ m_cmdLine = on;
+
+ configEnd ();
+}
+
+uint KateViewConfig::defaultMarkType () const
+{
+ if (m_defaultMarkTypeSet || isGlobal())
+ return m_defaultMarkType;
+
+ return s_global->defaultMarkType();
+}
+
+void KateViewConfig::setDefaultMarkType (uint type)
+{
+ configStart ();
+
+ m_defaultMarkTypeSet = true;
+ m_defaultMarkType = type;
+
+ configEnd ();
+}
+
+bool KateViewConfig::persistentSelection () const
+{
+ if (m_persistentSelectionSet || isGlobal())
+ return m_persistentSelection;
+
+ return s_global->persistentSelection();
+}
+
+void KateViewConfig::setPersistentSelection (bool on)
+{
+ configStart ();
+
+ m_persistentSelectionSet = true;
+ m_persistentSelection = on;
+
+ configEnd ();
+}
+
+int KateViewConfig::textToSearchMode () const
+{
+ if (m_textToSearchModeSet || isGlobal())
+ return m_textToSearchMode;
+
+ return s_global->textToSearchMode();
+}
+
+void KateViewConfig::setTextToSearchMode (int mode)
+{
+ configStart ();
+
+ m_textToSearchModeSet = true;
+ m_textToSearchMode = mode;
+
+ configEnd ();
+}
+
+//END
+
+//BEGIN KateRendererConfig
+KateRendererConfig::KateRendererConfig ()
+ :
+ m_font (new KateFontStruct ()),
+ m_lineMarkerColor (KTextEditor::MarkInterface::reservedMarkersCount()),
+ m_schemaSet (true),
+ m_fontSet (true),
+ m_wordWrapMarkerSet (true),
+ m_showIndentationLinesSet (true),
+ m_backgroundColorSet (true),
+ m_selectionColorSet (true),
+ m_highlightedLineColorSet (true),
+ m_highlightedBracketColorSet (true),
+ m_wordWrapMarkerColorSet (true),
+ m_tabMarkerColorSet(true),
+ m_iconBarColorSet (true),
+ m_lineNumberColorSet (true),
+ m_lineMarkerColorSet (m_lineMarkerColor.size()),
+ m_renderer (0)
+{
+ // init bitarray
+ m_lineMarkerColorSet.fill (true);
+
+ s_global = this;
+
+ // init with defaults from config or really hardcoded ones
+ KConfig *config = kapp->config();
+ config->setGroup("Kate Renderer Defaults");
+ readConfig (config);
+}
+
+KateRendererConfig::KateRendererConfig (KateRenderer *renderer)
+ : m_font (0),
+ m_lineMarkerColor (KTextEditor::MarkInterface::reservedMarkersCount()),
+ m_schemaSet (false),
+ m_fontSet (false),
+ m_wordWrapMarkerSet (false),
+ m_showIndentationLinesSet (false),
+ m_backgroundColorSet (false),
+ m_selectionColorSet (false),
+ m_highlightedLineColorSet (false),
+ m_highlightedBracketColorSet (false),
+ m_wordWrapMarkerColorSet (false),
+ m_tabMarkerColorSet(false),
+ m_iconBarColorSet (false),
+ m_lineNumberColorSet (false),
+ m_lineMarkerColorSet (m_lineMarkerColor.size()),
+ m_renderer (renderer)
+{
+ // init bitarray
+ m_lineMarkerColorSet.fill (false);
+}
+
+KateRendererConfig::~KateRendererConfig ()
+{
+ delete m_font;
+}
+
+void KateRendererConfig::readConfig (KConfig *config)
+{
+ configStart ();
+
+ setSchema (KateFactory::self()->schemaManager()->number (config->readEntry("Schema", KateSchemaManager::normalSchema())));
+
+ setWordWrapMarker (config->readBoolEntry("Word Wrap Marker", false ));
+
+ setShowIndentationLines (config->readBoolEntry( "Show Indentation Lines", false));
+
+ configEnd ();
+}
+
+void KateRendererConfig::writeConfig (KConfig *config)
+{
+ config->writeEntry ("Schema", KateFactory::self()->schemaManager()->name(schema()));
+
+ config->writeEntry("Word Wrap Marker", wordWrapMarker() );
+
+ config->writeEntry("Show Indentation Lines", showIndentationLines());
+}
+
+void KateRendererConfig::updateConfig ()
+{
+ if (m_renderer)
+ {
+ m_renderer->updateConfig ();
+ return;
+ }
+
+ if (isGlobal())
+ {
+ for (uint z=0; z < KateFactory::self()->renderers()->count(); z++)
+ {
+ KateFactory::self()->renderers()->at(z)->updateConfig ();
+ }
+ }
+}
+
+uint KateRendererConfig::schema () const
+{
+ if (m_schemaSet || isGlobal())
+ return m_schema;
+
+ return s_global->schema();
+}
+
+void KateRendererConfig::setSchema (uint schema)
+{
+ configStart ();
+ m_schemaSet = true;
+ m_schema = schema;
+ setSchemaInternal( schema );
+ configEnd ();
+}
+
+void KateRendererConfig::reloadSchema()
+{
+ if ( isGlobal() )
+ for ( uint z=0; z < KateFactory::self()->renderers()->count(); z++ )
+ KateFactory::self()->renderers()->at(z)->config()->reloadSchema();
+
+ else if ( m_renderer && m_schemaSet )
+ setSchemaInternal( m_schema );
+}
+
+void KateRendererConfig::setSchemaInternal( int schema )
+{
+ m_schemaSet = true;
+ m_schema = schema;
+
+ KConfig *config (KateFactory::self()->schemaManager()->schema(schema));
+
+ QColor tmp0 (KGlobalSettings::baseColor());
+ QColor tmp1 (KGlobalSettings::highlightColor());
+ QColor tmp2 (KGlobalSettings::alternateBackgroundColor());
+ QColor tmp3 ( "#FFFF99" );
+ QColor tmp4 (tmp2.dark());
+ QColor tmp5 ( KGlobalSettings::textColor() );
+ QColor tmp6 ( "#EAE9E8" );
+ QColor tmp7 ( "#000000" );
+
+ m_backgroundColor = config->readColorEntry("Color Background", &tmp0);
+ m_backgroundColorSet = true;
+ m_selectionColor = config->readColorEntry("Color Selection", &tmp1);
+ m_selectionColorSet = true;
+ m_highlightedLineColor = config->readColorEntry("Color Highlighted Line", &tmp2);
+ m_highlightedLineColorSet = true;
+ m_highlightedBracketColor = config->readColorEntry("Color Highlighted Bracket", &tmp3);
+ m_highlightedBracketColorSet = true;
+ m_wordWrapMarkerColor = config->readColorEntry("Color Word Wrap Marker", &tmp4);
+ m_wordWrapMarkerColorSet = true;
+ m_tabMarkerColor = config->readColorEntry("Color Tab Marker", &tmp5);
+ m_tabMarkerColorSet = true;
+ m_iconBarColor = config->readColorEntry("Color Icon Bar", &tmp6);
+ m_iconBarColorSet = true;
+ m_lineNumberColor = config->readColorEntry("Color Line Number", &tmp7);
+ m_lineNumberColorSet = true;
+
+ // same std colors like in KateDocument::markColor
+ QColor mark[7];
+ mark[0] = Qt::blue;
+ mark[1] = Qt::red;
+ mark[2] = Qt::yellow;
+ mark[3] = Qt::magenta;
+ mark[4] = Qt::gray;
+ mark[5] = Qt::green;
+ mark[6] = Qt::red;
+
+ for (int i = 1; i <= KTextEditor::MarkInterface::reservedMarkersCount(); i++) {
+ QColor col = config->readColorEntry(QString("Color MarkType%1").arg(i), &mark[i - 1]);
+ int index = i-1;
+ m_lineMarkerColorSet[index] = true;
+ m_lineMarkerColor[index] = col;
+ }
+
+ QFont f (KGlobalSettings::fixedFont());
+
+ if (!m_fontSet)
+ {
+ m_fontSet = true;
+ m_font = new KateFontStruct ();
+ }
+
+ m_font->setFont(config->readFontEntry("Font", &f));
+}
+
+KateFontStruct *KateRendererConfig::fontStruct ()
+{
+ if (m_fontSet || isGlobal())
+ return m_font;
+
+ return s_global->fontStruct ();
+}
+
+QFont *KateRendererConfig::font()
+{
+ return &(fontStruct ()->myFont);
+}
+
+KateFontMetrics *KateRendererConfig::fontMetrics()
+{
+ return &(fontStruct ()->myFontMetrics);
+}
+
+void KateRendererConfig::setFont(const QFont &font)
+{
+ configStart ();
+
+ if (!m_fontSet)
+ {
+ m_fontSet = true;
+ m_font = new KateFontStruct ();
+ }
+
+ m_font->setFont(font);
+
+ configEnd ();
+}
+
+bool KateRendererConfig::wordWrapMarker () const
+{
+ if (m_wordWrapMarkerSet || isGlobal())
+ return m_wordWrapMarker;
+
+ return s_global->wordWrapMarker();
+}
+
+void KateRendererConfig::setWordWrapMarker (bool on)
+{
+ configStart ();
+
+ m_wordWrapMarkerSet = true;
+ m_wordWrapMarker = on;
+
+ configEnd ();
+}
+
+const QColor& KateRendererConfig::backgroundColor() const
+{
+ if (m_backgroundColorSet || isGlobal())
+ return m_backgroundColor;
+
+ return s_global->backgroundColor();
+}
+
+void KateRendererConfig::setBackgroundColor (const QColor &col)
+{
+ configStart ();
+
+ m_backgroundColorSet = true;
+ m_backgroundColor = col;
+
+ configEnd ();
+}
+
+const QColor& KateRendererConfig::selectionColor() const
+{
+ if (m_selectionColorSet || isGlobal())
+ return m_selectionColor;
+
+ return s_global->selectionColor();
+}
+
+void KateRendererConfig::setSelectionColor (const QColor &col)
+{
+ configStart ();
+
+ m_selectionColorSet = true;
+ m_selectionColor = col;
+
+ configEnd ();
+}
+
+const QColor& KateRendererConfig::highlightedLineColor() const
+{
+ if (m_highlightedLineColorSet || isGlobal())
+ return m_highlightedLineColor;
+
+ return s_global->highlightedLineColor();
+}
+
+void KateRendererConfig::setHighlightedLineColor (const QColor &col)
+{
+ configStart ();
+
+ m_highlightedLineColorSet = true;
+ m_highlightedLineColor = col;
+
+ configEnd ();
+}
+
+const QColor& KateRendererConfig::lineMarkerColor(KTextEditor::MarkInterface::MarkTypes type) const
+{
+ int index = 0;
+ if (type > 0) { while((type >> index++) ^ 1) {} }
+ index -= 1;
+
+ if ( index < 0 || index >= KTextEditor::MarkInterface::reservedMarkersCount() )
+ {
+ static QColor dummy;
+ return dummy;
+ }
+
+ if (m_lineMarkerColorSet[index] || isGlobal())
+ return m_lineMarkerColor[index];
+
+ return s_global->lineMarkerColor( type );
+}
+
+void KateRendererConfig::setLineMarkerColor (const QColor &col, KTextEditor::MarkInterface::MarkTypes type)
+{
+ int index = static_cast<int>( log(static_cast<double>(type)) / log(2.0) );
+ Q_ASSERT( index >= 0 && index < KTextEditor::MarkInterface::reservedMarkersCount() );
+ configStart ();
+
+ m_lineMarkerColorSet[index] = true;
+ m_lineMarkerColor[index] = col;
+
+ configEnd ();
+}
+
+const QColor& KateRendererConfig::highlightedBracketColor() const
+{
+ if (m_highlightedBracketColorSet || isGlobal())
+ return m_highlightedBracketColor;
+
+ return s_global->highlightedBracketColor();
+}
+
+void KateRendererConfig::setHighlightedBracketColor (const QColor &col)
+{
+ configStart ();
+
+ m_highlightedBracketColorSet = true;
+ m_highlightedBracketColor = col;
+
+ configEnd ();
+}
+
+const QColor& KateRendererConfig::wordWrapMarkerColor() const
+{
+ if (m_wordWrapMarkerColorSet || isGlobal())
+ return m_wordWrapMarkerColor;
+
+ return s_global->wordWrapMarkerColor();
+}
+
+void KateRendererConfig::setWordWrapMarkerColor (const QColor &col)
+{
+ configStart ();
+
+ m_wordWrapMarkerColorSet = true;
+ m_wordWrapMarkerColor = col;
+
+ configEnd ();
+}
+
+const QColor& KateRendererConfig::tabMarkerColor() const
+{
+ if (m_tabMarkerColorSet || isGlobal())
+ return m_tabMarkerColor;
+
+ return s_global->tabMarkerColor();
+}
+
+void KateRendererConfig::setTabMarkerColor (const QColor &col)
+{
+ configStart ();
+
+ m_tabMarkerColorSet = true;
+ m_tabMarkerColor = col;
+
+ configEnd ();
+}
+
+const QColor& KateRendererConfig::iconBarColor() const
+{
+ if (m_iconBarColorSet || isGlobal())
+ return m_iconBarColor;
+
+ return s_global->iconBarColor();
+}
+
+void KateRendererConfig::setIconBarColor (const QColor &col)
+{
+ configStart ();
+
+ m_iconBarColorSet = true;
+ m_iconBarColor = col;
+
+ configEnd ();
+}
+
+const QColor& KateRendererConfig::lineNumberColor() const
+{
+ if (m_lineNumberColorSet || isGlobal())
+ return m_lineNumberColor;
+
+ return s_global->lineNumberColor();
+}
+
+void KateRendererConfig::setLineNumberColor (const QColor &col)
+{
+ configStart ();
+
+ m_lineNumberColorSet = true;
+ m_lineNumberColor = col;
+
+ configEnd ();
+}
+
+bool KateRendererConfig::showIndentationLines () const
+{
+ if (m_showIndentationLinesSet || isGlobal())
+ return m_showIndentationLines;
+
+ return s_global->showIndentationLines();
+}
+
+void KateRendererConfig::setShowIndentationLines (bool on)
+{
+ configStart ();
+
+ m_showIndentationLinesSet = true;
+ m_showIndentationLines = on;
+
+ configEnd ();
+}
+
+//END
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/kateconfig.h b/kate/part/kateconfig.h
new file mode 100644
index 000000000..e81234826
--- /dev/null
+++ b/kate/part/kateconfig.h
@@ -0,0 +1,537 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2003 Christoph Cullmann <cullmann@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KATE_CONFIG_H__
+#define __KATE_CONFIG_H__
+
+#include <ktexteditor/markinterface.h>
+
+#include <qbitarray.h>
+#include <qcolor.h>
+#include <qobject.h>
+#include <qvaluevector.h>
+
+class KateView;
+class KateDocument;
+class KateRenderer;
+class KateFontStruct;
+class KateFontMetrics;
+
+class KConfig;
+
+class QFont;
+class QTextCodec;
+
+/**
+ * Base Class for the Kate Config Classes
+ */
+class KateConfig
+{
+ public:
+ /**
+ * Default Constructor
+ */
+ KateConfig ();
+
+ /**
+ * Virtual Destructor
+ */
+ virtual ~KateConfig ();
+
+ public:
+ /**
+ * start some config changes
+ * this method is needed to init some kind of transaction
+ * for config changes, update will only be done once, at
+ * configEnd() call
+ */
+ void configStart ();
+
+ /**
+ * end a config change transaction, update the concerned
+ * documents/views/renderers
+ */
+ void configEnd ();
+
+ protected:
+ /**
+ * do the real update
+ */
+ virtual void updateConfig () = 0;
+
+ private:
+ /**
+ * recursion depth
+ */
+ uint configSessionNumber;
+
+ /**
+ * is a config session running
+ */
+ bool configIsRunning;
+};
+
+class KateDocumentConfig : public KateConfig
+{
+ private:
+ friend class KateFactory;
+
+ /**
+ * only used in KateFactory for the static global fallback !!!
+ */
+ KateDocumentConfig ();
+
+ public:
+ /**
+ * Construct a DocumentConfig
+ */
+ KateDocumentConfig (KateDocument *doc);
+
+ /**
+ * Cu DocumentConfig
+ */
+ ~KateDocumentConfig ();
+
+ inline static KateDocumentConfig *global () { return s_global; }
+
+ inline bool isGlobal () const { return (this == global()); }
+
+ public:
+ /**
+ * Read config from object
+ */
+ void readConfig (KConfig *config);
+
+ /**
+ * Write config to object
+ */
+ void writeConfig (KConfig *config);
+
+ protected:
+ void updateConfig ();
+
+ public:
+ int tabWidth () const;
+ void setTabWidth (int tabWidth);
+
+ int indentationWidth () const;
+ void setIndentationWidth (int indentationWidth);
+
+ enum IndentationMode
+ {
+ imNone = 0,
+ imNormal = 1,
+ imCStyle = 2,
+ imPythonStyle = 3,
+ imXmlStyle = 4,
+ imCSAndS = 5,
+ imVarIndent = 6,
+ imScriptIndent = 7
+ };
+
+ uint indentationMode () const;
+ void setIndentationMode (uint identationMode);
+
+ bool wordWrap () const;
+ void setWordWrap (bool on);
+
+ unsigned int wordWrapAt () const;
+ void setWordWrapAt (unsigned int col);
+
+ uint undoSteps () const;
+ void setUndoSteps ( uint undoSteps );
+
+ bool pageUpDownMovesCursor () const;
+ void setPageUpDownMovesCursor (bool on);
+
+ enum ConfigFlags
+ {
+ cfBackspaceIndents= 0x2,
+ cfWordWrap= 0x4,
+ cfRemoveSpaces = 0x10,
+ cfWrapCursor= 0x20,
+ cfAutoBrackets= 0x40,
+ cfTabIndentsMode = 0x200,
+ cfOvr= 0x1000,
+ cfKeepIndentProfile= 0x8000,
+ cfKeepExtraSpaces= 0x10000,
+ cfTabIndents= 0x80000,
+ cfShowTabs= 0x200000,
+ cfSpaceIndent= 0x400000,
+ cfSmartHome = 0x800000,
+ cfTabInsertsTab = 0x1000000,
+ cfReplaceTabsDyn= 0x2000000,
+ cfRemoveTrailingDyn=0x4000000,
+ cfDoxygenAutoTyping=0x8000000 , // Remove for KDE 4.0 (put in indenters)
+ cfMixedIndent = 0x10000000,
+ cfIndentPastedText = 0x20000000
+ };
+
+ uint configFlags () const;
+ void setConfigFlags (KateDocumentConfig::ConfigFlags flag, bool enable);
+ void setConfigFlags (uint fullFlags);
+
+ const QString &encoding () const;
+ QTextCodec *codec ();
+
+ void setEncoding (const QString &encoding);
+
+ bool isSetEncoding () const;
+
+ enum Eol
+ {
+ eolUnix = 0,
+ eolDos = 1,
+ eolMac = 2
+ };
+
+ int eol () const;
+ QString eolString ();
+
+ void setEol (int mode);
+
+ bool allowEolDetection () const;
+ void setAllowEolDetection (bool on);
+
+ enum BackupFlags
+ {
+ LocalFiles=1,
+ RemoteFiles=2
+ };
+
+ uint backupFlags () const;
+ void setBackupFlags (uint flags);
+
+ const QString &backupPrefix () const;
+ void setBackupPrefix (const QString &prefix);
+
+ const QString &backupSuffix () const;
+ void setBackupSuffix (const QString &suffix);
+
+ bool plugin (uint index) const;
+ void setPlugin (uint index, bool load);
+
+ /**
+ * Should Kate Part search for dir wide config file
+ * and if, how depth?
+ * @return search depth (< 0 no search)
+ */
+ int searchDirConfigDepth () const;
+
+ void setSearchDirConfigDepth (int depth);
+
+ private:
+ int m_tabWidth;
+ int m_indentationWidth;
+ uint m_indentationMode;
+ bool m_wordWrap;
+ int m_wordWrapAt;
+ uint m_undoSteps;
+ bool m_pageUpDownMovesCursor;
+ uint m_configFlags;
+ QString m_encoding;
+ int m_eol;
+ bool m_allowEolDetection;
+ uint m_backupFlags;
+ int m_searchDirConfigDepth;
+ QString m_backupPrefix;
+ QString m_backupSuffix;
+ QBitArray m_plugins;
+
+ bool m_tabWidthSet : 1;
+ bool m_indentationWidthSet : 1;
+ bool m_indentationModeSet : 1;
+ bool m_wordWrapSet : 1;
+ bool m_wordWrapAtSet : 1;
+ bool m_pageUpDownMovesCursorSet : 1;
+ bool m_undoStepsSet : 1;
+ uint m_configFlagsSet;
+ bool m_encodingSet : 1;
+ bool m_eolSet : 1;
+ bool m_allowEolDetectionSet : 1;
+ bool m_backupFlagsSet : 1;
+ bool m_searchDirConfigDepthSet : 1;
+ bool m_backupPrefixSet : 1;
+ bool m_backupSuffixSet : 1;
+ QBitArray m_pluginsSet;
+
+ private:
+ static KateDocumentConfig *s_global;
+ KateDocument *m_doc;
+};
+
+class KateViewConfig : public KateConfig
+{
+ private:
+ friend class KateFactory;
+
+ /**
+ * only used in KateFactory for the static global fallback !!!
+ */
+ KateViewConfig ();
+
+ public:
+ /**
+ * Construct a DocumentConfig
+ */
+ KateViewConfig (KateView *view);
+
+ /**
+ * Cu DocumentConfig
+ */
+ ~KateViewConfig ();
+
+ inline static KateViewConfig *global () { return s_global; }
+
+ inline bool isGlobal () const { return (this == global()); }
+
+ public:
+ /**
+ * Read config from object
+ */
+ void readConfig (KConfig *config);
+
+ /**
+ * Write config to object
+ */
+ void writeConfig (KConfig *config);
+
+ protected:
+ void updateConfig ();
+
+ public:
+ bool dynWordWrap () const;
+ void setDynWordWrap (bool wrap);
+
+ int dynWordWrapIndicators () const;
+ void setDynWordWrapIndicators (int mode);
+
+ int dynWordWrapAlignIndent () const;
+ void setDynWordWrapAlignIndent (int indent);
+
+ bool lineNumbers () const;
+ void setLineNumbers (bool on);
+
+ bool scrollBarMarks () const;
+ void setScrollBarMarks (bool on);
+
+ bool iconBar () const;
+ void setIconBar (bool on);
+
+ bool foldingBar () const;
+ void setFoldingBar (bool on);
+
+ int bookmarkSort () const;
+ void setBookmarkSort (int mode);
+
+ int autoCenterLines() const;
+ void setAutoCenterLines (int lines);
+
+ long searchFlags () const;
+ void setSearchFlags (long flags);
+
+ bool cmdLine () const;
+ void setCmdLine (bool on);
+
+ uint defaultMarkType () const;
+ void setDefaultMarkType (uint type);
+
+ bool persistentSelection () const;
+ void setPersistentSelection (bool on);
+
+ enum TextToSearch
+ {
+ Nowhere = 0,
+ SelectionOnly = 1,
+ SelectionWord = 2,
+ WordOnly = 3,
+ WordSelection = 4
+ };
+
+ int textToSearchMode () const;
+ void setTextToSearchMode (int mode);
+
+ private:
+ bool m_dynWordWrap;
+ int m_dynWordWrapIndicators;
+ int m_dynWordWrapAlignIndent;
+ bool m_lineNumbers;
+ bool m_scrollBarMarks;
+ bool m_iconBar;
+ bool m_foldingBar;
+ int m_bookmarkSort;
+ int m_autoCenterLines;
+ long m_searchFlags;
+ bool m_cmdLine;
+ uint m_defaultMarkType;
+ bool m_persistentSelection;
+ int m_textToSearchMode;
+
+ bool m_dynWordWrapSet : 1;
+ bool m_dynWordWrapIndicatorsSet : 1;
+ bool m_dynWordWrapAlignIndentSet : 1;
+ bool m_lineNumbersSet : 1;
+ bool m_scrollBarMarksSet : 1;
+ bool m_iconBarSet : 1;
+ bool m_foldingBarSet : 1;
+ bool m_bookmarkSortSet : 1;
+ bool m_autoCenterLinesSet : 1;
+ bool m_searchFlagsSet : 1;
+ bool m_cmdLineSet : 1;
+ bool m_defaultMarkTypeSet : 1;
+ bool m_persistentSelectionSet : 1;
+ bool m_textToSearchModeSet : 1;
+
+ private:
+ static KateViewConfig *s_global;
+ KateView *m_view;
+};
+
+class KateRendererConfig : public KateConfig
+{
+ private:
+ friend class KateFactory;
+
+ /**
+ * only used in KateFactory for the static global fallback !!!
+ */
+ KateRendererConfig ();
+
+
+ public:
+ /**
+ * Construct a DocumentConfig
+ */
+ KateRendererConfig (KateRenderer *renderer);
+
+ /**
+ * Cu DocumentConfig
+ */
+ ~KateRendererConfig ();
+
+ inline static KateRendererConfig *global () { return s_global; }
+
+ inline bool isGlobal () const { return (this == global()); }
+
+ public:
+ /**
+ * Read config from object
+ */
+ void readConfig (KConfig *config);
+
+ /**
+ * Write config to object
+ */
+ void writeConfig (KConfig *config);
+
+ protected:
+ void updateConfig ();
+
+ public:
+ uint schema () const;
+ void setSchema (uint schema);
+ /**
+ * Reload the schema from the schema manager.
+ * For the global instance, have all other instances reload.
+ * Used by the schema config page to apply changes.
+ */
+ void reloadSchema();
+
+ KateFontStruct *fontStruct ();
+ QFont *font();
+ KateFontMetrics *fontMetrics();
+
+ void setFont(const QFont &font);
+
+ bool wordWrapMarker () const;
+ void setWordWrapMarker (bool on);
+
+ const QColor& backgroundColor() const;
+ void setBackgroundColor (const QColor &col);
+
+ const QColor& selectionColor() const;
+ void setSelectionColor (const QColor &col);
+
+ const QColor& highlightedLineColor() const;
+ void setHighlightedLineColor (const QColor &col);
+
+ const QColor& lineMarkerColor(KTextEditor::MarkInterface::MarkTypes type = KTextEditor::MarkInterface::markType01) const; // markType01 == Bookmark
+ void setLineMarkerColor (const QColor &col, KTextEditor::MarkInterface::MarkTypes type = KTextEditor::MarkInterface::markType01);
+
+ const QColor& highlightedBracketColor() const;
+ void setHighlightedBracketColor (const QColor &col);
+
+ const QColor& wordWrapMarkerColor() const;
+ void setWordWrapMarkerColor (const QColor &col);
+
+ const QColor& tabMarkerColor() const;
+ void setTabMarkerColor (const QColor &col);
+
+ const QColor& iconBarColor() const;
+ void setIconBarColor (const QColor &col);
+
+ // the line number color is used for the line numbers on the left bar and
+ // for vertical separator lines and for code folding lines.
+ const QColor& lineNumberColor() const;
+ void setLineNumberColor (const QColor &col);
+
+ bool showIndentationLines () const;
+ void setShowIndentationLines (bool on);
+
+ private:
+ /**
+ * Read the schema properties from the config file.
+ */
+ void setSchemaInternal(int schema);
+
+ uint m_schema;
+ KateFontStruct *m_font;
+ bool m_wordWrapMarker;
+ bool m_showIndentationLines;
+ QColor m_backgroundColor;
+ QColor m_selectionColor;
+ QColor m_highlightedLineColor;
+ QColor m_highlightedBracketColor;
+ QColor m_wordWrapMarkerColor;
+ QColor m_tabMarkerColor;
+ QColor m_iconBarColor;
+ QColor m_lineNumberColor;
+ QValueVector<QColor> m_lineMarkerColor;
+
+ bool m_schemaSet : 1;
+ bool m_fontSet : 1;
+ bool m_wordWrapMarkerSet : 1;
+ bool m_showIndentationLinesSet : 1;
+ bool m_backgroundColorSet : 1;
+ bool m_selectionColorSet : 1;
+ bool m_highlightedLineColorSet : 1;
+ bool m_highlightedBracketColorSet : 1;
+ bool m_wordWrapMarkerColorSet : 1;
+ bool m_tabMarkerColorSet : 1;
+ bool m_iconBarColorSet : 1;
+ bool m_lineNumberColorSet : 1;
+ QBitArray m_lineMarkerColorSet;
+
+ private:
+ static KateRendererConfig *s_global;
+ KateRenderer *m_renderer;
+};
+
+#endif
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katecursor.cpp b/kate/part/katecursor.cpp
new file mode 100644
index 000000000..a67b7e475
--- /dev/null
+++ b/kate/part/katecursor.cpp
@@ -0,0 +1,192 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2002 Christian Couder <christian@kdevelop.org>
+ Copyright (C) 2001, 2003 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
+ Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "katecursor.h"
+
+#include "katedocument.h"
+#include "katetextline.h"
+
+//
+// KateDocCursor implementation
+//
+
+KateDocCursor::KateDocCursor(KateDocument *doc) : KateTextCursor(), m_doc(doc)
+{
+}
+
+KateDocCursor::KateDocCursor(int line, int col, KateDocument *doc)
+ : KateTextCursor(line, col), m_doc(doc)
+{
+}
+
+bool KateDocCursor::validPosition(uint line, uint col)
+{
+ return line < m_doc->numLines() && (int)col <= m_doc->lineLength(line);
+}
+
+bool KateDocCursor::validPosition()
+{
+ return validPosition(line(), col());
+}
+
+void KateDocCursor::position(uint *pline, uint *pcol) const
+{
+ if (pline)
+ *pline = (uint)line();
+
+ if (pcol)
+ *pcol = (uint)col();
+}
+
+bool KateDocCursor::setPosition(uint line, uint col)
+{
+ bool ok = validPosition(line, col);
+
+ if(ok)
+ setPos(line, col);
+
+ return ok;
+}
+
+bool KateDocCursor::gotoNextLine()
+{
+ bool ok = (line() + 1 < (int)m_doc->numLines());
+
+ if (ok) {
+ m_line++;
+ m_col = 0;
+ }
+
+ return ok;
+}
+
+bool KateDocCursor::gotoPreviousLine()
+{
+ bool ok = (line() > 0);
+
+ if (ok) {
+ m_line--;
+ m_col = 0;
+ }
+
+ return ok;
+}
+
+bool KateDocCursor::gotoEndOfNextLine()
+{
+ bool ok = gotoNextLine();
+ if(ok)
+ m_col = m_doc->lineLength(line());
+
+ return ok;
+}
+
+bool KateDocCursor::gotoEndOfPreviousLine()
+{
+ bool ok = gotoPreviousLine();
+ if(ok)
+ m_col = m_doc->lineLength(line());
+
+ return ok;
+}
+
+int KateDocCursor::nbCharsOnLineAfter()
+{
+ return ((int)m_doc->lineLength(line()) - col());
+}
+
+bool KateDocCursor::moveForward(uint nbChar)
+{
+ int nbCharLeft = nbChar - nbCharsOnLineAfter();
+
+ if(nbCharLeft > 0) {
+ return gotoNextLine() && moveForward((uint)nbCharLeft);
+ } else {
+ m_col += nbChar;
+ return true;
+ }
+}
+
+bool KateDocCursor::moveBackward(uint nbChar)
+{
+ int nbCharLeft = nbChar - m_col;
+ if(nbCharLeft > 0) {
+ return gotoEndOfPreviousLine() && moveBackward((uint)nbCharLeft);
+ } else {
+ m_col -= nbChar;
+ return true;
+ }
+}
+
+bool KateDocCursor::insertText(const QString& s)
+{
+ return m_doc->insertText(line(), col(), s);
+}
+
+bool KateDocCursor::removeText(uint nbChar)
+{
+ // Get a cursor at the end of the removed area
+ KateDocCursor endCursor = *this;
+ endCursor.moveForward(nbChar);
+
+ // Remove the text
+ return m_doc->removeText((uint)line(), (uint)col(),
+ (uint)endCursor.line(), (uint)endCursor.col());
+}
+
+QChar KateDocCursor::currentChar() const
+{
+ return m_doc->plainKateTextLine(line())->getChar(col());
+}
+
+uchar KateDocCursor::currentAttrib() const
+{
+ return m_doc->plainKateTextLine(line())->attribute(col());
+}
+
+bool KateDocCursor::nextNonSpaceChar()
+{
+ for(; m_line < (int)m_doc->numLines(); m_line++) {
+ m_col = m_doc->plainKateTextLine(line())->nextNonSpaceChar(col());
+ if(m_col != -1)
+ return true; // Next non-space char found
+ m_col = 0;
+ }
+ // No non-space char found
+ setPos(-1, -1);
+ return false;
+}
+
+bool KateDocCursor::previousNonSpaceChar()
+{
+ while (true) {
+ m_col = m_doc->plainKateTextLine(line())->previousNonSpaceChar(col());
+ if(m_col != -1) return true; // Previous non-space char found
+ if(m_line == 0) return false;
+ --m_line;
+ m_col = m_doc->plainKateTextLine(m_line)->length();
+ }
+ // No non-space char found
+ setPos(-1, -1);
+ return false;
+}
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katecursor.h b/kate/part/katecursor.h
new file mode 100644
index 000000000..20a86d5d9
--- /dev/null
+++ b/kate/part/katecursor.h
@@ -0,0 +1,250 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2002 Christian Couder <christian@kdevelop.org>
+ Copyright (C) 2001, 2003 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
+ Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef kate_cursor_h
+#define kate_cursor_h
+
+#include "../interfaces/document.h"
+#include <kdebug.h>
+
+class KateDocument;
+
+/**
+ Simple cursor class with no document pointer.
+*/
+class KateTextCursor
+{
+ public:
+ KateTextCursor() : m_line(0), m_col(0) {};
+ KateTextCursor(int line, int col) : m_line(line), m_col(col) {};
+ virtual ~KateTextCursor () {};
+
+ friend bool operator==(const KateTextCursor& c1, const KateTextCursor& c2)
+ { return c1.m_line == c2.m_line && c1.m_col == c2.m_col; }
+
+ friend bool operator!=(const KateTextCursor& c1, const KateTextCursor& c2)
+ { return !(c1 == c2); }
+
+ friend bool operator>(const KateTextCursor& c1, const KateTextCursor& c2)
+ { return c1.m_line > c2.m_line || (c1.m_line == c2.m_line && c1.m_col > c2.m_col); }
+
+ friend bool operator>=(const KateTextCursor& c1, const KateTextCursor& c2)
+ { return c1.m_line > c2.m_line || (c1.m_line == c2.m_line && c1.m_col >= c2.m_col); }
+
+ friend bool operator<(const KateTextCursor& c1, const KateTextCursor& c2)
+ { return !(c1 >= c2); }
+
+ friend bool operator<=(const KateTextCursor& c1, const KateTextCursor& c2)
+ { return !(c1 > c2); }
+
+ friend kdbgstream& operator<<(kdbgstream& stream, const KateTextCursor& c) {
+ stream << c.m_line << "," << c.m_col;
+ return stream;
+ }
+
+#ifndef Q_WS_WIN //not needed
+ friend void qSwap(KateTextCursor & c1, KateTextCursor & c2) {
+ KateTextCursor tmp = c1;
+ c1 = c2;
+ c2 = tmp;
+ }
+#endif
+
+ inline void pos(int *pline, int *pcol) const {
+ if(pline) *pline = m_line;
+ if(pcol) *pcol = m_col;
+ }
+
+ inline int line() const { return m_line; };
+ inline int col() const { return m_col; };
+
+ virtual void setLine(int line) { m_line = line; };
+ virtual void setCol(int col) { m_col = col; };
+ virtual void setPos(const KateTextCursor& pos) { m_line = pos.line(); m_col = pos.col(); };
+ virtual void setPos(int line, int col) { m_line = line; m_col = col; };
+
+ protected:
+ int m_line;
+ int m_col;
+};
+
+/**
+ Cursor class with a pointer to its document.
+*/
+class KateDocCursor : public KateTextCursor
+{
+ public:
+ KateDocCursor(KateDocument *doc);
+ KateDocCursor(int line, int col, KateDocument *doc);
+ virtual ~KateDocCursor() {};
+
+ bool validPosition(uint line, uint col);
+ bool validPosition();
+
+ bool gotoNextLine();
+ bool gotoPreviousLine();
+ bool gotoEndOfNextLine();
+ bool gotoEndOfPreviousLine();
+
+ int nbCharsOnLineAfter();
+ bool moveForward(uint nbChar);
+ bool moveBackward(uint nbChar);
+
+ // KTextEditor::Cursor interface
+ void position(uint *line, uint *col) const;
+ bool setPosition(uint line, uint col);
+ bool insertText(const QString& text);
+ bool removeText(uint numberOfCharacters);
+ QChar currentChar() const;
+
+ uchar currentAttrib() const;
+
+ /**
+ Find the position (line and col) of the next char
+ that is not a space. If found KateDocCursor points to the
+ found character. Otherwise to a invalid Position such that
+ validPosition() returns false.
+ @return True if the specified or a following character is not a space
+ Otherwise false.
+ */
+ bool nextNonSpaceChar();
+
+ /**
+ Find the position (line and col) of the previous char
+ that is not a space. If found KateDocCursor points to the
+ found character. Otherwise to a invalid Position such that
+ validPosition() returns false.
+ @return True if the specified or a preceding character is not a space
+ Otherwise false.
+ */
+ bool previousNonSpaceChar();
+
+ protected:
+ KateDocument *m_doc;
+};
+
+class KateRange
+{
+ public:
+ KateRange () {};
+ virtual ~KateRange () {};
+
+ virtual bool isValid() const = 0;
+ virtual KateTextCursor& start() = 0;
+ virtual KateTextCursor& end() = 0;
+ virtual const KateTextCursor& start() const = 0;
+ virtual const KateTextCursor& end() const = 0;
+};
+
+class KateTextRange : public KateRange
+{
+ public:
+ KateTextRange()
+ : m_valid(false)
+ {
+ };
+
+ KateTextRange(int startline, int startcol, int endline, int endcol)
+ : m_start(startline, startcol)
+ , m_end(endline, endcol)
+ , m_valid(true)
+ {
+ normalize();
+ };
+
+ KateTextRange(const KateTextCursor& start, const KateTextCursor& end)
+ : m_start(start)
+ , m_end(end)
+ , m_valid(true)
+ {
+ normalize();
+ };
+
+ virtual ~KateTextRange () {};
+
+ virtual bool isValid() const { return m_valid; };
+ void setValid(bool valid) {
+ m_valid = valid;
+ if( valid )
+ normalize();
+ };
+
+ virtual KateTextCursor& start() { return m_start; };
+ virtual KateTextCursor& end() { return m_end; };
+ virtual const KateTextCursor& start() const { return m_start; };
+ virtual const KateTextCursor& end() const { return m_end; };
+
+ /* if range is not valid, the result is undefined
+ if cursor is before start -1 is returned, if cursor is within range 0 is returned if cursor is after end 1 is returned*/
+ inline int cursorInRange(const KateTextCursor &cursor) const {
+ return ((cursor<m_start)?(-1):((cursor>m_end)?1:0));
+ }
+
+ inline void normalize() {
+ if( m_start > m_end )
+ qSwap(m_start, m_end);
+ }
+
+ protected:
+ KateTextCursor m_start, m_end;
+ bool m_valid;
+};
+
+
+class KateBracketRange : public KateTextRange
+{
+ public:
+ KateBracketRange()
+ : KateTextRange()
+ , m_minIndent(0)
+ {
+ };
+
+ KateBracketRange(int startline, int startcol, int endline, int endcol, int minIndent)
+ : KateTextRange(startline, startcol, endline, endcol)
+ , m_minIndent(minIndent)
+ {
+ };
+
+ KateBracketRange(const KateTextCursor& start, const KateTextCursor& end, int minIndent)
+ : KateTextRange(start, end)
+ , m_minIndent(minIndent)
+ {
+ };
+
+ int getMinIndent() const
+ {
+ return m_minIndent;
+ }
+
+ void setIndentMin(int m)
+ {
+ m_minIndent = m;
+ }
+
+ protected:
+ int m_minIndent;
+};
+
+
+#endif
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katedialogs.cpp b/kate/part/katedialogs.cpp
new file mode 100644
index 000000000..91da42e8f
--- /dev/null
+++ b/kate/part/katedialogs.cpp
@@ -0,0 +1,1740 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2002, 2003 Anders Lund <anders.lund@lund.tdcadsl.dk>
+ Copyright (C) 2003 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
+
+ Based on work of:
+ Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+//BEGIN Includes
+#include "katedialogs.h"
+#include "katedialogs.moc"
+
+#include "kateautoindent.h"
+#include "katebuffer.h"
+#include "kateconfig.h"
+#include "katedocument.h"
+#include "katefactory.h"
+#include "kateschema.h"
+#include "katesyntaxdocument.h"
+#include "kateview.h"
+
+
+#include <ktexteditor/configinterfaceextension.h>
+#include <ktexteditor/plugin.h>
+
+#include <kio/job.h>
+#include <kio/jobclasses.h>
+#include <kio/netaccess.h>
+
+#include <kaccel.h>
+#include <kapplication.h>
+#include <kbuttonbox.h>
+#include <kcharsets.h>
+#include <kcolorbutton.h>
+#include <kcolorcombo.h>
+#include <kcolordialog.h>
+#include <kcombobox.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kfontdialog.h>
+#include <kglobal.h>
+#include <kglobalsettings.h>
+#include <kiconloader.h>
+#include <kkeybutton.h>
+#include <kkeydialog.h>
+#include <klineedit.h>
+#include <klistview.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kmimetypechooser.h>
+#include <knuminput.h>
+#include <kparts/componentfactory.h>
+#include <kpopupmenu.h>
+#include <kprocess.h>
+#include <kprocio.h>
+#include <kregexpeditorinterface.h>
+#include <krun.h>
+#include <kseparator.h>
+#include <kstandarddirs.h>
+#include <ktempfile.h>
+
+#include <qbuttongroup.h>
+#include <qcheckbox.h>
+#include <qcombobox.h>
+#include <qdialog.h>
+#include <qdom.h>
+#include <qfile.h>
+#include <qgrid.h>
+#include <qgroupbox.h>
+#include <qhbox.h>
+#include <qheader.h>
+#include <qhgroupbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qlineedit.h>
+#include <qlistbox.h>
+#include <qlistview.h>
+#include <qmap.h>
+#include <qobjectlist.h>
+#include <qpainter.h>
+#include <qpointarray.h>
+#include <qptrcollection.h>
+#include <qpushbutton.h>
+#include <qradiobutton.h>
+#include <qslider.h>
+#include <qspinbox.h>
+#include <qstringlist.h>
+#include <qtabwidget.h>
+#include <qtextcodec.h>
+#include <qtoolbutton.h>
+#include <qvbox.h>
+#include <qvgroupbox.h>
+#include <qwhatsthis.h>
+#include <qwidgetstack.h>
+
+// trailing slash is important
+#define HLDOWNLOADPATH "http://kate.kde.org/syntax/"
+
+//END
+
+//BEGIN KateConfigPage
+KateConfigPage::KateConfigPage ( QWidget *parent, const char *name )
+ : Kate::ConfigPage (parent, name)
+ , m_changed (false)
+{
+ connect (this, SIGNAL(changed()), this, SLOT(somethingHasChanged ()));
+}
+
+KateConfigPage::~KateConfigPage ()
+{
+}
+
+void KateConfigPage::somethingHasChanged ()
+{
+ m_changed = true;
+ kdDebug (13000) << "TEST: something changed on the config page: " << this << endl;
+}
+//END KateConfigPage
+
+//BEGIN KateIndentConfigTab
+const int KateIndentConfigTab::flags[] = {
+ KateDocument::cfSpaceIndent,
+ KateDocument::cfKeepIndentProfile,
+ KateDocument::cfKeepExtraSpaces,
+ KateDocument::cfTabIndents,
+ KateDocument::cfBackspaceIndents,
+ KateDocumentConfig::cfDoxygenAutoTyping,
+ KateDocumentConfig::cfMixedIndent,
+ KateDocumentConfig::cfIndentPastedText
+};
+
+KateIndentConfigTab::KateIndentConfigTab(QWidget *parent)
+ : KateConfigPage(parent)
+{
+ QVBoxLayout *layout = new QVBoxLayout(this, 0, KDialog::spacingHint() );
+ int configFlags = KateDocumentConfig::global()->configFlags();
+
+ QVGroupBox *gbAuto = new QVGroupBox(i18n("Automatic Indentation"), this);
+
+ QHBox *indentLayout = new QHBox(gbAuto);
+ indentLayout->setSpacing(KDialog::spacingHint());
+ QLabel *indentLabel = new QLabel(i18n("&Indentation mode:"), indentLayout);
+ m_indentMode = new KComboBox (indentLayout);
+ m_indentMode->insertStringList (KateAutoIndent::listModes());
+ indentLabel->setBuddy(m_indentMode);
+ m_configPage = new QPushButton(SmallIconSet("configure"), i18n("Configure..."), indentLayout);
+
+ opt[5] = new QCheckBox(i18n("Insert leading Doxygen \"*\" when typing"), gbAuto);
+ opt[7] = new QCheckBox(i18n("Adjust indentation of code pasted from the clipboard"), gbAuto);
+
+ QVGroupBox *gbSpaces = new QVGroupBox(i18n("Indentation with Spaces"), this);
+ QVBox *spaceLayout = new QVBox(gbSpaces);
+ opt[0] = new QCheckBox(i18n("Use &spaces instead of tabs to indent"), spaceLayout );
+ opt[6] = new QCheckBox(i18n("Emacs style mixed mode"), spaceLayout);
+
+ indentationWidth = new KIntNumInput(KateDocumentConfig::global()->indentationWidth(), spaceLayout);
+ indentationWidth->setRange(1, 16, 1, false);
+ indentationWidth->setLabel(i18n("Number of spaces:"), AlignVCenter);
+
+ opt[1] = new QCheckBox(i18n("Keep indent &profile"), this);
+ opt[2] = new QCheckBox(i18n("&Keep extra spaces"), this);
+
+ QVGroupBox *keys = new QVGroupBox(i18n("Keys to Use"), this);
+ opt[3] = new QCheckBox(i18n("&Tab key indents"), keys);
+ opt[4] = new QCheckBox(i18n("&Backspace key indents"), keys);
+
+ QRadioButton *rb1, *rb2, *rb3;
+ m_tabs = new QButtonGroup( 1, Qt::Horizontal, i18n("Tab Key Mode if Nothing Selected"), this );
+ m_tabs->setRadioButtonExclusive( true );
+ m_tabs->insert( rb1=new QRadioButton( i18n("Insert indent &characters"), m_tabs ), 0 );
+ m_tabs->insert( rb2=new QRadioButton( i18n("I&nsert tab character"), m_tabs ), 1 );
+ m_tabs->insert( rb3=new QRadioButton( i18n("Indent current &line"), m_tabs ), 2 );
+
+ opt[0]->setChecked(configFlags & flags[0]);
+ opt[1]->setChecked(configFlags & flags[1]);
+ opt[2]->setChecked(configFlags & flags[2]);
+ opt[3]->setChecked(configFlags & flags[3]);
+ opt[4]->setChecked(configFlags & flags[4]);
+ opt[5]->setChecked(configFlags & flags[5]);
+ opt[6]->setChecked(configFlags & flags[6]);
+ opt[7]->setChecked(configFlags & flags[7]);
+
+ layout->addWidget(gbAuto);
+ layout->addWidget(gbSpaces);
+ layout->addWidget(opt[1]);
+ layout->addWidget(opt[2]);
+ layout->addWidget(keys);
+ layout->addWidget(m_tabs, 0);
+
+ layout->addStretch();
+
+ // What is this? help
+ QWhatsThis::add(opt[0], i18n(
+ "Check this if you want to indent with spaces rather than tabs."));
+ QWhatsThis::add(opt[2], i18n(
+ "Indentations of more than the selected number of spaces will not be "
+ "shortened."));
+ QWhatsThis::add(opt[3], i18n(
+ "This allows the <b>Tab</b> key to be used to increase the indentation "
+ "level."));
+ QWhatsThis::add(opt[4], i18n(
+ "This allows the <b>Backspace</b> key to be used to decrease the "
+ "indentation level."));
+ QWhatsThis::add(opt[5], i18n(
+ "Automatically inserts a leading \"*\" while typing within a Doxygen "
+ "style comment."));
+ QWhatsThis::add( opt[6], i18n(
+ "Use a mix of tab and space characters for indentation.") );
+ QWhatsThis::add( opt[7], i18n(
+ "If this option is selected, pasted code from the clipboard is indented. "
+ "Triggering the <b>undo</b>-action removes the indentation.") );
+ QWhatsThis::add(indentationWidth, i18n("The number of spaces to indent with."));
+
+ QWhatsThis::add(m_configPage, i18n(
+ "If this button is enabled, additional indenter specific options are "
+ "available and can be configured in an extra dialog.") );
+
+ reload ();
+
+ //
+ // after initial reload, connect the stuff for the changed () signal
+ //
+
+ connect(m_indentMode, SIGNAL(activated(int)), this, SLOT(slotChanged()));
+ connect(m_indentMode, SIGNAL(activated(int)), this, SLOT(indenterSelected(int)));
+
+ connect( opt[0], SIGNAL(toggled(bool)), this, SLOT(somethingToggled()));
+
+ connect( opt[0], SIGNAL( toggled(bool) ), this, SLOT( slotChanged() ) );
+ connect( opt[1], SIGNAL( toggled(bool) ), this, SLOT( slotChanged() ) );
+ connect( opt[2], SIGNAL( toggled(bool) ), this, SLOT( slotChanged() ) );
+ connect( opt[3], SIGNAL( toggled(bool) ), this, SLOT( slotChanged() ) );
+ connect( opt[4], SIGNAL( toggled(bool) ), this, SLOT( slotChanged() ) );
+ connect( opt[5], SIGNAL( toggled(bool) ), this, SLOT( slotChanged() ) );
+ connect( opt[6], SIGNAL( toggled(bool) ), this, SLOT( slotChanged() ) );
+ connect( opt[7], SIGNAL( toggled(bool) ), this, SLOT( slotChanged() ) );
+
+ connect(indentationWidth, SIGNAL(valueChanged(int)), this, SLOT(slotChanged()));
+
+ connect(rb1, SIGNAL(toggled(bool)), this, SLOT(slotChanged()));
+ connect(rb2, SIGNAL(toggled(bool)), this, SLOT(slotChanged()));
+ connect(rb3, SIGNAL(toggled(bool)), this, SLOT(slotChanged()));
+
+ connect(m_configPage, SIGNAL(clicked()), this, SLOT(configPage()));
+}
+
+void KateIndentConfigTab::somethingToggled() {
+ indentationWidth->setEnabled(opt[0]->isChecked());
+ opt[6]->setEnabled(opt[0]->isChecked());
+}
+
+void KateIndentConfigTab::indenterSelected (int index)
+{
+ if (index == KateDocumentConfig::imCStyle || index == KateDocumentConfig::imCSAndS)
+ opt[5]->setEnabled(true);
+ else
+ opt[5]->setEnabled(false);
+
+ m_configPage->setEnabled( KateAutoIndent::hasConfigPage(index) );
+}
+
+void KateIndentConfigTab::configPage()
+{
+ uint index = m_indentMode->currentItem();
+ if ( KateAutoIndent::hasConfigPage(index) )
+ {
+ KDialogBase dlg(this, "indenter_config_dialog", true, i18n("Configure Indenter"),
+ KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Cancel, true);
+
+ QVBox *box = new QVBox(&dlg);
+ box->setSpacing( KDialog::spacingHint() );
+ dlg.setMainWidget(box);
+ new QLabel("<qt><b>" + KateAutoIndent::modeDescription(index) + "</b></qt>", box);
+ new KSeparator(KSeparator::HLine, box);
+
+ IndenterConfigPage* page = KateAutoIndent::configPage(box, index);
+
+ if (!page) return;
+ box->setStretchFactor(page, 1);
+
+ connect( &dlg, SIGNAL(okClicked()), page, SLOT(apply()) );
+
+ dlg.resize(400, 300);
+ dlg.exec();
+ }
+}
+
+void KateIndentConfigTab::apply ()
+{
+ // nothing changed, no need to apply stuff
+ if (!changed())
+ return;
+ m_changed = false;
+
+ KateDocumentConfig::global()->configStart ();
+
+ int configFlags, z;
+
+ configFlags = KateDocumentConfig::global()->configFlags();
+ for (z = 0; z < numFlags; z++) {
+ configFlags &= ~flags[z];
+ if (opt[z]->isChecked()) configFlags |= flags[z];
+ }
+
+ KateDocumentConfig::global()->setConfigFlags(configFlags);
+ KateDocumentConfig::global()->setIndentationWidth(indentationWidth->value());
+
+ KateDocumentConfig::global()->setIndentationMode(m_indentMode->currentItem());
+
+ KateDocumentConfig::global()->setConfigFlags (KateDocumentConfig::cfTabIndentsMode, 2 == m_tabs->id (m_tabs->selected()));
+ KateDocumentConfig::global()->setConfigFlags (KateDocumentConfig::cfTabInsertsTab, 1 == m_tabs->id (m_tabs->selected()));
+
+ KateDocumentConfig::global()->configEnd ();
+}
+
+void KateIndentConfigTab::reload ()
+{
+ if (KateDocumentConfig::global()->configFlags() & KateDocumentConfig::cfTabIndentsMode)
+ m_tabs->setButton (2);
+ else if (KateDocumentConfig::global()->configFlags() & KateDocumentConfig::cfTabInsertsTab)
+ m_tabs->setButton (1);
+ else
+ m_tabs->setButton (0);
+
+ m_indentMode->setCurrentItem (KateDocumentConfig::global()->indentationMode());
+
+ somethingToggled ();
+ indenterSelected (m_indentMode->currentItem());
+}
+//END KateIndentConfigTab
+
+//BEGIN KateSelectConfigTab
+const int KateSelectConfigTab::flags[] = {};
+
+KateSelectConfigTab::KateSelectConfigTab(QWidget *parent)
+ : KateConfigPage(parent)
+{
+ int configFlags = KateDocumentConfig::global()->configFlags();
+
+ QVBoxLayout *layout = new QVBoxLayout(this, 0, KDialog::spacingHint() );
+
+ QVGroupBox *gbCursor = new QVGroupBox(i18n("Text Cursor Movement"), this);
+
+ opt[0] = new QCheckBox(i18n("Smart ho&me and smart end"), gbCursor);
+ opt[0]->setChecked(configFlags & KateDocumentConfig::cfSmartHome);
+ connect(opt[0], SIGNAL(toggled(bool)), this, SLOT(slotChanged()));
+
+ opt[1] = new QCheckBox(i18n("Wrap c&ursor"), gbCursor);
+ opt[1]->setChecked(configFlags & KateDocumentConfig::cfWrapCursor);
+ connect(opt[1], SIGNAL(toggled(bool)), this, SLOT(slotChanged()));
+
+ e6 = new QCheckBox(i18n("&PageUp/PageDown moves cursor"), gbCursor);
+ e6->setChecked(KateDocumentConfig::global()->pageUpDownMovesCursor());
+ connect(e6, SIGNAL(toggled(bool)), this, SLOT(slotChanged()));
+
+ e4 = new KIntNumInput(KateViewConfig::global()->autoCenterLines(), gbCursor);
+ e4->setRange(0, 1000000, 1, false);
+ e4->setLabel(i18n("Autocenter cursor (lines):"), AlignVCenter);
+ connect(e4, SIGNAL(valueChanged(int)), this, SLOT(slotChanged()));
+
+ layout->addWidget(gbCursor);
+
+ QRadioButton *rb1, *rb2;
+
+ m_tabs = new QButtonGroup( 1, Qt::Horizontal, i18n("Selection Mode"), this );
+ layout->add (m_tabs);
+
+ m_tabs->setRadioButtonExclusive( true );
+ m_tabs->insert( rb1=new QRadioButton( i18n("&Normal"), m_tabs ), 0 );
+ m_tabs->insert( rb2=new QRadioButton( i18n("&Persistent"), m_tabs ), 1 );
+
+ layout->addStretch();
+
+ QWhatsThis::add(rb1, i18n(
+ "Selections will be overwritten by typed text and will be lost on "
+ "cursor movement."));
+ QWhatsThis::add(rb2, i18n(
+ "Selections will stay even after cursor movement and typing."));
+
+ QWhatsThis::add(e4, i18n(
+ "Sets the number of lines to maintain visible above and below the "
+ "cursor when possible."));
+
+ QWhatsThis::add(opt[0], i18n(
+ "When selected, pressing the home key will cause the cursor to skip "
+ "whitespace and go to the start of a line's text. "
+ "The same applies for the end key."));
+
+ QWhatsThis::add(opt[1], i18n(
+ "When on, moving the insertion cursor using the <b>Left</b> and "
+ "<b>Right</b> keys will go on to previous/next line at beginning/end of "
+ "the line, similar to most editors.<p>When off, the insertion cursor "
+ "cannot be moved left of the line start, but it can be moved off the "
+ "line end, which can be very handy for programmers."));
+
+ QWhatsThis::add(e6, i18n("Selects whether the PageUp and PageDown keys should alter the vertical position of the cursor relative to the top of the view."));
+
+
+ reload ();
+
+ //
+ // after initial reload, connect the stuff for the changed () signal
+ //
+
+ connect(rb1, SIGNAL(toggled(bool)), this, SLOT(slotChanged()));
+ connect(rb2, SIGNAL(toggled(bool)), this, SLOT(slotChanged()));
+}
+
+void KateSelectConfigTab::apply ()
+{
+ // nothing changed, no need to apply stuff
+ if (!changed())
+ return;
+ m_changed = false;
+
+ KateViewConfig::global()->configStart ();
+ KateDocumentConfig::global()->configStart ();
+
+ int configFlags = KateDocumentConfig::global()->configFlags();
+
+ configFlags &= ~KateDocumentConfig::cfSmartHome;
+ configFlags &= ~KateDocumentConfig::cfWrapCursor;
+
+ if (opt[0]->isChecked()) configFlags |= KateDocumentConfig::cfSmartHome;
+ if (opt[1]->isChecked()) configFlags |= KateDocumentConfig::cfWrapCursor;
+
+ KateDocumentConfig::global()->setConfigFlags(configFlags);
+
+ KateViewConfig::global()->setAutoCenterLines(kMax(0, e4->value()));
+ KateDocumentConfig::global()->setPageUpDownMovesCursor(e6->isChecked());
+
+ KateViewConfig::global()->setPersistentSelection (m_tabs->id (m_tabs->selected()) == 1);
+
+ KateDocumentConfig::global()->configEnd ();
+ KateViewConfig::global()->configEnd ();
+}
+
+void KateSelectConfigTab::reload ()
+{
+ if (KateViewConfig::global()->persistentSelection())
+ m_tabs->setButton (1);
+ else
+ m_tabs->setButton (0);
+}
+//END KateSelectConfigTab
+
+//BEGIN KateEditConfigTab
+const int KateEditConfigTab::flags[] = {KateDocument::cfWordWrap,
+ KateDocument::cfAutoBrackets, KateDocument::cfShowTabs,
+ KateDocumentConfig::cfReplaceTabsDyn, KateDocumentConfig::cfRemoveTrailingDyn};
+
+KateEditConfigTab::KateEditConfigTab(QWidget *parent)
+ : KateConfigPage(parent)
+{
+ QVBoxLayout *mainLayout = new QVBoxLayout(this, 0, KDialog::spacingHint() );
+ int configFlags = KateDocumentConfig::global()->configFlags();
+
+ QVGroupBox *gbWhiteSpace = new QVGroupBox(i18n("Tabulators"), this);
+
+ opt[3] = new QCheckBox( i18n("&Insert spaces instead of tabulators"), gbWhiteSpace );
+ opt[3]->setChecked( configFlags & KateDocumentConfig::cfReplaceTabsDyn );
+ connect( opt[3], SIGNAL(toggled(bool)), this, SLOT(slotChanged()) );
+
+ opt[2] = new QCheckBox(i18n("&Show tabulators"), gbWhiteSpace);
+ opt[2]->setChecked(configFlags & flags[2]);
+ connect(opt[2], SIGNAL(toggled(bool)), this, SLOT(slotChanged()));
+
+ e2 = new KIntNumInput(KateDocumentConfig::global()->tabWidth(), gbWhiteSpace);
+ e2->setRange(1, 16, 1, false);
+ e2->setLabel(i18n("Tab width:"), AlignVCenter);
+ connect(e2, SIGNAL(valueChanged(int)), this, SLOT(slotChanged()));
+
+ mainLayout->addWidget(gbWhiteSpace);
+
+ QVGroupBox *gbWordWrap = new QVGroupBox(i18n("Static Word Wrap"), this);
+
+ opt[0] = new QCheckBox(i18n("Enable static &word wrap"), gbWordWrap);
+ opt[0]->setChecked(KateDocumentConfig::global()->wordWrap());
+ connect(opt[0], SIGNAL(toggled(bool)), this, SLOT(slotChanged()));
+
+ m_wwmarker = new QCheckBox( i18n("&Show static word wrap marker (if applicable)"), gbWordWrap );
+ m_wwmarker->setChecked( KateRendererConfig::global()->wordWrapMarker() );
+ connect(m_wwmarker, SIGNAL(toggled(bool)), this, SLOT(slotChanged()));
+
+ e1 = new KIntNumInput(KateDocumentConfig::global()->wordWrapAt(), gbWordWrap);
+ e1->setRange(20, 200, 1, false);
+ e1->setLabel(i18n("Wrap words at:"), AlignVCenter);
+ connect(e1, SIGNAL(valueChanged(int)), this, SLOT(slotChanged()));
+
+ mainLayout->addWidget(gbWordWrap);
+
+ opt[4] = new QCheckBox( i18n("Remove &trailing spaces"), this );
+ mainLayout->addWidget( opt[4] );
+ opt[4]->setChecked( configFlags & KateDocumentConfig::cfRemoveTrailingDyn );
+ connect( opt[4], SIGNAL(toggled(bool)), this, SLOT(slotChanged()) );
+
+ opt[1] = new QCheckBox(i18n("Auto &brackets"), this);
+ mainLayout->addWidget(opt[1]);
+ opt[1]->setChecked(configFlags & flags[1]);
+ connect(opt[1], SIGNAL(toggled(bool)), this, SLOT(slotChanged()));
+
+ e3 = new KIntNumInput(e2, KateDocumentConfig::global()->undoSteps(), this);
+ e3->setRange(0, 1000000, 1, false);
+ e3->setSpecialValueText( i18n("Unlimited") );
+ e3->setLabel(i18n("Maximum undo steps:"), AlignVCenter);
+ mainLayout->addWidget(e3);
+ connect(e3, SIGNAL(valueChanged(int)), this, SLOT(slotChanged()));
+
+ QHBoxLayout *e5Layout = new QHBoxLayout(mainLayout);
+ QLabel *e5Label = new QLabel(i18n("Smart search t&ext from:"), this);
+ e5Layout->addWidget(e5Label);
+ e5 = new KComboBox (this);
+ e5->insertItem( i18n("Nowhere") );
+ e5->insertItem( i18n("Selection Only") );
+ e5->insertItem( i18n("Selection, then Current Word") );
+ e5->insertItem( i18n("Current Word Only") );
+ e5->insertItem( i18n("Current Word, then Selection") );
+ e5->setCurrentItem(KateViewConfig::global()->textToSearchMode());
+ e5Layout->addWidget(e5);
+ e5Label->setBuddy(e5);
+ connect(e5, SIGNAL(activated(int)), this, SLOT(slotChanged()));
+
+ mainLayout->addStretch();
+
+ // What is this? help
+ QWhatsThis::add(opt[0], i18n(
+ "Automatically start a new line of text when the current line exceeds "
+ "the length specified by the <b>Wrap words at:</b> option."
+ "<p>This option does not wrap existing lines of text - use the <b>Apply "
+ "Static Word Wrap</b> option in the <b>Tools</b> menu for that purpose."
+ "<p>If you want lines to be <i>visually wrapped</i> instead, according "
+ "to the width of the view, enable <b>Dynamic Word Wrap</b> in the "
+ "<b>View Defaults</b> config page."));
+ QWhatsThis::add(e1, i18n(
+ "If the Word Wrap option is selected this entry determines the length "
+ "(in characters) at which the editor will automatically start a new line."));
+ QWhatsThis::add(opt[1], i18n(
+ "When the user types a left bracket ([,(, or {) KateView automatically "
+ "enters the right bracket (}, ), or ]) to the right of the cursor."));
+ QWhatsThis::add(opt[2], i18n(
+ "The editor will display a symbol to indicate the presence of a tab in "
+ "the text."));
+
+ QWhatsThis::add(e3, i18n(
+ "Sets the number of undo/redo steps to record. More steps uses more memory."));
+
+ QString gstfwt = i18n(
+ "This determines where KateView will get the search text from "
+ "(this will be automatically entered into the Find Text dialog): "
+ "<br>"
+ "<ul>"
+ "<li><b>Nowhere:</b> Don't guess the search text."
+ "</li>"
+ "<li><b>Selection Only:</b> Use the current text selection, "
+ "if available."
+ "</li>"
+ "<li><b>Selection, then Current Word:</b> Use the current "
+ "selection if available, otherwise use the current word."
+ "</li>"
+ "<li><b>Current Word Only:</b> Use the word that the cursor "
+ "is currently resting on, if available."
+ "</li>"
+ "<li><b>Current Word, then Selection:</b> Use the current "
+ "word if available, otherwise use the current selection."
+ "</li>"
+ "</ul>"
+ "Note that, in all the above modes, if a search string has "
+ "not been or cannot be determined, then the Find Text Dialog "
+ "will fall back to the last search text.");
+ QWhatsThis::add(e5Label, gstfwt);
+ QWhatsThis::add(e5, gstfwt);
+ QWhatsThis::add( opt[3], i18n(
+ "If this is enabled, the editor will calculate the number of spaces up to "
+ "the next tab position as defined by the tab width, and insert that number "
+ "of spaces instead of a TAB character." ) );
+ QWhatsThis::add( opt[4], i18n(
+ "If this is enabled, the editor will remove any trailing whitespace on "
+ "lines when they are left by the insertion cursor.") );
+ QWhatsThis::add( m_wwmarker, i18n(
+ "<p>If this option is checked, a vertical line will be drawn at the word "
+ "wrap column as defined in the <strong>Editing</strong> properties."
+ "<p>Note that the word wrap marker is only drawn if you use a fixed "
+ "pitch font." ));
+}
+
+void KateEditConfigTab::apply ()
+{
+ // nothing changed, no need to apply stuff
+ if (!changed())
+ return;
+ m_changed = false;
+
+ KateViewConfig::global()->configStart ();
+ KateDocumentConfig::global()->configStart ();
+
+ int configFlags, z;
+
+ configFlags = KateDocumentConfig::global()->configFlags();
+ for (z = 1; z < numFlags; z++) {
+ configFlags &= ~flags[z];
+ if (opt[z]->isChecked()) configFlags |= flags[z];
+ }
+ KateDocumentConfig::global()->setConfigFlags(configFlags);
+
+ KateDocumentConfig::global()->setWordWrapAt(e1->value());
+ KateDocumentConfig::global()->setWordWrap (opt[0]->isChecked());
+ KateDocumentConfig::global()->setTabWidth(e2->value());
+
+ if (e3->value() <= 0)
+ KateDocumentConfig::global()->setUndoSteps(0);
+ else
+ KateDocumentConfig::global()->setUndoSteps(e3->value());
+
+ KateViewConfig::global()->setTextToSearchMode(e5->currentItem());
+
+ KateRendererConfig::global()->setWordWrapMarker (m_wwmarker->isChecked());
+
+ KateDocumentConfig::global()->configEnd ();
+ KateViewConfig::global()->configEnd ();
+}
+
+void KateEditConfigTab::reload ()
+{
+}
+//END KateEditConfigTab
+
+//BEGIN KateViewDefaultsConfig
+KateViewDefaultsConfig::KateViewDefaultsConfig(QWidget *parent)
+ :KateConfigPage(parent)
+{
+ QRadioButton *rb1;
+ QRadioButton *rb2;
+
+ QVBoxLayout *blay=new QVBoxLayout(this,0,KDialog::spacingHint());
+
+ QVGroupBox *gbWordWrap = new QVGroupBox(i18n("Word Wrap"), this);
+
+ m_dynwrap=new QCheckBox(i18n("&Dynamic word wrap"),gbWordWrap);
+
+ QHBox *m_dynwrapIndicatorsLay = new QHBox (gbWordWrap);
+ m_dynwrapIndicatorsLabel = new QLabel( i18n("Dynamic word wrap indicators (if applicable):"), m_dynwrapIndicatorsLay );
+ m_dynwrapIndicatorsCombo = new KComboBox( m_dynwrapIndicatorsLay );
+ m_dynwrapIndicatorsCombo->insertItem( i18n("Off") );
+ m_dynwrapIndicatorsCombo->insertItem( i18n("Follow Line Numbers") );
+ m_dynwrapIndicatorsCombo->insertItem( i18n("Always On") );
+ m_dynwrapIndicatorsLabel->setBuddy(m_dynwrapIndicatorsCombo);
+
+ m_dynwrapAlignLevel = new KIntNumInput(gbWordWrap);
+ m_dynwrapAlignLevel->setLabel(i18n("Vertically align dynamically wrapped lines to indentation depth:"));
+ m_dynwrapAlignLevel->setRange(0, 80, 10);
+ // xgettext:no-c-format
+ m_dynwrapAlignLevel->setSuffix(i18n("% of View Width"));
+ m_dynwrapAlignLevel->setSpecialValueText(i18n("Disabled"));
+
+ blay->addWidget(gbWordWrap);
+
+ QVGroupBox *gbFold = new QVGroupBox(i18n("Code Folding"), this);
+
+ m_folding=new QCheckBox(i18n("Show &folding markers (if available)"), gbFold );
+ m_collapseTopLevel = new QCheckBox( i18n("Collapse toplevel folding nodes"), gbFold );
+ m_collapseTopLevel->hide ();
+
+ blay->addWidget(gbFold);
+
+ QVGroupBox *gbBar = new QVGroupBox(i18n("Borders"), this);
+
+ m_icons=new QCheckBox(i18n("Show &icon border"),gbBar);
+ m_line=new QCheckBox(i18n("Show &line numbers"),gbBar);
+ m_scrollBarMarks=new QCheckBox(i18n("Show &scrollbar marks"),gbBar);
+
+ blay->addWidget(gbBar);
+
+ m_bmSort = new QButtonGroup( 1, Qt::Horizontal, i18n("Sort Bookmarks Menu"), this );
+ m_bmSort->setRadioButtonExclusive( true );
+ m_bmSort->insert( rb1=new QRadioButton( i18n("By &position"), m_bmSort ), 0 );
+ m_bmSort->insert( rb2=new QRadioButton( i18n("By c&reation"), m_bmSort ), 1 );
+
+ blay->addWidget(m_bmSort, 0 );
+
+ m_showIndentLines = new QCheckBox(i18n("Show indentation lines"), this);
+ m_showIndentLines->setChecked(KateRendererConfig::global()->showIndentationLines());
+ blay->addWidget(m_showIndentLines);
+
+ blay->addStretch(1000);
+
+ QWhatsThis::add(m_dynwrap,i18n(
+ "If this option is checked, the text lines will be wrapped at the view "
+ "border on the screen."));
+ QString wtstr = i18n("Choose when the Dynamic Word Wrap Indicators should be displayed");
+ QWhatsThis::add(m_dynwrapIndicatorsLabel, wtstr);
+ QWhatsThis::add(m_dynwrapIndicatorsCombo, wtstr);
+ // xgettext:no-c-format
+ QWhatsThis::add(m_dynwrapAlignLevel, i18n(
+ "<p>Enables the start of dynamically wrapped lines to be aligned "
+ "vertically to the indentation level of the first line. This can help "
+ "to make code and markup more readable.</p><p>Additionally, this allows "
+ "you to set a maximum width of the screen, as a percentage, after which "
+ "dynamically wrapped lines will no longer be vertically aligned. For "
+ "example, at 50%, lines whose indentation levels are deeper than 50% of "
+ "the width of the screen will not have vertical alignment applied to "
+ "subsequent wrapped lines.</p>"));
+ QWhatsThis::add(m_line,i18n(
+ "If this option is checked, every new view will display line numbers "
+ "on the left hand side."));
+ QWhatsThis::add(m_icons,i18n(
+ "If this option is checked, every new view will display an icon border "
+ "on the left hand side.<br><br>The icon border shows bookmark signs, "
+ "for instance."));
+ QWhatsThis::add(m_scrollBarMarks,i18n(
+ "If this option is checked, every new view will show marks on the "
+ "vertical scrollbar.<br><br>These marks will, for instance, show "
+ "bookmarks."));
+ QWhatsThis::add(m_folding,i18n(
+ "If this option is checked, every new view will display marks for code "
+ "folding, if code folding is available."));
+ QWhatsThis::add(m_bmSort,i18n(
+ "Choose how the bookmarks should be ordered in the <b>Bookmarks</b> menu."));
+ QWhatsThis::add(rb1,i18n(
+ "The bookmarks will be ordered by the line numbers they are placed at."));
+ QWhatsThis::add(rb2,i18n(
+ "Each new bookmark will be added to the bottom, independently from "
+ "where it is placed in the document."));
+ QWhatsThis::add(m_showIndentLines, i18n(
+ "If this is enabled, the editor will display vertical lines to help "
+ "identify indent lines.") );
+
+ reload();
+
+ //
+ // after initial reload, connect the stuff for the changed () signal
+ //
+
+ connect(m_dynwrap, SIGNAL(toggled(bool)), this, SLOT(slotChanged()));
+ connect(m_dynwrapIndicatorsCombo, SIGNAL(activated(int)), this, SLOT(slotChanged()));
+ connect(m_dynwrapAlignLevel, SIGNAL(valueChanged(int)), this, SLOT(slotChanged()));
+ connect(m_icons, SIGNAL(toggled(bool)), this, SLOT(slotChanged()));
+ connect(m_scrollBarMarks, SIGNAL(toggled(bool)), this, SLOT(slotChanged()));
+ connect(m_line, SIGNAL(toggled(bool)), this, SLOT(slotChanged()));
+ connect(m_folding, SIGNAL(toggled(bool)), this, SLOT(slotChanged()));
+ connect(m_collapseTopLevel, SIGNAL(toggled(bool)), this, SLOT(slotChanged()) );
+ connect(rb1, SIGNAL(toggled(bool)), this, SLOT(slotChanged()));
+ connect(rb2, SIGNAL(toggled(bool)), this, SLOT(slotChanged()));
+ connect(m_showIndentLines, SIGNAL(toggled(bool)), this, SLOT(slotChanged()));
+}
+
+KateViewDefaultsConfig::~KateViewDefaultsConfig()
+{
+}
+
+void KateViewDefaultsConfig::apply ()
+{
+ // nothing changed, no need to apply stuff
+ if (!changed())
+ return;
+ m_changed = false;
+
+ KateViewConfig::global()->configStart ();
+ KateRendererConfig::global()->configStart ();
+
+ KateViewConfig::global()->setDynWordWrap (m_dynwrap->isChecked());
+ KateViewConfig::global()->setDynWordWrapIndicators (m_dynwrapIndicatorsCombo->currentItem ());
+ KateViewConfig::global()->setDynWordWrapAlignIndent(m_dynwrapAlignLevel->value());
+ KateViewConfig::global()->setLineNumbers (m_line->isChecked());
+ KateViewConfig::global()->setIconBar (m_icons->isChecked());
+ KateViewConfig::global()->setScrollBarMarks (m_scrollBarMarks->isChecked());
+ KateViewConfig::global()->setFoldingBar (m_folding->isChecked());
+ KateViewConfig::global()->setBookmarkSort (m_bmSort->id (m_bmSort->selected()));
+
+ KateRendererConfig::global()->setShowIndentationLines(m_showIndentLines->isChecked());
+
+ KateRendererConfig::global()->configEnd ();
+ KateViewConfig::global()->configEnd ();
+}
+
+void KateViewDefaultsConfig::reload ()
+{
+ m_dynwrap->setChecked(KateViewConfig::global()->dynWordWrap());
+ m_dynwrapIndicatorsCombo->setCurrentItem( KateViewConfig::global()->dynWordWrapIndicators() );
+ m_dynwrapAlignLevel->setValue(KateViewConfig::global()->dynWordWrapAlignIndent());
+ m_line->setChecked(KateViewConfig::global()->lineNumbers());
+ m_icons->setChecked(KateViewConfig::global()->iconBar());
+ m_scrollBarMarks->setChecked(KateViewConfig::global()->scrollBarMarks());
+ m_folding->setChecked(KateViewConfig::global()->foldingBar());
+ m_bmSort->setButton( KateViewConfig::global()->bookmarkSort() );
+ m_showIndentLines->setChecked(KateRendererConfig::global()->showIndentationLines());
+}
+
+void KateViewDefaultsConfig::reset () {;}
+
+void KateViewDefaultsConfig::defaults (){;}
+//END KateViewDefaultsConfig
+
+//BEGIN KateEditKeyConfiguration
+
+KateEditKeyConfiguration::KateEditKeyConfiguration( QWidget* parent, KateDocument* doc )
+ : KateConfigPage( parent )
+{
+ m_doc = doc;
+ m_ready = false;
+}
+
+void KateEditKeyConfiguration::showEvent ( QShowEvent * )
+{
+ if (!m_ready)
+ {
+ (new QVBoxLayout(this))->setAutoAdd(true);
+ KateView* view = (KateView*)m_doc->views().at(0);
+ m_ac = view->editActionCollection();
+ m_keyChooser = new KKeyChooser( m_ac, this, false );
+ connect( m_keyChooser, SIGNAL( keyChange() ), this, SLOT( slotChanged() ) );
+ m_keyChooser->show ();
+
+ m_ready = true;
+ }
+
+ QWidget::show ();
+}
+
+void KateEditKeyConfiguration::apply()
+{
+ if ( ! changed() )
+ return;
+ m_changed = false;
+
+ if (m_ready)
+ {
+ m_keyChooser->commitChanges();
+ m_ac->writeShortcutSettings( "Katepart Shortcuts" );
+ }
+}
+//END KateEditKeyConfiguration
+
+//BEGIN KateSaveConfigTab
+KateSaveConfigTab::KateSaveConfigTab( QWidget *parent )
+ : KateConfigPage( parent )
+{
+ int configFlags = KateDocumentConfig::global()->configFlags();
+ QVBoxLayout *layout = new QVBoxLayout(this, 0, KDialog::spacingHint() );
+
+ QVGroupBox *gbEnc = new QVGroupBox(i18n("File Format"), this);
+ layout->addWidget( gbEnc );
+
+ QHBox *e5Layout = new QHBox(gbEnc);
+ QLabel *e5Label = new QLabel(i18n("&Encoding:"), e5Layout);
+ m_encoding = new KComboBox (e5Layout);
+ e5Label->setBuddy(m_encoding);
+
+ e5Layout = new QHBox(gbEnc);
+ e5Label = new QLabel(i18n("End &of line:"), e5Layout);
+ m_eol = new KComboBox (e5Layout);
+ e5Label->setBuddy(m_eol);
+
+ allowEolDetection = new QCheckBox(i18n("&Automatic end of line detection"), gbEnc);
+
+ m_eol->insertItem (i18n("UNIX"));
+ m_eol->insertItem (i18n("DOS/Windows"));
+ m_eol->insertItem (i18n("Macintosh"));
+
+ QVGroupBox *gbMem = new QVGroupBox(i18n("Memory Usage"), this);
+ layout->addWidget( gbMem );
+
+ e5Layout = new QHBox(gbMem);
+ e5Layout->setSpacing (32);
+ blockCountLabel = new QLabel(i18n("Maximum loaded &blocks per file:"), e5Layout);
+ blockCount = new QSpinBox (4, 512, 4, e5Layout);
+
+ blockCount->setValue (KateBuffer::maxLoadedBlocks());
+ blockCountLabel->setBuddy(blockCount);
+
+ QVGroupBox *gbWhiteSpace = new QVGroupBox(i18n("Automatic Cleanups on Load/Save"), this);
+ layout->addWidget( gbWhiteSpace );
+
+ removeSpaces = new QCheckBox(i18n("Re&move trailing spaces"), gbWhiteSpace);
+ removeSpaces->setChecked(configFlags & KateDocument::cfRemoveSpaces);
+
+ QVGroupBox *dirConfigBox = new QVGroupBox(i18n("Folder Config File"), this);
+ layout->addWidget( dirConfigBox );
+
+ dirSearchDepth = new KIntNumInput(KateDocumentConfig::global()->searchDirConfigDepth(), dirConfigBox);
+ dirSearchDepth->setRange(-1, 64, 1, false);
+ dirSearchDepth->setSpecialValueText( i18n("Do not use config file") );
+ dirSearchDepth->setLabel(i18n("Se&arch depth for config file:"), AlignVCenter);
+
+ QGroupBox *gb = new QGroupBox( 1, Qt::Horizontal, i18n("Backup on Save"), this );
+ layout->addWidget( gb );
+ cbLocalFiles = new QCheckBox( i18n("&Local files"), gb );
+ cbRemoteFiles = new QCheckBox( i18n("&Remote files"), gb );
+
+ QHBox *hbBuPrefix = new QHBox( gb );
+ QLabel *lBuPrefix = new QLabel( i18n("&Prefix:"), hbBuPrefix );
+ leBuPrefix = new QLineEdit( hbBuPrefix );
+ lBuPrefix->setBuddy( leBuPrefix );
+
+ QHBox *hbBuSuffix = new QHBox( gb );
+ QLabel *lBuSuffix = new QLabel( i18n("&Suffix:"), hbBuSuffix );
+ leBuSuffix = new QLineEdit( hbBuSuffix );
+ lBuSuffix->setBuddy( leBuSuffix );
+
+ layout->addStretch();
+
+ QWhatsThis::add(removeSpaces, i18n(
+ "The editor will automatically eliminate extra spaces at the ends of "
+ "lines of text while loading/saving the file."));
+ QWhatsThis::add( gb, i18n(
+ "<p>Backing up on save will cause Kate to copy the disk file to "
+ "'&lt;prefix&gt;&lt;filename&gt;&lt;suffix&gt;' before saving changes."
+ "<p>The suffix defaults to <strong>~</strong> and prefix is empty by default" ) );
+ QWhatsThis::add( allowEolDetection, i18n(
+ "Check this if you want the editor to autodetect the end of line type."
+ "The first found end of line type will be used for the whole file.") );
+ QWhatsThis::add( cbLocalFiles, i18n(
+ "Check this if you want backups of local files when saving") );
+ QWhatsThis::add( cbRemoteFiles, i18n(
+ "Check this if you want backups of remote files when saving") );
+ QWhatsThis::add( leBuPrefix, i18n(
+ "Enter the prefix to prepend to the backup file names" ) );
+ QWhatsThis::add( leBuSuffix, i18n(
+ "Enter the suffix to add to the backup file names" ) );
+ QWhatsThis::add(dirSearchDepth, i18n(
+ "The editor will search the given number of folder levels upwards for .kateconfig file"
+ " and load the settings line from it." ));
+ QWhatsThis::add(blockCount, i18n(
+ "The editor will load given number of blocks (of around 2048 lines) of text into memory;"
+ " if the filesize is bigger than this the other blocks are swapped "
+ " to disk and loaded transparently as-needed.<br>"
+ " This can cause little delays while navigating in the document; a larger block count"
+ " increases the editing speed at the cost of memory. <br>For normal usage, just choose the highest possible"
+ " block count: limit it only if you have problems with the memory usage."));
+
+ reload();
+
+ //
+ // after initial reload, connect the stuff for the changed () signal
+ //
+
+ connect(m_encoding, SIGNAL(activated(int)), this, SLOT(slotChanged()));
+ connect(m_eol, SIGNAL(activated(int)), this, SLOT(slotChanged()));
+ connect( allowEolDetection, SIGNAL( toggled(bool) ), this, SLOT( slotChanged() ) );
+ connect(blockCount, SIGNAL(valueChanged(int)), this, SLOT(slotChanged()));
+ connect(removeSpaces, SIGNAL(toggled(bool)), this, SLOT(slotChanged()));
+ connect( cbLocalFiles, SIGNAL( toggled(bool) ), this, SLOT( slotChanged() ) );
+ connect( cbRemoteFiles, SIGNAL( toggled(bool) ), this, SLOT( slotChanged() ) );
+ connect(dirSearchDepth, SIGNAL(valueChanged(int)), this, SLOT(slotChanged()));
+ connect( leBuPrefix, SIGNAL( textChanged ( const QString & ) ), this, SLOT( slotChanged() ) );
+ connect( leBuSuffix, SIGNAL( textChanged ( const QString & ) ), this, SLOT( slotChanged() ) );
+}
+
+void KateSaveConfigTab::apply()
+{
+ // nothing changed, no need to apply stuff
+ if (!changed())
+ return;
+ m_changed = false;
+
+ KateBuffer::setMaxLoadedBlocks (blockCount->value());
+
+ KateDocumentConfig::global()->configStart ();
+
+ if ( leBuSuffix->text().isEmpty() && leBuPrefix->text().isEmpty() ) {
+ KMessageBox::information(
+ this,
+ i18n("You did not provide a backup suffix or prefix. Using default suffix: '~'"),
+ i18n("No Backup Suffix or Prefix")
+ );
+ leBuSuffix->setText( "~" );
+ }
+
+ uint f( 0 );
+ if ( cbLocalFiles->isChecked() )
+ f |= KateDocumentConfig::LocalFiles;
+ if ( cbRemoteFiles->isChecked() )
+ f |= KateDocumentConfig::RemoteFiles;
+
+ KateDocumentConfig::global()->setBackupFlags(f);
+ KateDocumentConfig::global()->setBackupPrefix(leBuPrefix->text());
+ KateDocumentConfig::global()->setBackupSuffix(leBuSuffix->text());
+
+ KateDocumentConfig::global()->setSearchDirConfigDepth(dirSearchDepth->value());
+
+ int configFlags = KateDocumentConfig::global()->configFlags();
+
+ configFlags &= ~KateDocument::cfRemoveSpaces; // clear flag
+ if (removeSpaces->isChecked()) configFlags |= KateDocument::cfRemoveSpaces; // set flag if checked
+
+ KateDocumentConfig::global()->setConfigFlags(configFlags);
+
+ KateDocumentConfig::global()->setEncoding((m_encoding->currentItem() == 0) ? "" : KGlobal::charsets()->encodingForName(m_encoding->currentText()));
+
+ KateDocumentConfig::global()->setEol(m_eol->currentItem());
+ KateDocumentConfig::global()->setAllowEolDetection(allowEolDetection->isChecked());
+
+ KateDocumentConfig::global()->configEnd ();
+}
+
+void KateSaveConfigTab::reload()
+{
+ // encoding
+ m_encoding->clear ();
+ m_encoding->insertItem (i18n("KDE Default"));
+ m_encoding->setCurrentItem(0);
+ QStringList encodings (KGlobal::charsets()->descriptiveEncodingNames());
+ int insert = 1;
+ for (uint i=0; i < encodings.count(); i++)
+ {
+ bool found = false;
+ QTextCodec *codecForEnc = KGlobal::charsets()->codecForName(KGlobal::charsets()->encodingForName(encodings[i]), found);
+
+ if (found)
+ {
+ m_encoding->insertItem (encodings[i]);
+
+ if ( codecForEnc->name() == KateDocumentConfig::global()->encoding() )
+ {
+ m_encoding->setCurrentItem(insert);
+ }
+
+ insert++;
+ }
+ }
+
+ // eol
+ m_eol->setCurrentItem(KateDocumentConfig::global()->eol());
+ allowEolDetection->setChecked(KateDocumentConfig::global()->allowEolDetection());
+
+ dirSearchDepth->setValue(KateDocumentConfig::global()->searchDirConfigDepth());
+
+ // other stuff
+ uint f ( KateDocumentConfig::global()->backupFlags() );
+ cbLocalFiles->setChecked( f & KateDocumentConfig::LocalFiles );
+ cbRemoteFiles->setChecked( f & KateDocumentConfig::RemoteFiles );
+ leBuPrefix->setText( KateDocumentConfig::global()->backupPrefix() );
+ leBuSuffix->setText( KateDocumentConfig::global()->backupSuffix() );
+}
+
+void KateSaveConfigTab::reset()
+{
+}
+
+void KateSaveConfigTab::defaults()
+{
+ cbLocalFiles->setChecked( true );
+ cbRemoteFiles->setChecked( false );
+ leBuPrefix->setText( "" );
+ leBuSuffix->setText( "~" );
+}
+
+//END KateSaveConfigTab
+
+//BEGIN PluginListItem
+class KatePartPluginListItem : public QCheckListItem
+{
+ public:
+ KatePartPluginListItem(bool checked, uint i, const QString &name, QListView *parent);
+ uint pluginIndex () const { return index; }
+
+ protected:
+ void stateChange(bool);
+
+ private:
+ uint index;
+ bool silentStateChange;
+};
+
+KatePartPluginListItem::KatePartPluginListItem(bool checked, uint i, const QString &name, QListView *parent)
+ : QCheckListItem(parent, name, CheckBox)
+ , index(i)
+ , silentStateChange(false)
+{
+ silentStateChange = true;
+ setOn(checked);
+ silentStateChange = false;
+}
+
+void KatePartPluginListItem::stateChange(bool b)
+{
+ if(!silentStateChange)
+ static_cast<KatePartPluginListView *>(listView())->stateChanged(this, b);
+}
+//END
+
+//BEGIN PluginListView
+KatePartPluginListView::KatePartPluginListView(QWidget *parent, const char *name)
+ : KListView(parent, name)
+{
+}
+
+void KatePartPluginListView::stateChanged(KatePartPluginListItem *item, bool b)
+{
+ emit stateChange(item, b);
+}
+//END
+
+//BEGIN KatePartPluginConfigPage
+KatePartPluginConfigPage::KatePartPluginConfigPage (QWidget *parent) : KateConfigPage (parent, "")
+{
+ // sizemanagment
+ QGridLayout *grid = new QGridLayout( this, 1, 1 );
+ grid->setSpacing( KDialogBase::spacingHint() );
+
+ listView = new KatePartPluginListView(this);
+ listView->addColumn(i18n("Name"));
+ listView->addColumn(i18n("Comment"));
+
+ grid->addWidget( listView, 0, 0);
+
+ for (uint i=0; i<KateFactory::self()->plugins().count(); i++)
+ {
+ KatePartPluginListItem *item = new KatePartPluginListItem(KateDocumentConfig::global()->plugin(i), i, (KateFactory::self()->plugins())[i]->name(), listView);
+ item->setText(0, (KateFactory::self()->plugins())[i]->name());
+ item->setText(1, (KateFactory::self()->plugins())[i]->comment());
+
+ m_items.append (item);
+ }
+
+ // configure button
+
+ btnConfigure = new QPushButton( i18n("Configure..."), this );
+ btnConfigure->setEnabled( false );
+ grid->addWidget( btnConfigure, 1, 0, Qt::AlignRight );
+ connect( btnConfigure, SIGNAL(clicked()), this, SLOT(slotConfigure()) );
+
+ connect( listView, SIGNAL(selectionChanged(QListViewItem*)), this, SLOT(slotCurrentChanged(QListViewItem*)) );
+ connect( listView, SIGNAL(stateChange(KatePartPluginListItem *, bool)),
+ this, SLOT(slotStateChanged(KatePartPluginListItem *, bool)));
+ connect(listView, SIGNAL(stateChange(KatePartPluginListItem *, bool)), this, SLOT(slotChanged()));
+}
+
+KatePartPluginConfigPage::~KatePartPluginConfigPage ()
+{
+}
+
+void KatePartPluginConfigPage::apply ()
+{
+ // nothing changed, no need to apply stuff
+ if (!changed())
+ return;
+ m_changed = false;
+
+ KateDocumentConfig::global()->configStart ();
+
+ for (uint i=0; i < m_items.count(); i++)
+ KateDocumentConfig::global()->setPlugin (m_items.at(i)->pluginIndex(), m_items.at(i)->isOn());
+
+ KateDocumentConfig::global()->configEnd ();
+}
+
+void KatePartPluginConfigPage::slotStateChanged( KatePartPluginListItem *item, bool b )
+{
+ if ( b )
+ slotCurrentChanged( (QListViewItem*)item );
+}
+
+void KatePartPluginConfigPage::slotCurrentChanged( QListViewItem* i )
+{
+ KatePartPluginListItem *item = static_cast<KatePartPluginListItem *>(i);
+ if ( ! item ) return;
+
+ bool b = false;
+ if ( item->isOn() )
+ {
+
+ // load this plugin, and see if it has config pages
+ KTextEditor::Plugin *plugin = KTextEditor::createPlugin(QFile::encodeName((KateFactory::self()->plugins())[item->pluginIndex()]->library()));
+ if ( plugin ) {
+ KTextEditor::ConfigInterfaceExtension *cie = KTextEditor::configInterfaceExtension( plugin );
+ b = ( cie && cie->configPages() );
+ }
+
+ }
+ btnConfigure->setEnabled( b );
+}
+
+void KatePartPluginConfigPage::slotConfigure()
+{
+ KatePartPluginListItem *item = static_cast<KatePartPluginListItem*>(listView->currentItem());
+ KTextEditor::Plugin *plugin =
+ KTextEditor::createPlugin(QFile::encodeName((KateFactory::self()->plugins())[item->pluginIndex()]->library()));
+
+ if ( ! plugin ) return;
+
+ KTextEditor::ConfigInterfaceExtension *cife =
+ KTextEditor::configInterfaceExtension( plugin );
+
+ if ( ! cife )
+ return;
+
+ if ( ! cife->configPages() )
+ return;
+
+ // If we have only one page, we use a simple dialog, else an icon list type
+ KDialogBase::DialogType dt =
+ cife->configPages() > 1 ?
+ KDialogBase::IconList : // still untested
+ KDialogBase::Plain;
+
+ QString name = (KateFactory::self()->plugins())[item->pluginIndex()]->name();
+ KDialogBase *kd = new KDialogBase ( dt,
+ i18n("Configure %1").arg( name ),
+ KDialogBase::Ok | KDialogBase::Cancel | KDialogBase::Help,
+ KDialogBase::Ok,
+ this );
+
+ QPtrList<KTextEditor::ConfigPage> editorPages;
+
+ for (uint i = 0; i < cife->configPages (); i++)
+ {
+ QWidget *page;
+ if ( dt == KDialogBase::IconList )
+ {
+ QStringList path;
+ path.clear();
+ path << cife->configPageName( i );
+ page = kd->addVBoxPage( path, cife->configPageFullName (i),
+ cife->configPagePixmap(i, KIcon::SizeMedium) );
+ }
+ else
+ {
+ page = kd->plainPage();
+ QVBoxLayout *_l = new QVBoxLayout( page );
+ _l->setAutoAdd( true );
+ }
+
+ editorPages.append( cife->configPage( i, page ) );
+ }
+
+ if (kd->exec())
+ {
+
+ for( uint i=0; i<editorPages.count(); i++ )
+ {
+ editorPages.at( i )->apply();
+ }
+ }
+
+ delete kd;
+}
+//END KatePartPluginConfigPage
+
+//BEGIN KateHlConfigPage
+KateHlConfigPage::KateHlConfigPage (QWidget *parent, KateDocument *doc)
+ : KateConfigPage (parent, "")
+ , hlData (0)
+ , m_doc (doc)
+{
+ QVBoxLayout *layout = new QVBoxLayout(this, 0, KDialog::spacingHint() );
+
+ // hl chooser
+ QHBox *hbHl = new QHBox( this );
+ layout->add (hbHl);
+
+ hbHl->setSpacing( KDialog::spacingHint() );
+ QLabel *lHl = new QLabel( i18n("H&ighlight:"), hbHl );
+ hlCombo = new QComboBox( false, hbHl );
+ lHl->setBuddy( hlCombo );
+ connect( hlCombo, SIGNAL(activated(int)),
+ this, SLOT(hlChanged(int)) );
+
+ for( int i = 0; i < KateHlManager::self()->highlights(); i++) {
+ if (KateHlManager::self()->hlSection(i).length() > 0)
+ hlCombo->insertItem(KateHlManager::self()->hlSection(i) + QString ("/") + KateHlManager::self()->hlNameTranslated(i));
+ else
+ hlCombo->insertItem(KateHlManager::self()->hlNameTranslated(i));
+ }
+
+ QGroupBox *gbInfo = new QGroupBox( 1, Qt::Horizontal, i18n("Information"), this );
+ layout->add (gbInfo);
+
+ // author
+ QHBox *hb1 = new QHBox( gbInfo);
+ new QLabel( i18n("Author:"), hb1 );
+ author = new QLabel (hb1);
+ author->setTextFormat (Qt::RichText);
+
+ // license
+ QHBox *hb2 = new QHBox( gbInfo);
+ new QLabel( i18n("License:"), hb2 );
+ license = new QLabel (hb2);
+
+ QGroupBox *gbProps = new QGroupBox( 1, Qt::Horizontal, i18n("Properties"), this );
+ layout->add (gbProps);
+
+ // file & mime types
+ QHBox *hbFE = new QHBox( gbProps);
+ QLabel *lFileExts = new QLabel( i18n("File e&xtensions:"), hbFE );
+ wildcards = new QLineEdit( hbFE );
+ lFileExts->setBuddy( wildcards );
+
+ QHBox *hbMT = new QHBox( gbProps );
+ QLabel *lMimeTypes = new QLabel( i18n("MIME &types:"), hbMT);
+ mimetypes = new QLineEdit( hbMT );
+ lMimeTypes->setBuddy( mimetypes );
+
+ QHBox *hbMT2 = new QHBox( gbProps );
+ QLabel *lprio = new QLabel( i18n("Prio&rity:"), hbMT2);
+ priority = new KIntNumInput( hbMT2 );
+
+ lprio->setBuddy( priority );
+
+ QToolButton *btnMTW = new QToolButton(hbMT);
+ btnMTW->setIconSet(QIconSet(SmallIcon("wizard")));
+ connect(btnMTW, SIGNAL(clicked()), this, SLOT(showMTDlg()));
+
+ // download/new buttons
+ QHBox *hbBtns = new QHBox( this );
+ layout->add (hbBtns);
+
+ ((QBoxLayout*)hbBtns->layout())->addStretch(1); // hmm.
+ hbBtns->setSpacing( KDialog::spacingHint() );
+ QPushButton *btnDl = new QPushButton(i18n("Do&wnload..."), hbBtns);
+ connect( btnDl, SIGNAL(clicked()), this, SLOT(hlDownload()) );
+
+ int currentHl = m_doc ? m_doc->hlMode() : 0;
+ hlCombo->setCurrentItem( currentHl );
+ hlChanged( currentHl );
+
+ QWhatsThis::add( hlCombo, i18n(
+ "Choose a <em>Syntax Highlight mode</em> from this list to view its "
+ "properties below.") );
+ QWhatsThis::add( wildcards, i18n(
+ "The list of file extensions used to determine which files to highlight "
+ "using the current syntax highlight mode.") );
+ QWhatsThis::add( mimetypes, i18n(
+ "The list of Mime Types used to determine which files to highlight "
+ "using the current highlight mode.<p>Click the wizard button on the "
+ "left of the entry field to display the MimeType selection dialog.") );
+ QWhatsThis::add( btnMTW, i18n(
+ "Display a dialog with a list of all available mime types to choose from."
+ "<p>The <strong>File Extensions</strong> entry will automatically be "
+ "edited as well.") );
+ QWhatsThis::add( btnDl, i18n(
+ "Click this button to download new or updated syntax highlight "
+ "descriptions from the Kate website.") );
+
+ layout->addStretch ();
+
+ connect( wildcards, SIGNAL( textChanged ( const QString & ) ), this, SLOT( slotChanged() ) );
+ connect( mimetypes, SIGNAL( textChanged ( const QString & ) ), this, SLOT( slotChanged() ) );
+ connect( priority, SIGNAL( valueChanged ( int ) ), this, SLOT( slotChanged() ) );
+}
+
+KateHlConfigPage::~KateHlConfigPage ()
+{
+}
+
+void KateHlConfigPage::apply ()
+{
+ // nothing changed, no need to apply stuff
+ if (!changed())
+ return;
+ m_changed = false;
+
+ writeback();
+
+ for ( QIntDictIterator<KateHlData> it( hlDataDict ); it.current(); ++it )
+ KateHlManager::self()->getHl( it.currentKey() )->setData( it.current() );
+
+ KateHlManager::self()->getKConfig()->sync ();
+}
+
+void KateHlConfigPage::reload ()
+{
+}
+
+void KateHlConfigPage::hlChanged(int z)
+{
+ writeback();
+
+ KateHighlighting *hl = KateHlManager::self()->getHl( z );
+
+ if (!hl)
+ {
+ hlData = 0;
+ return;
+ }
+
+ if ( !hlDataDict.find( z ) )
+ hlDataDict.insert( z, hl->getData() );
+
+ hlData = hlDataDict.find( z );
+ wildcards->setText(hlData->wildcards);
+ mimetypes->setText(hlData->mimetypes);
+ priority->setValue(hlData->priority);
+
+ // split author string if needed into multiple lines !
+ QStringList l= QStringList::split (QRegExp("[,;]"), hl->author());
+ author->setText (l.join ("<br>"));
+
+ license->setText (hl->license());
+}
+
+void KateHlConfigPage::writeback()
+{
+ if (hlData)
+ {
+ hlData->wildcards = wildcards->text();
+ hlData->mimetypes = mimetypes->text();
+ hlData->priority = priority->value();
+ }
+}
+
+void KateHlConfigPage::hlDownload()
+{
+ KateHlDownloadDialog diag(this,"hlDownload",true);
+ diag.exec();
+}
+
+void KateHlConfigPage::showMTDlg()
+{
+ QString text = i18n("Select the MimeTypes you want highlighted using the '%1' syntax highlight rules.\nPlease note that this will automatically edit the associated file extensions as well.").arg( hlCombo->currentText() );
+ QStringList list = QStringList::split( QRegExp("\\s*;\\s*"), mimetypes->text() );
+ KMimeTypeChooserDialog d( i18n("Select Mime Types"), text, list, "text", this );
+
+ if ( d.exec() == KDialogBase::Accepted ) {
+ // do some checking, warn user if mime types or patterns are removed.
+ // if the lists are empty, and the fields not, warn.
+ wildcards->setText(d.chooser()->patterns().join(";"));
+ mimetypes->setText(d.chooser()->mimeTypes().join(";"));
+ }
+}
+//END KateHlConfigPage
+
+//BEGIN KateHlDownloadDialog
+KateHlDownloadDialog::KateHlDownloadDialog(QWidget *parent, const char *name, bool modal)
+ :KDialogBase(KDialogBase::Swallow, i18n("Highlight Download"), User1|Close, User1, parent, name, modal, true, i18n("&Install"))
+{
+ QVBox* vbox = new QVBox(this);
+ setMainWidget(vbox);
+ vbox->setSpacing(spacingHint());
+ new QLabel(i18n("Select the syntax highlighting files you want to update:"), vbox);
+ list = new QListView(vbox);
+ list->addColumn("");
+ list->addColumn(i18n("Name"));
+ list->addColumn(i18n("Installed"));
+ list->addColumn(i18n("Latest"));
+ list->setSelectionMode(QListView::Multi);
+ list->setAllColumnsShowFocus(true);
+
+ new QLabel(i18n("<b>Note:</b> New versions are selected automatically."), vbox);
+ actionButton (User1)->setIconSet(SmallIconSet("ok"));
+
+ transferJob = KIO::get(
+ KURL(QString(HLDOWNLOADPATH)
+ + QString("update-")
+ + QString(KATEPART_VERSION)
+ + QString(".xml")), true, true );
+ connect(transferJob, SIGNAL(data(KIO::Job *, const QByteArray &)),
+ this, SLOT(listDataReceived(KIO::Job *, const QByteArray &)));
+// void data( KIO::Job *, const QByteArray &data);
+ resize(450, 400);
+}
+
+KateHlDownloadDialog::~KateHlDownloadDialog(){}
+
+void KateHlDownloadDialog::listDataReceived(KIO::Job *, const QByteArray &data)
+{
+ if (!transferJob || transferJob->isErrorPage())
+ {
+ actionButton(User1)->setEnabled(false);
+ return;
+ }
+
+ listData+=QString(data);
+ kdDebug(13000)<<QString("CurrentListData: ")<<listData<<endl<<endl;
+ kdDebug(13000)<<QString("Data length: %1").arg(data.size())<<endl;
+ kdDebug(13000)<<QString("listData length: %1").arg(listData.length())<<endl;
+ if (data.size()==0)
+ {
+ if (listData.length()>0)
+ {
+ QString installedVersion;
+ KateHlManager *hlm=KateHlManager::self();
+ QDomDocument doc;
+ doc.setContent(listData);
+ QDomElement DocElem=doc.documentElement();
+ QDomNode n=DocElem.firstChild();
+ KateHighlighting *hl = 0;
+
+ if (n.isNull()) kdDebug(13000)<<"There is no usable childnode"<<endl;
+ while (!n.isNull())
+ {
+ installedVersion=" --";
+
+ QDomElement e=n.toElement();
+ if (!e.isNull())
+ kdDebug(13000)<<QString("NAME: ")<<e.tagName()<<QString(" - ")<<e.attribute("name")<<endl;
+ n=n.nextSibling();
+
+ QString Name=e.attribute("name");
+
+ for (int i=0;i<hlm->highlights();i++)
+ {
+ hl=hlm->getHl(i);
+ if (hl && hl->name()==Name)
+ {
+ installedVersion=" "+hl->version();
+ break;
+ }
+ else hl = 0;
+ }
+
+ // autoselect entry if new or updated.
+ QListViewItem* entry = new QListViewItem(
+ list, "", e.attribute("name"), installedVersion,
+ e.attribute("version"),e.attribute("url"));
+ if (!hl || hl->version() < e.attribute("version"))
+ {
+ entry->setSelected(true);
+ entry->setPixmap(0, SmallIcon(("knewstuff")));
+ }
+ }
+ }
+ }
+}
+
+void KateHlDownloadDialog::slotUser1()
+{
+ QString destdir=KGlobal::dirs()->saveLocation("data","katepart/syntax/");
+ for (QListViewItem *it=list->firstChild();it;it=it->nextSibling())
+ {
+ if (list->isSelected(it))
+ {
+ KURL src(it->text(4));
+ QString filename=src.fileName(false);
+ QString dest = destdir+filename;
+
+ KIO::NetAccess::download(src,dest, this);
+ }
+ }
+
+ // update Config !!
+ KateSyntaxDocument doc (true);
+}
+//END KateHlDownloadDialog
+
+//BEGIN KateGotoLineDialog
+KateGotoLineDialog::KateGotoLineDialog(QWidget *parent, int line, int max)
+ : KDialogBase(parent, 0L, true, i18n("Go to Line"), Ok | Cancel, Ok) {
+
+ QWidget *page = new QWidget(this);
+ setMainWidget(page);
+
+ QVBoxLayout *topLayout = new QVBoxLayout( page, 0, spacingHint() );
+ e1 = new KIntNumInput(line, page);
+ e1->setRange(1, max);
+ e1->setEditFocus(true);
+
+ QLabel *label = new QLabel( e1,i18n("&Go to line:"), page );
+ topLayout->addWidget(label);
+ topLayout->addWidget(e1);
+ topLayout->addSpacing(spacingHint()); // A little bit extra space
+ topLayout->addStretch(10);
+ e1->setFocus();
+}
+
+int KateGotoLineDialog::getLine() {
+ return e1->value();
+}
+//END KateGotoLineDialog
+
+//BEGIN KateModOnHdPrompt
+KateModOnHdPrompt::KateModOnHdPrompt( KateDocument *doc,
+ int modtype,
+ const QString &reason,
+ QWidget *parent )
+ : KDialogBase( parent, "", true, "", Ok|Apply|Cancel|User1 ),
+ m_doc( doc ),
+ m_modtype ( modtype ),
+ m_tmpfile( 0 )
+{
+ QString title, btnOK, whatisok;
+ if ( modtype == 3 ) // deleted
+ {
+ title = i18n("File Was Deleted on Disk");
+ btnOK = i18n("&Save File As...");
+ whatisok = i18n("Lets you select a location and save the file again.");
+ } else {
+ title = i18n("File Changed on Disk");
+ btnOK = i18n("&Reload File");
+ whatisok = i18n("Reload the file from disk. If you have unsaved changes, "
+ "they will be lost.");
+ }
+
+ setButtonText( Ok, btnOK);
+ setButtonText( Apply, i18n("&Ignore") );
+
+ setButtonWhatsThis( Ok, whatisok );
+ setButtonWhatsThis( Apply, i18n("Ignore the changes. You will not be prompted again.") );
+ setButtonWhatsThis( Cancel, i18n("Do nothing. Next time you focus the file, "
+ "or try to save it or close it, you will be prompted again.") );
+
+ enableButtonSeparator( true );
+ setCaption( title );
+
+ QFrame *w = makeMainWidget();
+ QVBoxLayout *lo = new QVBoxLayout( w );
+ QHBoxLayout *lo1 = new QHBoxLayout( lo );
+ QLabel *icon = new QLabel( w );
+ icon->setPixmap( DesktopIcon("messagebox_warning" ) );
+ lo1->addWidget( icon );
+ lo1->addWidget( new QLabel( reason + "\n\n" + i18n("What do you want to do?"), w ) );
+
+ // If the file isn't deleted, present a diff button, and a overwrite action.
+ if ( modtype != 3 )
+ {
+ QHBoxLayout *lo2 = new QHBoxLayout( lo );
+ QPushButton *btnDiff = new QPushButton( i18n("&View Difference"), w );
+ lo2->addStretch( 1 );
+ lo2->addWidget( btnDiff );
+ connect( btnDiff, SIGNAL(clicked()), this, SLOT(slotDiff()) );
+ QWhatsThis::add( btnDiff, i18n(
+ "Calculates the difference between the editor contents and the disk "
+ "file using diff(1) and opens the diff file with the default application "
+ "for that.") );
+
+ setButtonText( User1, i18n("Overwrite") );
+ setButtonWhatsThis( User1, i18n("Overwrite the disk file with the editor content.") );
+ }
+ else
+ showButton( User1, false );
+}
+
+KateModOnHdPrompt::~KateModOnHdPrompt()
+{
+}
+
+void KateModOnHdPrompt::slotDiff()
+{
+ // Start a KProcess that creates a diff
+ KProcIO *p = new KProcIO();
+ p->setComm( KProcess::All );
+ *p << "diff" << "-u" << "-" << m_doc->url().path();
+ connect( p, SIGNAL(processExited(KProcess*)), this, SLOT(slotPDone(KProcess*)) );
+ connect( p, SIGNAL(readReady(KProcIO*)), this, SLOT(slotPRead(KProcIO*)) );
+
+ setCursor( WaitCursor );
+
+ p->start( KProcess::NotifyOnExit, true );
+
+ uint lastln = m_doc->numLines();
+ for ( uint l = 0; l < lastln; l++ )
+ p->writeStdin( m_doc->textLine( l ) );
+
+ p->closeWhenDone();
+}
+
+void KateModOnHdPrompt::slotPRead( KProcIO *p)
+{
+ // create a file for the diff if we haven't one allready
+ if ( ! m_tmpfile )
+ m_tmpfile = new KTempFile();
+ // put all the data we have in it
+ QString stmp;
+ bool dataRead = false;
+ while ( p->readln( stmp, false ) > -1 )
+ {
+ *m_tmpfile->textStream() << stmp << endl;
+ dataRead = true;
+ }
+
+ // dominik: only ackRead(), when we *really* read data, otherwise, this slot
+ // is called initity times, which leads to a crash
+ if( dataRead )
+ p->ackRead();
+}
+
+void KateModOnHdPrompt::slotPDone( KProcess *p )
+{
+ setCursor( ArrowCursor );
+ if( ! m_tmpfile )
+ {
+ // dominik: there were only whitespace changes, so that the diff returned by
+ // diff(1) has 0 bytes. So slotPRead() is never called, as there is
+ // no data, so that m_tmpfile was never created and thus is NULL.
+ // NOTE: would be nice, if we could produce a fake-diff, so that kompare
+ // tells us "The files are identical". Right now, we get an ugly
+ // "Could not parse diff output".
+ m_tmpfile = new KTempFile();
+ }
+ m_tmpfile->close();
+
+ if ( ! p->normalExit() /*|| p->exitStatus()*/ )
+ {
+ KMessageBox::sorry( this,
+ i18n("The diff command failed. Please make sure that "
+ "diff(1) is installed and in your PATH."),
+ i18n("Error Creating Diff") );
+ delete m_tmpfile;
+ m_tmpfile = 0;
+ return;
+ }
+
+ KRun::runURL( m_tmpfile->name(), "text/x-diff", true );
+ delete m_tmpfile;
+ m_tmpfile = 0;
+}
+
+void KateModOnHdPrompt::slotApply()
+{
+ if ( KMessageBox::warningContinueCancel(
+ this,
+ i18n("Ignoring means that you will not be warned again (unless "
+ "the disk file changes once more): if you save the document, you "
+ "will overwrite the file on disk; if you do not save then the disk file "
+ "(if present) is what you have."),
+ i18n("You Are on Your Own"),
+ KStdGuiItem::cont(),
+ "kate_ignore_modonhd" ) != KMessageBox::Continue )
+ return;
+
+ done(Ignore);
+}
+
+void KateModOnHdPrompt::slotOk()
+{
+ done( m_modtype == 3 ? Save : Reload );
+}
+
+void KateModOnHdPrompt::slotUser1()
+{
+ done( Overwrite );
+}
+
+//END KateModOnHdPrompt
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katedialogs.h b/kate/part/katedialogs.h
new file mode 100644
index 000000000..ba03d5eff
--- /dev/null
+++ b/kate/part/katedialogs.h
@@ -0,0 +1,406 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2002, 2003 Anders Lund <anders.lund@lund.tdcadsl.dk>
+ Copyright (C) 2003 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
+
+ Based on work of:
+ Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KATE_DIALOGS_H__
+#define __KATE_DIALOGS_H__
+
+#include "katehighlight.h"
+#include "kateattribute.h"
+
+#include "../interfaces/document.h"
+
+#include <klistview.h>
+#include <kdialogbase.h>
+#include <kmimetype.h>
+
+#include <qstringlist.h>
+#include <qcolor.h>
+#include <qintdict.h>
+#include <qvbox.h>
+#include <qtabwidget.h>
+
+class KatePartPluginListItem;
+
+struct syntaxContextData;
+
+class KateDocument;
+class KateView;
+
+namespace KIO
+{
+ class Job;
+ class TransferJob;
+}
+
+class KAccel;
+class KColorButton;
+class KComboBox;
+class KIntNumInput;
+class KKeyButton;
+class KKeyChooser;
+class KMainWindow;
+class KPushButton;
+class KRegExpDialog;
+class KIntNumInput;
+
+class QButtonGroup;
+class QCheckBox;
+class QHBoxLayout;
+class QLabel;
+class QLineEdit;
+class QPushButton;
+class QRadioButton;
+class QSpinBox;
+class QListBoxItem;
+class QWidgetStack;
+class QVBox;
+class QListViewItem;
+class QCheckBox;
+
+class KateConfigPage : public Kate::ConfigPage
+{
+ Q_OBJECT
+
+ public:
+ KateConfigPage ( QWidget *parent=0, const char *name=0 );
+ virtual ~KateConfigPage ();
+
+ public:
+ bool changed () { return m_changed; }
+
+ private slots:
+ void somethingHasChanged ();
+
+ protected:
+ bool m_changed;
+};
+
+class KateGotoLineDialog : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+
+ KateGotoLineDialog(QWidget *parent, int line, int max);
+ int getLine();
+
+ protected:
+
+ KIntNumInput *e1;
+ QPushButton *btnOK;
+};
+
+class KateIndentConfigTab : public KateConfigPage
+{
+ Q_OBJECT
+
+ public:
+ KateIndentConfigTab(QWidget *parent);
+
+ protected slots:
+ void somethingToggled();
+ void indenterSelected (int);
+
+ protected:
+ enum { numFlags = 8 };
+ static const int flags[numFlags];
+ QCheckBox *opt[numFlags];
+ KIntNumInput *indentationWidth;
+ QButtonGroup *m_tabs;
+ KComboBox *m_indentMode;
+ QPushButton *m_configPage;
+
+ public slots:
+ void configPage();
+
+ void apply ();
+ void reload ();
+ void reset () {};
+ void defaults () {};
+};
+
+class KateSelectConfigTab : public KateConfigPage
+{
+ Q_OBJECT
+
+ public:
+ KateSelectConfigTab(QWidget *parent);
+
+ protected:
+ enum { numFlags = 2 };
+ static const int flags[numFlags];
+ QCheckBox *opt[numFlags];
+
+ QButtonGroup *m_tabs;
+ KIntNumInput *e4;
+ QCheckBox *e6;
+
+ public slots:
+ void apply ();
+ void reload ();
+ void reset () {};
+ void defaults () {};
+};
+
+class KateEditConfigTab : public KateConfigPage
+{
+ Q_OBJECT
+
+ public:
+ KateEditConfigTab(QWidget *parent);
+
+ protected:
+ enum { numFlags = 5 };
+ static const int flags[numFlags];
+ QCheckBox *opt[numFlags];
+
+ KIntNumInput *e1;
+ KIntNumInput *e2;
+ KIntNumInput *e3;
+ KComboBox *e5;
+ QCheckBox *m_wwmarker;
+
+ public slots:
+ void apply ();
+ void reload ();
+ void reset () {};
+ void defaults () {};
+};
+
+class KateViewDefaultsConfig : public KateConfigPage
+{
+ Q_OBJECT
+
+ public:
+ KateViewDefaultsConfig( QWidget *parent );
+ ~KateViewDefaultsConfig();
+
+ private:
+ QCheckBox *m_line;
+ QCheckBox *m_folding;
+ QCheckBox *m_collapseTopLevel;
+ QCheckBox *m_icons;
+ QCheckBox *m_scrollBarMarks;
+ QCheckBox *m_dynwrap;
+ QCheckBox *m_showIndentLines;
+ KIntNumInput *m_dynwrapAlignLevel;
+ QLabel *m_dynwrapIndicatorsLabel;
+ KComboBox *m_dynwrapIndicatorsCombo;
+ QButtonGroup *m_bmSort;
+
+ public slots:
+ void apply ();
+ void reload ();
+ void reset ();
+ void defaults ();
+};
+
+class KateEditKeyConfiguration: public KateConfigPage
+{
+ Q_OBJECT
+
+ public:
+ KateEditKeyConfiguration( QWidget* parent, KateDocument* doc );
+
+ public slots:
+ void apply();
+ void reload() {};
+ void reset() {};
+ void defaults() {};
+
+ protected:
+ void showEvent ( QShowEvent * );
+
+ private:
+ bool m_ready;
+ class KateDocument *m_doc;
+ KKeyChooser* m_keyChooser;
+ class KActionCollection *m_ac;
+};
+
+class KateSaveConfigTab : public KateConfigPage
+{
+ Q_OBJECT
+ public:
+ KateSaveConfigTab( QWidget *parent );
+
+ public slots:
+ void apply();
+ void reload();
+ void reset();
+ void defaults();
+
+ protected:
+ KComboBox *m_encoding, *m_eol;
+ QCheckBox *cbLocalFiles, *cbRemoteFiles;
+ QCheckBox *replaceTabs, *removeSpaces, *allowEolDetection;
+ QLineEdit *leBuPrefix;
+ QLineEdit *leBuSuffix;
+ KIntNumInput *dirSearchDepth;
+ class QSpinBox *blockCount;
+ class QLabel *blockCountLabel;
+};
+
+class KatePartPluginListItem;
+
+class KatePartPluginListView : public KListView
+{
+ Q_OBJECT
+
+ friend class KatePartPluginListItem;
+
+ public:
+ KatePartPluginListView (QWidget *parent = 0, const char *name = 0);
+
+ signals:
+ void stateChange(KatePartPluginListItem *, bool);
+
+ private:
+ void stateChanged(KatePartPluginListItem *, bool);
+};
+
+class QListViewItem;
+class KatePartPluginConfigPage : public KateConfigPage
+{
+ Q_OBJECT
+
+ public:
+ KatePartPluginConfigPage (QWidget *parent);
+ ~KatePartPluginConfigPage ();
+
+ public slots:
+ void apply ();
+ void reload () {};
+ void reset () {};
+ void defaults () {};
+
+ private slots:
+ void slotCurrentChanged( QListViewItem * );
+ void slotConfigure();
+ void slotStateChanged( KatePartPluginListItem *, bool );
+
+ private:
+ KatePartPluginListView *listView;
+ QPtrList<KatePartPluginListItem> m_items;
+ class QPushButton *btnConfigure;
+};
+
+class KateHlConfigPage : public KateConfigPage
+{
+ Q_OBJECT
+
+ public:
+ KateHlConfigPage (QWidget *parent, KateDocument *doc);
+ ~KateHlConfigPage ();
+
+ public slots:
+ void apply ();
+ void reload ();
+ void reset () {};
+ void defaults () {};
+
+ protected slots:
+ void hlChanged(int);
+ void hlDownload();
+ void showMTDlg();
+
+ private:
+ void writeback ();
+
+ QComboBox *hlCombo;
+ QLineEdit *wildcards;
+ QLineEdit *mimetypes;
+ class KIntNumInput *priority;
+ class QLabel *author, *license;
+
+ QIntDict<KateHlData> hlDataDict;
+ KateHlData *hlData;
+
+ KateDocument *m_doc;
+};
+
+class KateHlDownloadDialog: public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ KateHlDownloadDialog(QWidget *parent, const char *name, bool modal);
+ ~KateHlDownloadDialog();
+
+ private:
+ class QListView *list;
+ class QString listData;
+ KIO::TransferJob *transferJob;
+
+ private slots:
+ void listDataReceived(KIO::Job *, const QByteArray &data);
+
+ public slots:
+ void slotUser1();
+};
+
+class KProcIO;
+class KProcess;
+/**
+ * This dialog will prompt the user for what do with a file that is
+ * modified on disk.
+ * If the file wasn't deleted, it has a 'diff' button, which will create
+ * a diff file (uing diff(1)) and launch that using KRun.
+ */
+class KateModOnHdPrompt : public KDialogBase
+{
+ Q_OBJECT
+ public:
+ enum Status {
+ Reload=1, // 0 is KDialogBase::Cancel
+ Save,
+ Overwrite,
+ Ignore
+ };
+ KateModOnHdPrompt( KateDocument *doc, int modtype, const QString &reason, QWidget *parent );
+ ~KateModOnHdPrompt();
+
+ public slots:
+ /**
+ * Show a diff between the document text and the disk file.
+ * This will not close the dialog, since we still need a
+ * decision from the user.
+ */
+ void slotDiff();
+
+ void slotOk();
+ void slotApply();
+ void slotUser1();
+
+ private slots:
+ void slotPRead(KProcIO*); ///< Read from the diff process
+ void slotPDone(KProcess*); ///< Runs the diff file when done
+
+ private:
+ KateDocument *m_doc;
+ int m_modtype;
+ class KTempFile *m_tmpfile; ///< The diff file. Deleted by KRun when the viewer is exited.
+
+};
+
+#endif
diff --git a/kate/part/katedocument.cpp b/kate/part/katedocument.cpp
new file mode 100644
index 000000000..68374ebe1
--- /dev/null
+++ b/kate/part/katedocument.cpp
@@ -0,0 +1,5174 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2001-2004 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
+ Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02111-13020, USA.
+*/
+
+//BEGIN includes
+#include "katedocument.h"
+#include "katedocument.moc"
+#include "katekeyinterceptorfunctor.h"
+#include "katefactory.h"
+#include "katedialogs.h"
+#include "katehighlight.h"
+#include "kateview.h"
+#include "katesearch.h"
+#include "kateautoindent.h"
+#include "katetextline.h"
+#include "katedocumenthelpers.h"
+#include "kateprinter.h"
+#include "katelinerange.h"
+#include "katesupercursor.h"
+#include "katearbitraryhighlight.h"
+#include "katerenderer.h"
+#include "kateattribute.h"
+#include "kateconfig.h"
+#include "katefiletype.h"
+#include "kateschema.h"
+#include "katetemplatehandler.h"
+#include <ktexteditor/plugin.h>
+
+#include <kio/job.h>
+#include <kio/netaccess.h>
+#include <kio/kfileitem.h>
+
+
+#include <kparts/event.h>
+
+#include <klocale.h>
+#include <kglobal.h>
+#include <kapplication.h>
+#include <kpopupmenu.h>
+#include <kconfig.h>
+#include <kfiledialog.h>
+#include <kmessagebox.h>
+#include <kstdaction.h>
+#include <kiconloader.h>
+#include <kxmlguifactory.h>
+#include <kdialogbase.h>
+#include <kdebug.h>
+#include <kglobalsettings.h>
+#include <klibloader.h>
+#include <kdirwatch.h>
+#include <kwin.h>
+#include <kencodingfiledialog.h>
+#include <ktempfile.h>
+#include <kmdcodec.h>
+#include <kstandarddirs.h>
+
+#include <qtimer.h>
+#include <qfile.h>
+#include <qclipboard.h>
+#include <qtextstream.h>
+#include <qtextcodec.h>
+#include <qmap.h>
+//END includes
+
+//BEGIN PRIVATE CLASSES
+class KatePartPluginItem
+{
+ public:
+ KTextEditor::Plugin *plugin;
+};
+//END PRIVATE CLASSES
+
+//BEGIN d'tor, c'tor
+//
+// KateDocument Constructor
+//
+KateDocument::KateDocument ( bool bSingleViewMode, bool bBrowserView,
+ bool bReadOnly, QWidget *parentWidget,
+ const char *widgetName, QObject *parent, const char *name)
+: Kate::Document(parent, name),
+ m_plugins (KateFactory::self()->plugins().count()),
+ m_undoDontMerge(false),
+ m_undoIgnoreCancel(false),
+ lastUndoGroupWhenSaved( 0 ),
+ lastRedoGroupWhenSaved( 0 ),
+ docWasSavedWhenUndoWasEmpty( true ),
+ docWasSavedWhenRedoWasEmpty( true ),
+ m_modOnHd (false),
+ m_modOnHdReason (0),
+ m_job (0),
+ m_tempFile (0),
+ m_tabInterceptor(0)
+{
+ m_undoComplexMerge=false;
+ m_isInUndo = false;
+ // my dcop object
+ setObjId ("KateDocument#"+documentDCOPSuffix());
+
+ // ktexteditor interfaces
+ setBlockSelectionInterfaceDCOPSuffix (documentDCOPSuffix());
+ setConfigInterfaceDCOPSuffix (documentDCOPSuffix());
+ setConfigInterfaceExtensionDCOPSuffix (documentDCOPSuffix());
+ setCursorInterfaceDCOPSuffix (documentDCOPSuffix());
+ setEditInterfaceDCOPSuffix (documentDCOPSuffix());
+ setEncodingInterfaceDCOPSuffix (documentDCOPSuffix());
+ setHighlightingInterfaceDCOPSuffix (documentDCOPSuffix());
+ setMarkInterfaceDCOPSuffix (documentDCOPSuffix());
+ setMarkInterfaceExtensionDCOPSuffix (documentDCOPSuffix());
+ setPrintInterfaceDCOPSuffix (documentDCOPSuffix());
+ setSearchInterfaceDCOPSuffix (documentDCOPSuffix());
+ setSelectionInterfaceDCOPSuffix (documentDCOPSuffix());
+ setSelectionInterfaceExtDCOPSuffix (documentDCOPSuffix());
+ setSessionConfigInterfaceDCOPSuffix (documentDCOPSuffix());
+ setUndoInterfaceDCOPSuffix (documentDCOPSuffix());
+ setWordWrapInterfaceDCOPSuffix (documentDCOPSuffix());
+
+ // init local plugin array
+ m_plugins.fill (0);
+
+ // register doc at factory
+ KateFactory::self()->registerDocument (this);
+
+ m_reloading = false;
+ m_loading = false;
+ m_encodingSticky = false;
+
+ m_buffer = new KateBuffer (this);
+
+ // init the config object, be careful not to use it
+ // until the initial readConfig() call is done
+ m_config = new KateDocumentConfig (this);
+
+ // init some more vars !
+ m_activeView = 0L;
+
+ hlSetByUser = false;
+ m_fileType = -1;
+ m_fileTypeSetByUser = false;
+ setInstance( KateFactory::self()->instance() );
+
+ editSessionNumber = 0;
+ editIsRunning = false;
+ m_editCurrentUndo = 0L;
+ editWithUndo = false;
+
+ m_docNameNumber = 0;
+
+ m_bSingleViewMode = bSingleViewMode;
+ m_bBrowserView = bBrowserView;
+ m_bReadOnly = bReadOnly;
+
+ m_marks.setAutoDelete( true );
+ m_markPixmaps.setAutoDelete( true );
+ m_markDescriptions.setAutoDelete( true );
+ setMarksUserChangable( markType01 );
+
+ m_undoMergeTimer = new QTimer(this);
+ connect(m_undoMergeTimer, SIGNAL(timeout()), SLOT(undoCancel()));
+
+ clearMarks ();
+ clearUndo ();
+ clearRedo ();
+ setModified (false);
+ docWasSavedWhenUndoWasEmpty = true;
+
+ // normal hl
+ m_buffer->setHighlight (0);
+
+ m_extension = new KateBrowserExtension( this );
+ m_arbitraryHL = new KateArbitraryHighlight();
+ m_indenter = KateAutoIndent::createIndenter ( this, 0 );
+
+ m_indenter->updateConfig ();
+
+ // some nice signals from the buffer
+ connect(m_buffer, SIGNAL(tagLines(int,int)), this, SLOT(tagLines(int,int)));
+ connect(m_buffer, SIGNAL(codeFoldingUpdated()),this,SIGNAL(codeFoldingUpdated()));
+
+ // if the user changes the highlight with the dialog, notify the doc
+ connect(KateHlManager::self(),SIGNAL(changed()),SLOT(internalHlChanged()));
+
+ // signal for the arbitrary HL
+ connect(m_arbitraryHL, SIGNAL(tagLines(KateView*, KateSuperRange*)), SLOT(tagArbitraryLines(KateView*, KateSuperRange*)));
+
+ // signals for mod on hd
+ connect( KateFactory::self()->dirWatch(), SIGNAL(dirty (const QString &)),
+ this, SLOT(slotModOnHdDirty (const QString &)) );
+
+ connect( KateFactory::self()->dirWatch(), SIGNAL(created (const QString &)),
+ this, SLOT(slotModOnHdCreated (const QString &)) );
+
+ connect( KateFactory::self()->dirWatch(), SIGNAL(deleted (const QString &)),
+ this, SLOT(slotModOnHdDeleted (const QString &)) );
+
+ // update doc name
+ setDocName ("");
+
+ // if single view mode, like in the konqui embedding, create a default view ;)
+ if ( m_bSingleViewMode )
+ {
+ KTextEditor::View *view = createView( parentWidget, widgetName );
+ insertChildClient( view );
+ view->show();
+ setWidget( view );
+ }
+
+ connect(this,SIGNAL(sigQueryClose(bool *, bool*)),this,SLOT(slotQueryClose_save(bool *, bool*)));
+
+ m_isasking = 0;
+
+ // plugins
+ for (uint i=0; i<KateFactory::self()->plugins().count(); i++)
+ {
+ if (config()->plugin (i))
+ loadPlugin (i);
+ }
+}
+
+//
+// KateDocument Destructor
+//
+KateDocument::~KateDocument()
+{
+ // remove file from dirwatch
+ deactivateDirWatch ();
+
+ if (!singleViewMode())
+ {
+ // clean up remaining views
+ m_views.setAutoDelete( true );
+ m_views.clear();
+ }
+
+ delete m_editCurrentUndo;
+
+ delete m_arbitraryHL;
+
+ // cleanup the undo items, very important, truee :/
+ undoItems.setAutoDelete(true);
+ undoItems.clear();
+
+ // clean up plugins
+ unloadAllPlugins ();
+
+ delete m_config;
+ delete m_indenter;
+ KateFactory::self()->deregisterDocument (this);
+}
+//END
+
+//BEGIN Plugins
+void KateDocument::unloadAllPlugins ()
+{
+ for (uint i=0; i<m_plugins.count(); i++)
+ unloadPlugin (i);
+}
+
+void KateDocument::enableAllPluginsGUI (KateView *view)
+{
+ for (uint i=0; i<m_plugins.count(); i++)
+ enablePluginGUI (m_plugins[i], view);
+}
+
+void KateDocument::disableAllPluginsGUI (KateView *view)
+{
+ for (uint i=0; i<m_plugins.count(); i++)
+ disablePluginGUI (m_plugins[i], view);
+}
+
+void KateDocument::loadPlugin (uint pluginIndex)
+{
+ if (m_plugins[pluginIndex]) return;
+
+ m_plugins[pluginIndex] = KTextEditor::createPlugin (QFile::encodeName((KateFactory::self()->plugins())[pluginIndex]->library()), this);
+
+ enablePluginGUI (m_plugins[pluginIndex]);
+}
+
+void KateDocument::unloadPlugin (uint pluginIndex)
+{
+ if (!m_plugins[pluginIndex]) return;
+
+ disablePluginGUI (m_plugins[pluginIndex]);
+
+ delete m_plugins[pluginIndex];
+ m_plugins[pluginIndex] = 0L;
+}
+
+void KateDocument::enablePluginGUI (KTextEditor::Plugin *plugin, KateView *view)
+{
+ if (!plugin) return;
+ if (!KTextEditor::pluginViewInterface(plugin)) return;
+
+ KXMLGUIFactory *factory = view->factory();
+ if ( factory )
+ factory->removeClient( view );
+
+ KTextEditor::pluginViewInterface(plugin)->addView(view);
+
+ if ( factory )
+ factory->addClient( view );
+}
+
+void KateDocument::enablePluginGUI (KTextEditor::Plugin *plugin)
+{
+ if (!plugin) return;
+ if (!KTextEditor::pluginViewInterface(plugin)) return;
+
+ for (uint i=0; i< m_views.count(); i++)
+ enablePluginGUI (plugin, m_views.at(i));
+}
+
+void KateDocument::disablePluginGUI (KTextEditor::Plugin *plugin, KateView *view)
+{
+ if (!plugin) return;
+ if (!KTextEditor::pluginViewInterface(plugin)) return;
+
+ KXMLGUIFactory *factory = view->factory();
+ if ( factory )
+ factory->removeClient( view );
+
+ KTextEditor::pluginViewInterface( plugin )->removeView( view );
+
+ if ( factory )
+ factory->addClient( view );
+}
+
+void KateDocument::disablePluginGUI (KTextEditor::Plugin *plugin)
+{
+ if (!plugin) return;
+ if (!KTextEditor::pluginViewInterface(plugin)) return;
+
+ for (uint i=0; i< m_views.count(); i++)
+ disablePluginGUI (plugin, m_views.at(i));
+}
+//END
+
+//BEGIN KTextEditor::Document stuff
+
+KTextEditor::View *KateDocument::createView( QWidget *parent, const char *name )
+{
+ KateView* newView = new KateView( this, parent, name);
+ connect(newView, SIGNAL(cursorPositionChanged()), SLOT(undoCancel()));
+ if ( s_fileChangedDialogsActivated )
+ connect( newView, SIGNAL(gotFocus( Kate::View * )), this, SLOT(slotModifiedOnDisk()) );
+ return newView;
+}
+
+QPtrList<KTextEditor::View> KateDocument::views () const
+{
+ return m_textEditViews;
+}
+
+void KateDocument::setActiveView( KateView *view )
+{
+ if ( m_activeView == view ) return;
+
+ m_activeView = view;
+}
+//END
+
+//BEGIN KTextEditor::ConfigInterfaceExtension stuff
+
+uint KateDocument::configPages () const
+{
+ return 10;
+}
+
+KTextEditor::ConfigPage *KateDocument::configPage (uint number, QWidget *parent, const char * )
+{
+ switch( number )
+ {
+ case 0:
+ return new KateViewDefaultsConfig (parent);
+
+ case 1:
+ return new KateSchemaConfigPage (parent, this);
+
+ case 2:
+ return new KateSelectConfigTab (parent);
+
+ case 3:
+ return new KateEditConfigTab (parent);
+
+ case 4:
+ return new KateIndentConfigTab (parent);
+
+ case 5:
+ return new KateSaveConfigTab (parent);
+
+ case 6:
+ return new KateHlConfigPage (parent, this);
+
+ case 7:
+ return new KateFileTypeConfigTab (parent);
+
+ case 8:
+ return new KateEditKeyConfiguration (parent, this);
+
+ case 9:
+ return new KatePartPluginConfigPage (parent);
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+QString KateDocument::configPageName (uint number) const
+{
+ switch( number )
+ {
+ case 0:
+ return i18n ("Appearance");
+
+ case 1:
+ return i18n ("Fonts & Colors");
+
+ case 2:
+ return i18n ("Cursor & Selection");
+
+ case 3:
+ return i18n ("Editing");
+
+ case 4:
+ return i18n ("Indentation");
+
+ case 5:
+ return i18n("Open/Save");
+
+ case 6:
+ return i18n ("Highlighting");
+
+ case 7:
+ return i18n("Filetypes");
+
+ case 8:
+ return i18n ("Shortcuts");
+
+ case 9:
+ return i18n ("Plugins");
+
+ default:
+ return QString ("");
+ }
+
+ return QString ("");
+}
+
+QString KateDocument::configPageFullName (uint number) const
+{
+ switch( number )
+ {
+ case 0:
+ return i18n("Appearance");
+
+ case 1:
+ return i18n ("Font & Color Schemas");
+
+ case 2:
+ return i18n ("Cursor & Selection Behavior");
+
+ case 3:
+ return i18n ("Editing Options");
+
+ case 4:
+ return i18n ("Indentation Rules");
+
+ case 5:
+ return i18n("File Opening & Saving");
+
+ case 6:
+ return i18n ("Highlighting Rules");
+
+ case 7:
+ return i18n("Filetype Specific Settings");
+
+ case 8:
+ return i18n ("Shortcuts Configuration");
+
+ case 9:
+ return i18n ("Plugin Manager");
+
+ default:
+ return QString ("");
+ }
+
+ return QString ("");
+}
+
+QPixmap KateDocument::configPagePixmap (uint number, int size) const
+{
+ switch( number )
+ {
+ case 0:
+ return BarIcon("view_text",size);
+
+ case 1:
+ return BarIcon("colorize", size);
+
+ case 2:
+ return BarIcon("frame_edit", size);
+
+ case 3:
+ return BarIcon("edit", size);
+
+ case 4:
+ return BarIcon("rightjust", size);
+
+ case 5:
+ return BarIcon("filesave", size);
+
+ case 6:
+ return BarIcon("source", size);
+
+ case 7:
+ return BarIcon("edit", size);
+
+ case 8:
+ return BarIcon("key_enter", size);
+
+ case 9:
+ return BarIcon("connect_established", size);
+
+ default:
+ return BarIcon("edit", size);
+ }
+
+ return BarIcon("edit", size);
+}
+//END
+
+//BEGIN KTextEditor::EditInterface stuff
+
+QString KateDocument::text() const
+{
+ QString s;
+
+ for (uint i = 0; i < m_buffer->count(); i++)
+ {
+ KateTextLine::Ptr textLine = m_buffer->plainLine(i);
+
+ if (textLine)
+ {
+ s.append (textLine->string());
+
+ if ((i+1) < m_buffer->count())
+ s.append('\n');
+ }
+ }
+
+ return s;
+}
+
+QString KateDocument::text ( uint startLine, uint startCol, uint endLine, uint endCol ) const
+{
+ return text(startLine, startCol, endLine, endCol, false);
+}
+
+QString KateDocument::text ( uint startLine, uint startCol, uint endLine, uint endCol, bool blockwise) const
+{
+ if ( blockwise && (startCol > endCol) )
+ return QString ();
+
+ QString s;
+
+ if (startLine == endLine)
+ {
+ if (startCol > endCol)
+ return QString ();
+
+ KateTextLine::Ptr textLine = m_buffer->plainLine(startLine);
+
+ if ( !textLine )
+ return QString ();
+
+ return textLine->string(startCol, endCol-startCol);
+ }
+ else
+ {
+
+ for (uint i = startLine; (i <= endLine) && (i < m_buffer->count()); i++)
+ {
+ KateTextLine::Ptr textLine = m_buffer->plainLine(i);
+
+ if ( !blockwise )
+ {
+ if (i == startLine)
+ s.append (textLine->string(startCol, textLine->length()-startCol));
+ else if (i == endLine)
+ s.append (textLine->string(0, endCol));
+ else
+ s.append (textLine->string());
+ }
+ else
+ {
+ s.append( textLine->string( startCol, endCol-startCol));
+ }
+
+ if ( i < endLine )
+ s.append('\n');
+ }
+ }
+
+ return s;
+}
+
+QString KateDocument::textLine( uint line ) const
+{
+ KateTextLine::Ptr l = m_buffer->plainLine(line);
+
+ if (!l)
+ return QString();
+
+ return l->string();
+}
+
+bool KateDocument::setText(const QString &s)
+{
+ if (!isReadWrite())
+ return false;
+
+ QPtrList<KTextEditor::Mark> m = marks ();
+ QValueList<KTextEditor::Mark> msave;
+
+ for (uint i=0; i < m.count(); i++)
+ msave.append (*m.at(i));
+
+ editStart ();
+
+ // delete the text
+ clear();
+
+ // insert the new text
+ insertText (0, 0, s);
+
+ editEnd ();
+
+ for (uint i=0; i < msave.count(); i++)
+ setMark (msave[i].line, msave[i].type);
+
+ return true;
+}
+
+bool KateDocument::clear()
+{
+ if (!isReadWrite())
+ return false;
+
+ for (KateView * view = m_views.first(); view != 0L; view = m_views.next() ) {
+ view->clear();
+ view->tagAll();
+ view->update();
+ }
+
+ clearMarks ();
+
+ return removeText (0,0,lastLine()+1, 0);
+}
+
+bool KateDocument::insertText( uint line, uint col, const QString &s)
+{
+ return insertText (line, col, s, false);
+}
+
+bool KateDocument::insertText( uint line, uint col, const QString &s, bool blockwise )
+{
+ if (!isReadWrite())
+ return false;
+
+ if (s.isEmpty())
+ return true;
+
+ if (line == numLines())
+ editInsertLine(line,"");
+ else if (line > lastLine())
+ return false;
+
+ editStart ();
+
+ uint insertPos = col;
+ uint len = s.length();
+
+ QString buf;
+
+ bool replacetabs = ( config()->configFlags() & KateDocumentConfig::cfReplaceTabsDyn && ! m_isInUndo );
+ uint tw = config()->tabWidth();
+ uint insertPosExpanded = insertPos;
+ KateTextLine::Ptr l = m_buffer->line( line );
+ if (l != 0)
+ insertPosExpanded = l->cursorX( insertPos, tw );
+
+ for (uint pos = 0; pos < len; pos++)
+ {
+ QChar ch = s[pos];
+
+ if (ch == '\n')
+ {
+ editInsertText (line, insertPos, buf);
+
+ if ( !blockwise )
+ {
+ editWrapLine (line, insertPos + buf.length());
+ insertPos = insertPosExpanded = 0;
+ }
+ else
+ {
+ if ( line == lastLine() )
+ editWrapLine (line, insertPos + buf.length());
+ }
+
+ line++;
+ buf.truncate(0);
+ l = m_buffer->line( line );
+ if (l)
+ insertPosExpanded = l->cursorX( insertPos, tw );
+ }
+ else
+ {
+ if ( replacetabs && ch == '\t' )
+ {
+ uint tr = tw - ( insertPosExpanded+buf.length() )%tw;
+ for ( uint i=0; i < tr; i++ )
+ buf += ' ';
+ }
+ else
+ buf += ch; // append char to buffer
+ }
+ }
+
+ editInsertText (line, insertPos, buf);
+
+ editEnd ();
+ emit textInserted(line,insertPos);
+ return true;
+}
+
+bool KateDocument::removeText ( uint startLine, uint startCol, uint endLine, uint endCol )
+{
+ return removeText (startLine, startCol, endLine, endCol, false);
+}
+
+bool KateDocument::removeText ( uint startLine, uint startCol, uint endLine, uint endCol, bool blockwise)
+{
+ if (!isReadWrite())
+ return false;
+
+ if ( blockwise && (startCol > endCol) )
+ return false;
+
+ if ( startLine > endLine )
+ return false;
+
+ if ( startLine > lastLine() )
+ return false;
+
+ if (!blockwise) {
+ emit aboutToRemoveText(KateTextRange(startLine,startCol,endLine,endCol));
+ }
+ editStart ();
+
+ if ( !blockwise )
+ {
+ if ( endLine > lastLine() )
+ {
+ endLine = lastLine()+1;
+ endCol = 0;
+ }
+
+ if (startLine == endLine)
+ {
+ editRemoveText (startLine, startCol, endCol-startCol);
+ }
+ else if ((startLine+1) == endLine)
+ {
+ if ( (m_buffer->plainLine(startLine)->length()-startCol) > 0 )
+ editRemoveText (startLine, startCol, m_buffer->plainLine(startLine)->length()-startCol);
+
+ editRemoveText (startLine+1, 0, endCol);
+ editUnWrapLine (startLine);
+ }
+ else
+ {
+ for (uint line = endLine; line >= startLine; line--)
+ {
+ if ((line > startLine) && (line < endLine))
+ {
+ editRemoveLine (line);
+ }
+ else
+ {
+ if (line == endLine)
+ {
+ if ( endLine <= lastLine() )
+ editRemoveText (line, 0, endCol);
+ }
+ else
+ {
+ if ( (m_buffer->plainLine(line)->length()-startCol) > 0 )
+ editRemoveText (line, startCol, m_buffer->plainLine(line)->length()-startCol);
+
+ editUnWrapLine (startLine);
+ }
+ }
+
+ if ( line == 0 )
+ break;
+ }
+ }
+ } // if ( ! blockwise )
+ else
+ {
+ if ( endLine > lastLine() )
+ endLine = lastLine ();
+
+ for (uint line = endLine; line >= startLine; line--)
+ {
+
+ editRemoveText (line, startCol, endCol-startCol);
+
+ if ( line == 0 )
+ break;
+ }
+ }
+
+ editEnd ();
+ emit textRemoved();
+ return true;
+}
+
+bool KateDocument::insertLine( uint l, const QString &str )
+{
+ if (!isReadWrite())
+ return false;
+
+ if (l > numLines())
+ return false;
+
+ return editInsertLine (l, str);
+}
+
+bool KateDocument::removeLine( uint line )
+{
+ if (!isReadWrite())
+ return false;
+
+ if (line > lastLine())
+ return false;
+
+ return editRemoveLine (line);
+}
+
+uint KateDocument::length() const
+{
+ uint l = 0;
+
+ for (uint i = 0; i < m_buffer->count(); i++)
+ {
+ KateTextLine::Ptr line = m_buffer->plainLine(i);
+
+ if (line)
+ l += line->length();
+ }
+
+ return l;
+}
+
+uint KateDocument::numLines() const
+{
+ return m_buffer->count();
+}
+
+uint KateDocument::numVisLines() const
+{
+ return m_buffer->countVisible ();
+}
+
+int KateDocument::lineLength ( uint line ) const
+{
+ KateTextLine::Ptr l = m_buffer->plainLine(line);
+
+ if (!l)
+ return -1;
+
+ return l->length();
+}
+//END
+
+//BEGIN KTextEditor::EditInterface internal stuff
+//
+// Starts an edit session with (or without) undo, update of view disabled during session
+//
+void KateDocument::editStart (bool withUndo)
+{
+ editSessionNumber++;
+
+ if (editSessionNumber > 1)
+ return;
+
+ editIsRunning = true;
+ editWithUndo = withUndo;
+
+ if (editWithUndo)
+ undoStart();
+ else
+ undoCancel();
+
+ for (uint z = 0; z < m_views.count(); z++)
+ {
+ m_views.at(z)->editStart ();
+ }
+
+ m_buffer->editStart ();
+}
+
+void KateDocument::undoStart()
+{
+ if (m_editCurrentUndo || (m_activeView && m_activeView->imComposeEvent())) return;
+
+ // Make sure the buffer doesn't get bigger than requested
+ if ((config()->undoSteps() > 0) && (undoItems.count() > config()->undoSteps()))
+ {
+ undoItems.setAutoDelete(true);
+ undoItems.removeFirst();
+ undoItems.setAutoDelete(false);
+ docWasSavedWhenUndoWasEmpty = false;
+ }
+
+ // new current undo item
+ m_editCurrentUndo = new KateUndoGroup(this);
+}
+
+void KateDocument::undoEnd()
+{
+ if (m_activeView && m_activeView->imComposeEvent())
+ return;
+
+ if (m_editCurrentUndo)
+ {
+ bool changedUndo = false;
+
+ if (m_editCurrentUndo->isEmpty())
+ delete m_editCurrentUndo;
+ else if (!m_undoDontMerge && undoItems.last() && undoItems.last()->merge(m_editCurrentUndo,m_undoComplexMerge))
+ delete m_editCurrentUndo;
+ else
+ {
+ undoItems.append(m_editCurrentUndo);
+ changedUndo = true;
+ }
+
+ m_undoDontMerge = false;
+ m_undoIgnoreCancel = true;
+
+ m_editCurrentUndo = 0L;
+
+ // (Re)Start the single-shot timer to cancel the undo merge
+ // the user has 5 seconds to input more data, or undo merging gets canceled for the current undo item.
+ m_undoMergeTimer->start(5000, true);
+
+ if (changedUndo)
+ emit undoChanged();
+ }
+}
+
+void KateDocument::undoCancel()
+{
+ if (m_undoIgnoreCancel) {
+ m_undoIgnoreCancel = false;
+ return;
+ }
+
+ m_undoDontMerge = true;
+
+ Q_ASSERT(!m_editCurrentUndo);
+
+ // As you can see by the above assert, neither of these should really be required
+ delete m_editCurrentUndo;
+ m_editCurrentUndo = 0L;
+}
+
+void KateDocument::undoSafePoint() {
+ Q_ASSERT(m_editCurrentUndo);
+ if (!m_editCurrentUndo) return;
+ m_editCurrentUndo->safePoint();
+}
+
+//
+// End edit session and update Views
+//
+void KateDocument::editEnd ()
+{
+ if (editSessionNumber == 0)
+ return;
+
+ // wrap the new/changed text, if something really changed!
+ if (m_buffer->editChanged() && (editSessionNumber == 1))
+ if (editWithUndo && config()->wordWrap())
+ wrapText (m_buffer->editTagStart(), m_buffer->editTagEnd());
+
+ editSessionNumber--;
+
+ if (editSessionNumber > 0)
+ return;
+
+ // end buffer edit, will trigger hl update
+ // this will cause some possible adjustment of tagline start/end
+ m_buffer->editEnd ();
+
+ if (editWithUndo)
+ undoEnd();
+
+ // edit end for all views !!!!!!!!!
+ for (uint z = 0; z < m_views.count(); z++)
+ m_views.at(z)->editEnd (m_buffer->editTagStart(), m_buffer->editTagEnd(), m_buffer->editTagFrom());
+
+ if (m_buffer->editChanged())
+ {
+ setModified(true);
+ emit textChanged ();
+ }
+
+ editIsRunning = false;
+}
+
+bool KateDocument::wrapText (uint startLine, uint endLine)
+{
+ uint col = config()->wordWrapAt();
+
+ if (col == 0)
+ return false;
+
+ editStart ();
+
+ for (uint line = startLine; (line <= endLine) && (line < numLines()); line++)
+ {
+ KateTextLine::Ptr l = m_buffer->line(line);
+
+ if (!l)
+ return false;
+
+ kdDebug (13020) << "try wrap line: " << line << endl;
+
+ if (l->lengthWithTabs(m_buffer->tabWidth()) > col)
+ {
+ KateTextLine::Ptr nextl = m_buffer->line(line+1);
+
+ kdDebug (13020) << "do wrap line: " << line << endl;
+
+ const QChar *text = l->text();
+ uint eolPosition = l->length()-1;
+
+ // take tabs into account here, too
+ uint x = 0;
+ const QString & t = l->string();
+ uint z2 = 0;
+ for ( ; z2 < l->length(); z2++)
+ {
+ if (t[z2] == QChar('\t'))
+ x += m_buffer->tabWidth() - (x % m_buffer->tabWidth());
+ else
+ x++;
+
+ if (x > col)
+ break;
+ }
+
+ uint searchStart = kMin (z2, l->length()-1);
+
+ // If where we are wrapping is an end of line and is a space we don't
+ // want to wrap there
+ if (searchStart == eolPosition && text[searchStart].isSpace())
+ searchStart--;
+
+ // Scan backwards looking for a place to break the line
+ // We are not interested in breaking at the first char
+ // of the line (if it is a space), but we are at the second
+ // anders: if we can't find a space, try breaking on a word
+ // boundry, using KateHighlight::canBreakAt().
+ // This could be a priority (setting) in the hl/filetype/document
+ int z = 0;
+ uint nw = 0; // alternative position, a non word character
+ for (z=searchStart; z > 0; z--)
+ {
+ if (text[z].isSpace()) break;
+ if ( ! nw && highlight()->canBreakAt( text[z] , l->attribute(z) ) )
+ nw = z;
+ }
+
+ if (z > 0)
+ {
+ // cu space
+ editRemoveText (line, z, 1);
+ }
+ else
+ {
+ // There was no space to break at so break at a nonword character if
+ // found, or at the wrapcolumn ( that needs be configurable )
+ // Don't try and add any white space for the break
+ if ( nw && nw < col ) nw++; // break on the right side of the character
+ z = nw ? nw : col;
+ }
+
+ if (nextl && !nextl->isAutoWrapped())
+ {
+ editWrapLine (line, z, true);
+ editMarkLineAutoWrapped (line+1, true);
+
+ endLine++;
+ }
+ else
+ {
+ if (nextl && (nextl->length() > 0) && !nextl->getChar(0).isSpace() && ((l->length() < 1) || !l->getChar(l->length()-1).isSpace()))
+ editInsertText (line+1, 0, QString (" "));
+
+ bool newLineAdded = false;
+ editWrapLine (line, z, false, &newLineAdded);
+
+ editMarkLineAutoWrapped (line+1, true);
+
+ endLine++;
+ }
+ }
+ }
+
+ editEnd ();
+
+ return true;
+}
+
+void KateDocument::editAddUndo (KateUndoGroup::UndoType type, uint line, uint col, uint len, const QString &text)
+{
+ if (editIsRunning && editWithUndo && m_editCurrentUndo) {
+ m_editCurrentUndo->addItem(type, line, col, len, text);
+
+ // Clear redo buffer
+ if (redoItems.count()) {
+ redoItems.setAutoDelete(true);
+ redoItems.clear();
+ redoItems.setAutoDelete(false);
+ }
+ }
+}
+
+bool KateDocument::editInsertText ( uint line, uint col, const QString &str )
+{
+ if (!isReadWrite())
+ return false;
+
+ QString s = str;
+
+ KateTextLine::Ptr l = m_buffer->line(line);
+
+ if (!l)
+ return false;
+
+ if ( config()->configFlags() & KateDocumentConfig::cfReplaceTabsDyn && ! m_isInUndo )
+ {
+ uint tw = config()->tabWidth();
+ int pos = 0;
+ uint l = 0;
+ while ( (pos = s.find('\t')) > -1 )
+ {
+ l = tw - ( (col + pos)%tw );
+ s.replace( pos, 1, QString().fill( ' ', l ) );
+ }
+ }
+
+ editStart ();
+
+ editAddUndo (KateUndoGroup::editInsertText, line, col, s.length(), s);
+
+ l->insertText (col, s.length(), s.unicode());
+// removeTrailingSpace(line); // ### nessecary?
+
+ m_buffer->changeLine(line);
+
+ for( QPtrListIterator<KateSuperCursor> it (m_superCursors); it.current(); ++it )
+ it.current()->editTextInserted (line, col, s.length());
+
+ editEnd ();
+
+ return true;
+}
+
+bool KateDocument::editRemoveText ( uint line, uint col, uint len )
+{
+ if (!isReadWrite())
+ return false;
+
+ KateTextLine::Ptr l = m_buffer->line(line);
+
+ if (!l)
+ return false;
+
+ editStart ();
+
+ editAddUndo (KateUndoGroup::editRemoveText, line, col, len, l->string().mid(col, len));
+
+ l->removeText (col, len);
+ removeTrailingSpace( line );
+
+ m_buffer->changeLine(line);
+
+ for( QPtrListIterator<KateSuperCursor> it (m_superCursors); it.current(); ++it )
+ it.current()->editTextRemoved (line, col, len);
+
+ editEnd ();
+
+ return true;
+}
+
+bool KateDocument::editMarkLineAutoWrapped ( uint line, bool autowrapped )
+{
+ if (!isReadWrite())
+ return false;
+
+ KateTextLine::Ptr l = m_buffer->line(line);
+
+ if (!l)
+ return false;
+
+ editStart ();
+
+ editAddUndo (KateUndoGroup::editMarkLineAutoWrapped, line, autowrapped ? 1 : 0, 0, QString::null);
+
+ l->setAutoWrapped (autowrapped);
+
+ m_buffer->changeLine(line);
+
+ editEnd ();
+
+ return true;
+}
+
+bool KateDocument::editWrapLine ( uint line, uint col, bool newLine, bool *newLineAdded)
+{
+ if (!isReadWrite())
+ return false;
+
+ KateTextLine::Ptr l = m_buffer->line(line);
+
+ if (!l)
+ return false;
+
+ editStart ();
+
+ KateTextLine::Ptr nextLine = m_buffer->line(line+1);
+
+ int pos = l->length() - col;
+
+ if (pos < 0)
+ pos = 0;
+
+ editAddUndo (KateUndoGroup::editWrapLine, line, col, pos, (!nextLine || newLine) ? "1" : "0");
+
+ if (!nextLine || newLine)
+ {
+ KateTextLine::Ptr textLine = new KateTextLine();
+
+ textLine->insertText (0, pos, l->text()+col, l->attributes()+col);
+ l->truncate(col);
+
+ m_buffer->insertLine (line+1, textLine);
+ m_buffer->changeLine(line);
+
+ QPtrList<KTextEditor::Mark> list;
+ for( QIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it )
+ {
+ if( it.current()->line >= line )
+ {
+ if ((col == 0) || (it.current()->line > line))
+ list.append( it.current() );
+ }
+ }
+
+ for( QPtrListIterator<KTextEditor::Mark> it( list ); it.current(); ++it )
+ {
+ KTextEditor::Mark* mark = m_marks.take( it.current()->line );
+ mark->line++;
+ m_marks.insert( mark->line, mark );
+ }
+
+ if( !list.isEmpty() )
+ emit marksChanged();
+
+ // yes, we added a new line !
+ if (newLineAdded)
+ (*newLineAdded) = true;
+ }
+ else
+ {
+ nextLine->insertText (0, pos, l->text()+col, l->attributes()+col);
+ l->truncate(col);
+
+ m_buffer->changeLine(line);
+ m_buffer->changeLine(line+1);
+
+ // no, no new line added !
+ if (newLineAdded)
+ (*newLineAdded) = false;
+ }
+
+ for( QPtrListIterator<KateSuperCursor> it (m_superCursors); it.current(); ++it )
+ it.current()->editLineWrapped (line, col, !nextLine || newLine);
+
+ editEnd ();
+
+ return true;
+}
+
+bool KateDocument::editUnWrapLine ( uint line, bool removeLine, uint length )
+{
+ if (!isReadWrite())
+ return false;
+
+ KateTextLine::Ptr l = m_buffer->line(line);
+ KateTextLine::Ptr nextLine = m_buffer->line(line+1);
+
+ if (!l || !nextLine)
+ return false;
+
+ editStart ();
+
+ uint col = l->length ();
+
+ editAddUndo (KateUndoGroup::editUnWrapLine, line, col, length, removeLine ? "1" : "0");
+
+ if (removeLine)
+ {
+ l->insertText (col, nextLine->length(), nextLine->text(), nextLine->attributes());
+
+ m_buffer->changeLine(line);
+ m_buffer->removeLine(line+1);
+ }
+ else
+ {
+ l->insertText (col, (nextLine->length() < length) ? nextLine->length() : length,
+ nextLine->text(), nextLine->attributes());
+ nextLine->removeText (0, (nextLine->length() < length) ? nextLine->length() : length);
+
+ m_buffer->changeLine(line);
+ m_buffer->changeLine(line+1);
+ }
+
+ QPtrList<KTextEditor::Mark> list;
+ for( QIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it )
+ {
+ if( it.current()->line >= line+1 )
+ list.append( it.current() );
+
+ if ( it.current()->line == line+1 )
+ {
+ KTextEditor::Mark* mark = m_marks.take( line );
+
+ if (mark)
+ {
+ it.current()->type |= mark->type;
+ }
+ }
+ }
+
+ for( QPtrListIterator<KTextEditor::Mark> it( list ); it.current(); ++it )
+ {
+ KTextEditor::Mark* mark = m_marks.take( it.current()->line );
+ mark->line--;
+ m_marks.insert( mark->line, mark );
+ }
+
+ if( !list.isEmpty() )
+ emit marksChanged();
+
+ for( QPtrListIterator<KateSuperCursor> it (m_superCursors); it.current(); ++it )
+ it.current()->editLineUnWrapped (line, col, removeLine, length);
+
+ editEnd ();
+
+ return true;
+}
+
+bool KateDocument::editInsertLine ( uint line, const QString &s )
+{
+ if (!isReadWrite())
+ return false;
+
+ if ( line > numLines() )
+ return false;
+
+ editStart ();
+
+ editAddUndo (KateUndoGroup::editInsertLine, line, 0, s.length(), s);
+
+ removeTrailingSpace( line ); // old line
+
+ KateTextLine::Ptr tl = new KateTextLine();
+ tl->insertText (0, s.length(), s.unicode(), 0);
+ m_buffer->insertLine(line, tl);
+ m_buffer->changeLine(line);
+
+ removeTrailingSpace( line ); // new line
+
+ QPtrList<KTextEditor::Mark> list;
+ for( QIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it )
+ {
+ if( it.current()->line >= line )
+ list.append( it.current() );
+ }
+
+ for( QPtrListIterator<KTextEditor::Mark> it( list ); it.current(); ++it )
+ {
+ KTextEditor::Mark* mark = m_marks.take( it.current()->line );
+ mark->line++;
+ m_marks.insert( mark->line, mark );
+ }
+
+ if( !list.isEmpty() )
+ emit marksChanged();
+
+ for( QPtrListIterator<KateSuperCursor> it (m_superCursors); it.current(); ++it )
+ it.current()->editLineInserted (line);
+
+ editEnd ();
+
+ return true;
+}
+
+bool KateDocument::editRemoveLine ( uint line )
+{
+ if (!isReadWrite())
+ return false;
+
+ if ( line > lastLine() )
+ return false;
+
+ if ( numLines() == 1 )
+ return editRemoveText (0, 0, m_buffer->line(0)->length());
+
+ editStart ();
+
+ editAddUndo (KateUndoGroup::editRemoveLine, line, 0, lineLength(line), textLine(line));
+
+ m_buffer->removeLine(line);
+
+ QPtrList<KTextEditor::Mark> list;
+ KTextEditor::Mark* rmark = 0;
+ for( QIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it )
+ {
+ if ( (it.current()->line > line) )
+ list.append( it.current() );
+ else if ( (it.current()->line == line) )
+ rmark = it.current();
+ }
+
+ if (rmark)
+ delete (m_marks.take (rmark->line));
+
+ for( QPtrListIterator<KTextEditor::Mark> it( list ); it.current(); ++it )
+ {
+ KTextEditor::Mark* mark = m_marks.take( it.current()->line );
+ mark->line--;
+ m_marks.insert( mark->line, mark );
+ }
+
+ if( !list.isEmpty() )
+ emit marksChanged();
+
+ for( QPtrListIterator<KateSuperCursor> it (m_superCursors); it.current(); ++it )
+ it.current()->editLineRemoved (line);
+
+ editEnd();
+
+ return true;
+}
+//END
+
+//BEGIN KTextEditor::UndoInterface stuff
+
+uint KateDocument::undoCount () const
+{
+ return undoItems.count ();
+}
+
+uint KateDocument::redoCount () const
+{
+ return redoItems.count ();
+}
+
+uint KateDocument::undoSteps () const
+{
+ return m_config->undoSteps();
+}
+
+void KateDocument::setUndoSteps(uint steps)
+{
+ m_config->setUndoSteps (steps);
+}
+
+void KateDocument::undo()
+{
+ m_isInUndo = true;
+ if ((undoItems.count() > 0) && undoItems.last())
+ {
+ clearSelection ();
+
+ undoItems.last()->undo();
+ redoItems.append (undoItems.last());
+ undoItems.removeLast ();
+ updateModified();
+
+ emit undoChanged ();
+ }
+ m_isInUndo = false;
+}
+
+void KateDocument::redo()
+{
+ m_isInUndo = true;
+ if ((redoItems.count() > 0) && redoItems.last())
+ {
+ clearSelection ();
+
+ redoItems.last()->redo();
+ undoItems.append (redoItems.last());
+ redoItems.removeLast ();
+ updateModified();
+
+ emit undoChanged ();
+ }
+ m_isInUndo = false;
+}
+
+void KateDocument::updateModified()
+{
+ /*
+ How this works:
+
+ After noticing that there where to many scenarios to take into
+ consideration when using 'if's to toggle the "Modified" flag
+ I came up with this baby, flexible and repetitive calls are
+ minimal.
+
+ A numeric unique pattern is generated by toggleing a set of bits,
+ each bit symbolizes a different state in the Undo Redo structure.
+
+ undoItems.isEmpty() != null BIT 1
+ redoItems.isEmpty() != null BIT 2
+ docWasSavedWhenUndoWasEmpty == true BIT 3
+ docWasSavedWhenRedoWasEmpty == true BIT 4
+ lastUndoGroupWhenSavedIsLastUndo BIT 5
+ lastUndoGroupWhenSavedIsLastRedo BIT 6
+ lastRedoGroupWhenSavedIsLastUndo BIT 7
+ lastRedoGroupWhenSavedIsLastRedo BIT 8
+
+ If you find a new pattern, please add it to the patterns array
+ */
+
+ unsigned char currentPattern = 0;
+ const unsigned char patterns[] = {5,16,24,26,88,90,93,133,144,149,165};
+ const unsigned char patternCount = sizeof(patterns);
+ KateUndoGroup* undoLast = 0;
+ KateUndoGroup* redoLast = 0;
+
+ if (undoItems.isEmpty())
+ {
+ currentPattern |= 1;
+ }
+ else
+ {
+ undoLast = undoItems.last();
+ }
+
+ if (redoItems.isEmpty())
+ {
+ currentPattern |= 2;
+ }
+ else
+ {
+ redoLast = redoItems.last();
+ }
+
+ if (docWasSavedWhenUndoWasEmpty) currentPattern |= 4;
+ if (docWasSavedWhenRedoWasEmpty) currentPattern |= 8;
+ if (lastUndoGroupWhenSaved == undoLast) currentPattern |= 16;
+ if (lastUndoGroupWhenSaved == redoLast) currentPattern |= 32;
+ if (lastRedoGroupWhenSaved == undoLast) currentPattern |= 64;
+ if (lastRedoGroupWhenSaved == redoLast) currentPattern |= 128;
+
+ // This will print out the pattern information
+
+ kdDebug(13020) << k_funcinfo
+ << "Pattern:" << static_cast<unsigned int>(currentPattern) << endl;
+
+ for (uint patternIndex = 0; patternIndex < patternCount; ++patternIndex)
+ {
+ if ( currentPattern == patterns[patternIndex] )
+ {
+ setModified( false );
+ kdDebug(13020) << k_funcinfo << "setting modified to false!" << endl;
+ break;
+ }
+ }
+}
+
+void KateDocument::clearUndo()
+{
+ undoItems.setAutoDelete (true);
+ undoItems.clear ();
+ undoItems.setAutoDelete (false);
+
+ lastUndoGroupWhenSaved = 0;
+ docWasSavedWhenUndoWasEmpty = false;
+
+ emit undoChanged ();
+}
+
+void KateDocument::clearRedo()
+{
+ redoItems.setAutoDelete (true);
+ redoItems.clear ();
+ redoItems.setAutoDelete (false);
+
+ lastRedoGroupWhenSaved = 0;
+ docWasSavedWhenRedoWasEmpty = false;
+
+ emit undoChanged ();
+}
+
+QPtrList<KTextEditor::Cursor> KateDocument::cursors () const
+{
+ return myCursors;
+}
+//END
+
+//BEGIN KTextEditor::SearchInterface stuff
+
+bool KateDocument::searchText (unsigned int startLine, unsigned int startCol, const QString &text, unsigned int *foundAtLine, unsigned int *foundAtCol, unsigned int *matchLen, bool casesensitive, bool backwards)
+{
+ if (text.isEmpty())
+ return false;
+
+ int line = startLine;
+ int col = startCol;
+
+ if (!backwards)
+ {
+ int searchEnd = lastLine();
+
+ while (line <= searchEnd)
+ {
+ KateTextLine::Ptr textLine = m_buffer->plainLine(line);
+
+ if (!textLine)
+ return false;
+
+ uint foundAt, myMatchLen;
+ bool found = textLine->searchText (col, text, &foundAt, &myMatchLen, casesensitive, false);
+
+ if (found)
+ {
+ (*foundAtLine) = line;
+ (*foundAtCol) = foundAt;
+ (*matchLen) = myMatchLen;
+ return true;
+ }
+
+ col = 0;
+ line++;
+ }
+ }
+ else
+ {
+ // backward search
+ int searchEnd = 0;
+
+ while (line >= searchEnd)
+ {
+ KateTextLine::Ptr textLine = m_buffer->plainLine(line);
+
+ if (!textLine)
+ return false;
+
+ uint foundAt, myMatchLen;
+ bool found = textLine->searchText (col, text, &foundAt, &myMatchLen, casesensitive, true);
+
+ if (found)
+ {
+ /* if ((uint) line == startLine && foundAt + myMatchLen >= (uint) col
+ && line == selectStart.line() && foundAt == (uint) selectStart.col()
+ && line == selectEnd.line() && foundAt + myMatchLen == (uint) selectEnd.col())
+ {
+ // To avoid getting stuck at one match we skip a match if it is already
+ // selected (most likely because it has just been found).
+ if (foundAt > 0)
+ col = foundAt - 1;
+ else {
+ if (--line >= 0)
+ col = lineLength(line);
+ }
+ continue;
+ }*/
+
+ (*foundAtLine) = line;
+ (*foundAtCol) = foundAt;
+ (*matchLen) = myMatchLen;
+ return true;
+ }
+
+ if (line >= 1)
+ col = lineLength(line-1);
+
+ line--;
+ }
+ }
+
+ return false;
+}
+
+bool KateDocument::searchText (unsigned int startLine, unsigned int startCol, const QRegExp &regexp, unsigned int *foundAtLine, unsigned int *foundAtCol, unsigned int *matchLen, bool backwards)
+{
+ kdDebug(13020)<<"KateDocument::searchText( "<<startLine<<", "<<startCol<<", "<<regexp.pattern()<<", "<<backwards<<" )"<<endl;
+ if (regexp.isEmpty() || !regexp.isValid())
+ return false;
+
+ int line = startLine;
+ int col = startCol;
+
+ if (!backwards)
+ {
+ int searchEnd = lastLine();
+
+ while (line <= searchEnd)
+ {
+ KateTextLine::Ptr textLine = m_buffer->plainLine(line);
+
+ if (!textLine)
+ return false;
+
+ uint foundAt, myMatchLen;
+ bool found = textLine->searchText (col, regexp, &foundAt, &myMatchLen, false);
+
+ if (found)
+ {
+ // A special case which can only occur when searching with a regular expression consisting
+ // only of a lookahead (e.g. ^(?=\{) for a function beginning without selecting '{').
+ if (myMatchLen == 0 && (uint) line == startLine && foundAt == (uint) col)
+ {
+ if (col < lineLength(line))
+ col++;
+ else {
+ line++;
+ col = 0;
+ }
+ continue;
+ }
+
+ (*foundAtLine) = line;
+ (*foundAtCol) = foundAt;
+ (*matchLen) = myMatchLen;
+ return true;
+ }
+
+ col = 0;
+ line++;
+ }
+ }
+ else
+ {
+ // backward search
+ int searchEnd = 0;
+
+ while (line >= searchEnd)
+ {
+ KateTextLine::Ptr textLine = m_buffer->plainLine(line);
+
+ if (!textLine)
+ return false;
+
+ uint foundAt, myMatchLen;
+ bool found = textLine->searchText (col, regexp, &foundAt, &myMatchLen, true);
+
+ if (found)
+ {
+ /*if ((uint) line == startLine && foundAt + myMatchLen >= (uint) col
+ && line == selectStart.line() && foundAt == (uint) selectStart.col()
+ && line == selectEnd.line() && foundAt + myMatchLen == (uint) selectEnd.col())
+ {
+ // To avoid getting stuck at one match we skip a match if it is already
+ // selected (most likely because it has just been found).
+ if (foundAt > 0)
+ col = foundAt - 1;
+ else {
+ if (--line >= 0)
+ col = lineLength(line);
+ }
+ continue;
+ }*/
+
+ (*foundAtLine) = line;
+ (*foundAtCol) = foundAt;
+ (*matchLen) = myMatchLen;
+ return true;
+ }
+
+ if (line >= 1)
+ col = lineLength(line-1);
+
+ line--;
+ }
+ }
+
+ return false;
+}
+//END
+
+//BEGIN KTextEditor::HighlightingInterface stuff
+
+uint KateDocument::hlMode ()
+{
+ return KateHlManager::self()->findHl(highlight());
+}
+
+bool KateDocument::setHlMode (uint mode)
+{
+ m_buffer->setHighlight (mode);
+
+ if (true)
+ {
+ setDontChangeHlOnSave();
+ return true;
+ }
+
+ return false;
+}
+
+void KateDocument::bufferHlChanged ()
+{
+ // update all views
+ makeAttribs(false);
+
+ emit hlChanged();
+}
+
+uint KateDocument::hlModeCount ()
+{
+ return KateHlManager::self()->highlights();
+}
+
+QString KateDocument::hlModeName (uint mode)
+{
+ return KateHlManager::self()->hlName (mode);
+}
+
+QString KateDocument::hlModeSectionName (uint mode)
+{
+ return KateHlManager::self()->hlSection (mode);
+}
+
+void KateDocument::setDontChangeHlOnSave()
+{
+ hlSetByUser = true;
+}
+//END
+
+//BEGIN KTextEditor::ConfigInterface stuff
+void KateDocument::readConfig(KConfig *config)
+{
+ config->setGroup("Kate Document Defaults");
+
+ // read max loadable blocks, more blocks will be swapped out
+ KateBuffer::setMaxLoadedBlocks (config->readNumEntry("Maximal Loaded Blocks", KateBuffer::maxLoadedBlocks()));
+
+ KateDocumentConfig::global()->readConfig (config);
+
+ config->setGroup("Kate View Defaults");
+ KateViewConfig::global()->readConfig (config);
+
+ config->setGroup("Kate Renderer Defaults");
+ KateRendererConfig::global()->readConfig (config);
+}
+
+void KateDocument::writeConfig(KConfig *config)
+{
+ config->setGroup("Kate Document Defaults");
+
+ // write max loadable blocks, more blocks will be swapped out
+ config->writeEntry("Maximal Loaded Blocks", KateBuffer::maxLoadedBlocks());
+
+ KateDocumentConfig::global()->writeConfig (config);
+
+ config->setGroup("Kate View Defaults");
+ KateViewConfig::global()->writeConfig (config);
+
+ config->setGroup("Kate Renderer Defaults");
+ KateRendererConfig::global()->writeConfig (config);
+}
+
+void KateDocument::readConfig()
+{
+ KConfig *config = kapp->config();
+ readConfig (config);
+}
+
+void KateDocument::writeConfig()
+{
+ KConfig *config = kapp->config();
+ writeConfig (config);
+ config->sync();
+}
+
+void KateDocument::readSessionConfig(KConfig *kconfig)
+{
+ // restore the url
+ KURL url (kconfig->readEntry("URL"));
+
+ // get the encoding
+ QString tmpenc=kconfig->readEntry("Encoding");
+ if (!tmpenc.isEmpty() && (tmpenc != encoding()))
+ setEncoding(tmpenc);
+
+ // open the file if url valid
+ if (!url.isEmpty() && url.isValid())
+ openURL (url);
+
+ // restore the hl stuff
+ m_buffer->setHighlight(KateHlManager::self()->nameFind(kconfig->readEntry("Highlighting")));
+
+ if (hlMode() > 0)
+ hlSetByUser = true;
+
+ // indent mode
+ config()->setIndentationMode( (uint)kconfig->readNumEntry("Indentation Mode", config()->indentationMode() ) );
+
+ // Restore Bookmarks
+ QValueList<int> marks = kconfig->readIntListEntry("Bookmarks");
+ for( uint i = 0; i < marks.count(); i++ )
+ addMark( marks[i], KateDocument::markType01 );
+}
+
+void KateDocument::writeSessionConfig(KConfig *kconfig)
+{
+ if ( m_url.isLocalFile() && !KGlobal::dirs()->relativeLocation("tmp", m_url.path()).startsWith("/"))
+ return;
+ // save url
+ kconfig->writeEntry("URL", m_url.prettyURL() );
+
+ // save encoding
+ kconfig->writeEntry("Encoding",encoding());
+
+ // save hl
+ kconfig->writeEntry("Highlighting", highlight()->name());
+
+ kconfig->writeEntry("Indentation Mode", config()->indentationMode() );
+
+ // Save Bookmarks
+ QValueList<int> marks;
+ for( QIntDictIterator<KTextEditor::Mark> it( m_marks );
+ it.current() && it.current()->type & KTextEditor::MarkInterface::markType01;
+ ++it )
+ marks << it.current()->line;
+
+ kconfig->writeEntry( "Bookmarks", marks );
+}
+
+void KateDocument::configDialog()
+{
+ KDialogBase *kd = new KDialogBase ( KDialogBase::IconList,
+ i18n("Configure"),
+ KDialogBase::Ok | KDialogBase::Cancel | KDialogBase::Help,
+ KDialogBase::Ok,
+ kapp->mainWidget() );
+
+#ifndef Q_WS_WIN //TODO: reenable
+ KWin::setIcons( kd->winId(), kapp->icon(), kapp->miniIcon() );
+#endif
+
+ QPtrList<KTextEditor::ConfigPage> editorPages;
+
+ for (uint i = 0; i < KTextEditor::configInterfaceExtension (this)->configPages (); i++)
+ {
+ QStringList path;
+ path.clear();
+ path << KTextEditor::configInterfaceExtension (this)->configPageName (i);
+ QVBox *page = kd->addVBoxPage(path, KTextEditor::configInterfaceExtension (this)->configPageFullName (i),
+ KTextEditor::configInterfaceExtension (this)->configPagePixmap(i, KIcon::SizeMedium) );
+
+ editorPages.append (KTextEditor::configInterfaceExtension (this)->configPage(i, page));
+ }
+
+ if (kd->exec())
+ {
+ KateDocumentConfig::global()->configStart ();
+ KateViewConfig::global()->configStart ();
+ KateRendererConfig::global()->configStart ();
+
+ for (uint i=0; i<editorPages.count(); i++)
+ {
+ editorPages.at(i)->apply();
+ }
+
+ KateDocumentConfig::global()->configEnd ();
+ KateViewConfig::global()->configEnd ();
+ KateRendererConfig::global()->configEnd ();
+
+ writeConfig ();
+ }
+
+ delete kd;
+}
+
+uint KateDocument::mark( uint line )
+{
+ if( !m_marks[line] )
+ return 0;
+ return m_marks[line]->type;
+}
+
+void KateDocument::setMark( uint line, uint markType )
+{
+ clearMark( line );
+ addMark( line, markType );
+}
+
+void KateDocument::clearMark( uint line )
+{
+ if( line > lastLine() )
+ return;
+
+ if( !m_marks[line] )
+ return;
+
+ KTextEditor::Mark* mark = m_marks.take( line );
+ emit markChanged( *mark, MarkRemoved );
+ emit marksChanged();
+ delete mark;
+ tagLines( line, line );
+ repaintViews(true);
+}
+
+void KateDocument::addMark( uint line, uint markType )
+{
+ if( line > lastLine())
+ return;
+
+ if( markType == 0 )
+ return;
+
+ if( m_marks[line] ) {
+ KTextEditor::Mark* mark = m_marks[line];
+
+ // Remove bits already set
+ markType &= ~mark->type;
+
+ if( markType == 0 )
+ return;
+
+ // Add bits
+ mark->type |= markType;
+ } else {
+ KTextEditor::Mark *mark = new KTextEditor::Mark;
+ mark->line = line;
+ mark->type = markType;
+ m_marks.insert( line, mark );
+ }
+
+ // Emit with a mark having only the types added.
+ KTextEditor::Mark temp;
+ temp.line = line;
+ temp.type = markType;
+ emit markChanged( temp, MarkAdded );
+
+ emit marksChanged();
+ tagLines( line, line );
+ repaintViews(true);
+}
+
+void KateDocument::removeMark( uint line, uint markType )
+{
+ if( line > lastLine() )
+ return;
+ if( !m_marks[line] )
+ return;
+
+ KTextEditor::Mark* mark = m_marks[line];
+
+ // Remove bits not set
+ markType &= mark->type;
+
+ if( markType == 0 )
+ return;
+
+ // Subtract bits
+ mark->type &= ~markType;
+
+ // Emit with a mark having only the types removed.
+ KTextEditor::Mark temp;
+ temp.line = line;
+ temp.type = markType;
+ emit markChanged( temp, MarkRemoved );
+
+ if( mark->type == 0 )
+ m_marks.remove( line );
+
+ emit marksChanged();
+ tagLines( line, line );
+ repaintViews(true);
+}
+
+QPtrList<KTextEditor::Mark> KateDocument::marks()
+{
+ QPtrList<KTextEditor::Mark> list;
+
+ for( QIntDictIterator<KTextEditor::Mark> it( m_marks );
+ it.current(); ++it ) {
+ list.append( it.current() );
+ }
+
+ return list;
+}
+
+void KateDocument::clearMarks()
+{
+ for( QIntDictIterator<KTextEditor::Mark> it( m_marks );
+ it.current(); ++it ) {
+ KTextEditor::Mark* mark = it.current();
+ emit markChanged( *mark, MarkRemoved );
+ tagLines( mark->line, mark->line );
+ }
+
+ m_marks.clear();
+
+ emit marksChanged();
+ repaintViews(true);
+}
+
+void KateDocument::setPixmap( MarkInterface::MarkTypes type, const QPixmap& pixmap )
+{
+ m_markPixmaps.replace( type, new QPixmap( pixmap ) );
+}
+
+void KateDocument::setDescription( MarkInterface::MarkTypes type, const QString& description )
+{
+ m_markDescriptions.replace( type, new QString( description ) );
+}
+
+QPixmap *KateDocument::markPixmap( MarkInterface::MarkTypes type )
+{
+ return m_markPixmaps[type];
+}
+
+QColor KateDocument::markColor( MarkInterface::MarkTypes type )
+{
+ uint reserved = (0x1 << KTextEditor::MarkInterface::reservedMarkersCount()) - 1;
+ if ((uint)type >= (uint)markType01 && (uint)type <= reserved) {
+ return KateRendererConfig::global()->lineMarkerColor(type);
+ } else {
+ return QColor();
+ }
+}
+
+QString KateDocument::markDescription( MarkInterface::MarkTypes type )
+{
+ if( m_markDescriptions[type] )
+ return *m_markDescriptions[type];
+ return QString::null;
+}
+
+void KateDocument::setMarksUserChangable( uint markMask )
+{
+ m_editableMarks = markMask;
+}
+
+uint KateDocument::editableMarks()
+{
+ return m_editableMarks;
+}
+//END
+
+//BEGIN KTextEditor::PrintInterface stuff
+bool KateDocument::printDialog ()
+{
+ return KatePrinter::print (this);
+}
+
+bool KateDocument::print ()
+{
+ return KatePrinter::print (this);
+}
+//END
+
+//BEGIN KTextEditor::DocumentInfoInterface (### unfinished)
+QString KateDocument::mimeType()
+{
+ KMimeType::Ptr result = KMimeType::defaultMimeTypePtr();
+
+ // if the document has a URL, try KMimeType::findByURL
+ if ( ! m_url.isEmpty() )
+ result = KMimeType::findByURL( m_url );
+
+ else if ( m_url.isEmpty() || ! m_url.isLocalFile() )
+ result = mimeTypeForContent();
+
+ return result->name();
+}
+
+// TODO implement this -- how to calculate?
+long KateDocument::fileSize()
+{
+ return 0;
+}
+
+// TODO implement this
+QString KateDocument::niceFileSize()
+{
+ return "UNKNOWN";
+}
+
+KMimeType::Ptr KateDocument::mimeTypeForContent()
+{
+ QByteArray buf (1024);
+ uint bufpos = 0;
+
+ for (uint i=0; i < numLines(); i++)
+ {
+ QString line = textLine( i );
+ uint len = line.length() + 1;
+
+ if (bufpos + len > 1024)
+ len = 1024 - bufpos;
+
+ memcpy(&buf[bufpos], (line + "\n").latin1(), len);
+
+ bufpos += len;
+
+ if (bufpos >= 1024)
+ break;
+ }
+ buf.resize( bufpos );
+
+ int accuracy = 0;
+ return KMimeType::findByContent( buf, &accuracy );
+}
+//END KTextEditor::DocumentInfoInterface
+
+
+//BEGIN KParts::ReadWrite stuff
+
+bool KateDocument::openURL( const KURL &url )
+{
+// kdDebug(13020)<<"KateDocument::openURL( "<<url.prettyURL()<<")"<<endl;
+ // no valid URL
+ if ( !url.isValid() )
+ return false;
+
+ // could not close old one
+ if ( !closeURL() )
+ return false;
+
+ // set my url
+ m_url = url;
+
+ if ( m_url.isLocalFile() )
+ {
+ // local mode, just like in kpart
+
+ m_file = m_url.path();
+
+ emit started( 0 );
+
+ if (openFile())
+ {
+ emit completed();
+ emit setWindowCaption( m_url.prettyURL() );
+
+ return true;
+ }
+
+ return false;
+ }
+ else
+ {
+ // remote mode
+
+ m_bTemp = true;
+
+ m_tempFile = new KTempFile ();
+ m_file = m_tempFile->name();
+
+ m_job = KIO::get ( url, false, isProgressInfoEnabled() );
+
+ // connect to slots
+ connect( m_job, SIGNAL( data( KIO::Job*, const QByteArray& ) ),
+ SLOT( slotDataKate( KIO::Job*, const QByteArray& ) ) );
+
+ connect( m_job, SIGNAL( result( KIO::Job* ) ),
+ SLOT( slotFinishedKate( KIO::Job* ) ) );
+
+ QWidget *w = widget ();
+ if (!w && !m_views.isEmpty ())
+ w = m_views.first();
+
+ if (w)
+ m_job->setWindow (w->topLevelWidget());
+
+ emit started( m_job );
+
+ return true;
+ }
+}
+
+void KateDocument::slotDataKate ( KIO::Job *, const QByteArray &data )
+{
+// kdDebug(13020) << "KateDocument::slotData" << endl;
+
+ if (!m_tempFile || !m_tempFile->file())
+ return;
+
+ m_tempFile->file()->writeBlock (data);
+}
+
+void KateDocument::slotFinishedKate ( KIO::Job * job )
+{
+// kdDebug(13020) << "KateDocument::slotJobFinished" << endl;
+
+ if (!m_tempFile)
+ return;
+
+ delete m_tempFile;
+ m_tempFile = 0;
+ m_job = 0;
+
+ if (job->error())
+ emit canceled( job->errorString() );
+ else
+ {
+ if ( openFile(job) )
+ emit setWindowCaption( m_url.prettyURL() );
+ emit completed();
+ }
+}
+
+void KateDocument::abortLoadKate()
+{
+ if ( m_job )
+ {
+ kdDebug(13020) << "Aborting job " << m_job << endl;
+ m_job->kill();
+ m_job = 0;
+ }
+
+ delete m_tempFile;
+ m_tempFile = 0;
+}
+
+bool KateDocument::openFile()
+{
+ return openFile (0);
+}
+
+bool KateDocument::openFile(KIO::Job * job)
+{
+ m_loading = true;
+ // add new m_file to dirwatch
+ activateDirWatch ();
+
+ //
+ // use metadata
+ //
+ if (job)
+ {
+ QString metaDataCharset = job->queryMetaData("charset");
+
+ // only overwrite config if nothing set
+ if (!metaDataCharset.isEmpty () && (!m_config->isSetEncoding() || m_config->encoding().isEmpty()))
+ setEncoding (metaDataCharset);
+ }
+
+ //
+ // service type magic to get encoding right
+ //
+ QString serviceType = m_extension->urlArgs().serviceType.simplifyWhiteSpace();
+ int pos = serviceType.find(';');
+ if (pos != -1)
+ setEncoding (serviceType.mid(pos+1));
+
+ // if the encoding is set here - on the command line/from the dialog/from KIO
+ // we prevent file type and document variables from changing it
+ bool encodingSticky = m_encodingSticky;
+ m_encodingSticky = m_config->isSetEncoding();
+
+ // Try getting the filetype here, so that variables does not have to be reset.
+ int fileTypeFound = KateFactory::self()->fileTypeManager()->fileType (this);
+ if ( fileTypeFound > -1 )
+ updateFileType( fileTypeFound );
+
+ // do we have success ?
+ bool success = m_buffer->openFile (m_file);
+ //
+ // yeah, success
+ //
+ m_loading = false; // done reading file.
+ if (success)
+ {
+ /*if (highlight() && !m_url.isLocalFile()) {
+ // The buffer's highlighting gets nuked by KateBuffer::clear()
+ m_buffer->setHighlight(m_highlight);
+ }*/
+
+ // update our hl type if needed
+ if (!hlSetByUser)
+ {
+ int hl (KateHlManager::self()->detectHighlighting (this));
+
+ if (hl >= 0)
+ m_buffer->setHighlight(hl);
+ }
+
+ // update file type if we haven't allready done so.
+ if ( fileTypeFound < 0 )
+ updateFileType (KateFactory::self()->fileTypeManager()->fileType (this));
+
+ // read dir config (if possible and wanted)
+ readDirConfig ();
+
+ // read vars
+ readVariables();
+
+ // update the md5 digest
+ createDigest( m_digest );
+ }
+
+ //
+ // update views
+ //
+ for (KateView * view = m_views.first(); view != 0L; view = m_views.next() )
+ {
+ view->updateView(true);
+ }
+
+ //
+ // emit the signal we need for example for kate app
+ //
+ emit fileNameChanged ();
+
+ //
+ // set doc name, dummy value as arg, don't need it
+ //
+ setDocName (QString::null);
+
+ //
+ // to houston, we are not modified
+ //
+ if (m_modOnHd)
+ {
+ m_modOnHd = false;
+ m_modOnHdReason = 0;
+ emit modifiedOnDisc (this, m_modOnHd, 0);
+ }
+
+ //
+ // display errors
+ //
+ if (s_openErrorDialogsActivated)
+ {
+ if (!success && m_buffer->loadingBorked())
+ KMessageBox::error (widget(), i18n ("The file %1 could not be loaded completely, as there is not enough temporary disk storage for it.").arg(m_url.url()));
+ else if (!success)
+ KMessageBox::error (widget(), i18n ("The file %1 could not be loaded, as it was not possible to read from it.\n\nCheck if you have read access to this file.").arg(m_url.url()));
+ }
+
+ // warn -> opened binary file!!!!!!!
+ if (m_buffer->binary())
+ {
+ // this file can't be saved again without killing it
+ setReadWrite( false );
+
+ KMessageBox::information (widget()
+ , i18n ("The file %1 is a binary, saving it will result in a corrupt file.").arg(m_url.url())
+ , i18n ("Binary File Opened")
+ , "Binary File Opened Warning");
+ }
+
+ m_encodingSticky = encodingSticky;
+
+ //
+ // return the success
+ //
+ return success;
+}
+
+bool KateDocument::save()
+{
+ bool l ( url().isLocalFile() );
+
+ if ( ( l && config()->backupFlags() & KateDocumentConfig::LocalFiles )
+ || ( ! l && config()->backupFlags() & KateDocumentConfig::RemoteFiles ) )
+ {
+ KURL u( url() );
+ u.setFileName( config()->backupPrefix() + url().fileName() + config()->backupSuffix() );
+
+ kdDebug () << "backup src file name: " << url() << endl;
+ kdDebug () << "backup dst file name: " << u << endl;
+
+ // get the right permissions, start with safe default
+ mode_t perms = 0600;
+ KIO::UDSEntry fentry;
+ if (KIO::NetAccess::stat (url(), fentry, kapp->mainWidget()))
+ {
+ kdDebug () << "stating succesfull: " << url() << endl;
+ KFileItem item (fentry, url());
+ perms = item.permissions();
+ }
+
+ // first del existing file if any, than copy over the file we have
+ // failure if a: the existing file could not be deleted, b: the file could not be copied
+ if ( (!KIO::NetAccess::exists( u, false, kapp->mainWidget() ) || KIO::NetAccess::del( u, kapp->mainWidget() ))
+ && KIO::NetAccess::file_copy( url(), u, perms, true, false, kapp->mainWidget() ) )
+ {
+ kdDebug(13020)<<"backing up successfull ("<<url().prettyURL()<<" -> "<<u.prettyURL()<<")"<<endl;
+ }
+ else
+ {
+ kdDebug(13020)<<"backing up failed ("<<url().prettyURL()<<" -> "<<u.prettyURL()<<")"<<endl;
+ // FIXME: notify user for real ;)
+ }
+ }
+
+ return KParts::ReadWritePart::save();
+}
+
+bool KateDocument::saveFile()
+{
+ //
+ // we really want to save this file ?
+ //
+ if (m_buffer->loadingBorked() && (KMessageBox::warningContinueCancel(widget(),
+ i18n("This file could not be loaded correctly due to lack of temporary disk space. Saving it could cause data loss.\n\nDo you really want to save it?"),i18n("Possible Data Loss"),i18n("Save Nevertheless")) != KMessageBox::Continue))
+ return false;
+
+ //
+ // warn -> try to save binary file!!!!!!!
+ //
+ if (m_buffer->binary() && (KMessageBox::warningContinueCancel (widget()
+ , i18n ("The file %1 is a binary, saving it will result in a corrupt file.").arg(m_url.url())
+ , i18n ("Trying to Save Binary File")
+ , i18n("Save Nevertheless"), "Binary File Save Warning") != KMessageBox::Continue))
+ return false;
+
+ if ( !url().isEmpty() )
+ {
+ if (s_fileChangedDialogsActivated && m_modOnHd)
+ {
+ QString str = reasonedMOHString() + "\n\n";
+
+ if (!isModified())
+ {
+ if (KMessageBox::warningContinueCancel(0,
+ str + i18n("Do you really want to save this unmodified file? You could overwrite changed data in the file on disk."),i18n("Trying to Save Unmodified File"),i18n("Save Nevertheless")) != KMessageBox::Continue)
+ return false;
+ }
+ else
+ {
+ if (KMessageBox::warningContinueCancel(0,
+ str + i18n("Do you really want to save this file? Both your open file and the file on disk were changed. There could be some data lost."),i18n("Possible Data Loss"),i18n("Save Nevertheless")) != KMessageBox::Continue)
+ return false;
+ }
+ }
+ }
+
+ //
+ // can we encode it if we want to save it ?
+ //
+ if (!m_buffer->canEncode ()
+ && (KMessageBox::warningContinueCancel(0,
+ i18n("The selected encoding cannot encode every unicode character in this document. Do you really want to save it? There could be some data lost."),i18n("Possible Data Loss"),i18n("Save Nevertheless")) != KMessageBox::Continue))
+ {
+ return false;
+ }
+
+ // remove file from dirwatch
+ deactivateDirWatch ();
+
+ //
+ // try to save
+ //
+ bool success = m_buffer->saveFile (m_file);
+
+ // update the md5 digest
+ createDigest( m_digest );
+
+ // add m_file again to dirwatch
+ activateDirWatch ();
+
+ //
+ // hurray, we had success, do stuff we need
+ //
+ if (success)
+ {
+ // update our hl type if needed
+ if (!hlSetByUser)
+ {
+ int hl (KateHlManager::self()->detectHighlighting (this));
+
+ if (hl >= 0)
+ m_buffer->setHighlight(hl);
+ }
+
+ // read our vars
+ readVariables();
+ }
+
+ //
+ // we are not modified
+ //
+ if (success && m_modOnHd)
+ {
+ m_modOnHd = false;
+ m_modOnHdReason = 0;
+ emit modifiedOnDisc (this, m_modOnHd, 0);
+ }
+
+ //
+ // display errors
+ //
+ if (!success)
+ KMessageBox::error (widget(), i18n ("The document could not be saved, as it was not possible to write to %1.\n\nCheck that you have write access to this file or that enough disk space is available.").arg(m_url.url()));
+
+ //
+ // return success
+ //
+ return success;
+}
+
+bool KateDocument::saveAs( const KURL &u )
+{
+ QString oldDir = url().directory();
+
+ if ( KParts::ReadWritePart::saveAs( u ) )
+ {
+ // null means base on filename
+ setDocName( QString::null );
+
+ if ( u.directory() != oldDir )
+ readDirConfig();
+
+ emit fileNameChanged();
+ emit nameChanged((Kate::Document *) this);
+
+ return true;
+ }
+
+ return false;
+}
+
+void KateDocument::readDirConfig ()
+{
+ int depth = config()->searchDirConfigDepth ();
+
+ if (m_url.isLocalFile() && (depth > -1))
+ {
+ QString currentDir = QFileInfo (m_file).dirPath();
+
+ // only search as deep as specified or not at all ;)
+ while (depth > -1)
+ {
+ kdDebug (13020) << "search for config file in path: " << currentDir << endl;
+
+ // try to open config file in this dir
+ QFile f (currentDir + "/.kateconfig");
+
+ if (f.open (IO_ReadOnly))
+ {
+ QTextStream stream (&f);
+
+ uint linesRead = 0;
+ QString line = stream.readLine();
+ while ((linesRead < 32) && !line.isNull())
+ {
+ readVariableLine( line );
+
+ line = stream.readLine();
+
+ linesRead++;
+ }
+
+ break;
+ }
+
+ QString newDir = QFileInfo (currentDir).dirPath();
+
+ // bail out on looping (for example reached /)
+ if (currentDir == newDir)
+ break;
+
+ currentDir = newDir;
+ --depth;
+ }
+ }
+}
+
+void KateDocument::activateDirWatch ()
+{
+ // same file as we are monitoring, return
+ if (m_file == m_dirWatchFile)
+ return;
+
+ // remove the old watched file
+ deactivateDirWatch ();
+
+ // add new file if needed
+ if (m_url.isLocalFile() && !m_file.isEmpty())
+ {
+ KateFactory::self()->dirWatch ()->addFile (m_file);
+ m_dirWatchFile = m_file;
+ }
+}
+
+void KateDocument::deactivateDirWatch ()
+{
+ if (!m_dirWatchFile.isEmpty())
+ KateFactory::self()->dirWatch ()->removeFile (m_dirWatchFile);
+
+ m_dirWatchFile = QString::null;
+}
+
+bool KateDocument::closeURL()
+{
+ abortLoadKate();
+
+ //
+ // file mod on hd
+ //
+ if ( !m_reloading && !url().isEmpty() )
+ {
+ if (s_fileChangedDialogsActivated && m_modOnHd)
+ {
+ if (!(KMessageBox::warningContinueCancel(
+ widget(),
+ reasonedMOHString() + "\n\n" + i18n("Do you really want to continue to close this file? Data loss may occur."),
+ i18n("Possible Data Loss"), i18n("Close Nevertheless"),
+ QString("kate_close_modonhd_%1").arg( m_modOnHdReason ) ) == KMessageBox::Continue))
+ return false;
+ }
+ }
+
+ //
+ // first call the normal kparts implementation
+ //
+ if (!KParts::ReadWritePart::closeURL ())
+ return false;
+
+ // remove file from dirwatch
+ deactivateDirWatch ();
+
+ //
+ // empty url + filename
+ //
+ m_url = KURL ();
+ m_file = QString::null;
+
+ // we are not modified
+ if (m_modOnHd)
+ {
+ m_modOnHd = false;
+ m_modOnHdReason = 0;
+ emit modifiedOnDisc (this, m_modOnHd, 0);
+ }
+
+ // clear the buffer
+ m_buffer->clear();
+
+ // remove all marks
+ clearMarks ();
+
+ // clear undo/redo history
+ clearUndo();
+ clearRedo();
+
+ // no, we are no longer modified
+ setModified(false);
+
+ // we have no longer any hl
+ m_buffer->setHighlight(0);
+
+ // update all our views
+ for (KateView * view = m_views.first(); view != 0L; view = m_views.next() )
+ {
+ // Explicitly call the internal version because we don't want this to look like
+ // an external request (and thus have the view not QWidget::scroll()ed.
+ view->setCursorPositionInternal(0, 0, 1, false);
+ view->clearSelection();
+ view->updateView(true);
+ }
+
+ // uh, filename changed
+ emit fileNameChanged ();
+
+ // update doc name
+ setDocName (QString::null);
+
+ // success
+ return true;
+}
+
+void KateDocument::setReadWrite( bool rw )
+{
+ if (isReadWrite() != rw)
+ {
+ KParts::ReadWritePart::setReadWrite (rw);
+
+ for( KateView* view = m_views.first(); view != 0L; view = m_views.next() )
+ {
+ view->slotUpdate();
+ view->slotReadWriteChanged ();
+ }
+ }
+}
+
+void KateDocument::setModified(bool m) {
+
+ if (isModified() != m) {
+ KParts::ReadWritePart::setModified (m);
+
+ for( KateView* view = m_views.first(); view != 0L; view = m_views.next() )
+ {
+ view->slotUpdate();
+ }
+
+ emit modifiedChanged ();
+ emit modStateChanged ((Kate::Document *)this);
+ }
+ if ( m == false )
+ {
+ if ( ! undoItems.isEmpty() )
+ {
+ lastUndoGroupWhenSaved = undoItems.last();
+ }
+
+ if ( ! redoItems.isEmpty() )
+ {
+ lastRedoGroupWhenSaved = redoItems.last();
+ }
+
+ docWasSavedWhenUndoWasEmpty = undoItems.isEmpty();
+ docWasSavedWhenRedoWasEmpty = redoItems.isEmpty();
+ }
+}
+//END
+
+//BEGIN Kate specific stuff ;)
+
+void KateDocument::makeAttribs(bool needInvalidate)
+{
+ for (uint z = 0; z < m_views.count(); z++)
+ m_views.at(z)->renderer()->updateAttributes ();
+
+ if (needInvalidate)
+ m_buffer->invalidateHighlighting();
+
+ tagAll ();
+}
+
+// the attributes of a hl have changed, update
+void KateDocument::internalHlChanged()
+{
+ makeAttribs();
+}
+
+void KateDocument::addView(KTextEditor::View *view) {
+ if (!view)
+ return;
+
+ m_views.append( (KateView *) view );
+ m_textEditViews.append( view );
+
+ // apply the view & renderer vars from the file type
+ const KateFileType *t = 0;
+ if ((m_fileType > -1) && (t = KateFactory::self()->fileTypeManager()->fileType(m_fileType)))
+ readVariableLine (t->varLine, true);
+
+ // apply the view & renderer vars from the file
+ readVariables (true);
+
+ m_activeView = (KateView *) view;
+}
+
+void KateDocument::removeView(KTextEditor::View *view) {
+ if (!view)
+ return;
+
+ if (m_activeView == view)
+ m_activeView = 0L;
+
+ m_views.removeRef( (KateView *) view );
+ m_textEditViews.removeRef( view );
+}
+
+void KateDocument::addSuperCursor(KateSuperCursor *cursor, bool privateC) {
+ if (!cursor)
+ return;
+
+ m_superCursors.append( cursor );
+
+ if (!privateC)
+ myCursors.append( cursor );
+}
+
+void KateDocument::removeSuperCursor(KateSuperCursor *cursor, bool privateC) {
+ if (!cursor)
+ return;
+
+ if (!privateC)
+ myCursors.removeRef( cursor );
+
+ m_superCursors.removeRef( cursor );
+}
+
+bool KateDocument::ownedView(KateView *view) {
+ // do we own the given view?
+ return (m_views.containsRef(view) > 0);
+}
+
+bool KateDocument::isLastView(int numViews) {
+ return ((int) m_views.count() == numViews);
+}
+
+uint KateDocument::currentColumn( const KateTextCursor& cursor )
+{
+ KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
+
+ if (textLine)
+ return textLine->cursorX(cursor.col(), config()->tabWidth());
+ else
+ return 0;
+}
+
+bool KateDocument::typeChars ( KateView *view, const QString &chars )
+{
+ KateTextLine::Ptr textLine = m_buffer->plainLine(view->cursorLine ());
+
+ if (!textLine)
+ return false;
+
+ bool bracketInserted = false;
+ QString buf;
+ QChar c;
+
+ for( uint z = 0; z < chars.length(); z++ )
+ {
+ QChar ch = c = chars[z];
+ if (ch.isPrint() || ch == '\t')
+ {
+ buf.append (ch);
+
+ if (!bracketInserted && (config()->configFlags() & KateDocument::cfAutoBrackets))
+ {
+ QChar end_ch;
+ bool complete = true;
+ QChar prevChar = textLine->getChar(view->cursorColumnReal()-1);
+ QChar nextChar = textLine->getChar(view->cursorColumnReal());
+ switch(ch) {
+ case '(': end_ch = ')'; break;
+ case '[': end_ch = ']'; break;
+ case '{': end_ch = '}'; break;
+ case '\'':end_ch = '\'';break;
+ case '"': end_ch = '"'; break;
+ default: complete = false;
+ }
+ if (complete)
+ {
+ if (view->hasSelection())
+ { // there is a selection, enclose the selection
+ buf.append (view->selection());
+ buf.append (end_ch);
+ bracketInserted = true;
+ }
+ else
+ { // no selection, check whether we should better refuse to complete
+ if ( ( (ch == '\'' || ch == '"') &&
+ (prevChar.isLetterOrNumber() || prevChar == ch) )
+ || nextChar.isLetterOrNumber()
+ || (nextChar == end_ch && prevChar != ch) )
+ {
+ kdDebug(13020) << "AutoBracket refused before: " << nextChar << "\n";
+ }
+ else
+ {
+ buf.append (end_ch);
+ bracketInserted = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (buf.isEmpty())
+ return false;
+
+ editStart ();
+
+ if (!view->config()->persistentSelection() && view->hasSelection() )
+ view->removeSelectedText();
+
+ int oldLine = view->cursorLine ();
+ int oldCol = view->cursorColumnReal ();
+
+
+ if (config()->configFlags() & KateDocument::cfOvr)
+ removeText (view->cursorLine(), view->cursorColumnReal(), view->cursorLine(), kMin( view->cursorColumnReal()+buf.length(), textLine->length() ) );
+
+ insertText (view->cursorLine(), view->cursorColumnReal(), buf);
+ m_indenter->processChar(c);
+
+ editEnd ();
+
+ if (bracketInserted)
+ view->setCursorPositionInternal (view->cursorLine(), view->cursorColumnReal()-1);
+
+ emit charactersInteractivelyInserted (oldLine, oldCol, chars);
+
+ return true;
+}
+
+void KateDocument::newLine( KateTextCursor& c, KateViewInternal *v )
+{
+ editStart();
+
+ if( !v->view()->config()->persistentSelection() && v->view()->hasSelection() )
+ v->view()->removeSelectedText();
+
+ // temporary hack to get the cursor pos right !!!!!!!!!
+ c = v->getCursor ();
+
+ if (c.line() > (int)lastLine())
+ c.setLine(lastLine());
+
+ if ( c.line() < 0 )
+ c.setLine( 0 );
+
+ uint ln = c.line();
+
+ KateTextLine::Ptr textLine = kateTextLine(c.line());
+
+ if (c.col() > (int)textLine->length())
+ c.setCol(textLine->length());
+
+ if (m_indenter->canProcessNewLine ())
+ {
+ int pos = textLine->firstChar();
+
+ // length should do the job better
+ if (pos < 0)
+ pos = textLine->length();
+
+ if (c.col() < pos)
+ c.setCol(pos); // place cursor on first char if before
+
+ editWrapLine (c.line(), c.col());
+
+ KateDocCursor cursor (c.line() + 1, pos, this);
+ m_indenter->processNewline(cursor, true);
+
+ c.setPos(cursor);
+ }
+ else
+ {
+ editWrapLine (c.line(), c.col());
+ c.setPos(c.line() + 1, 0);
+ }
+
+ removeTrailingSpace( ln );
+
+ editEnd();
+}
+
+void KateDocument::transpose( const KateTextCursor& cursor)
+{
+ KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
+
+ if (!textLine || (textLine->length() < 2))
+ return;
+
+ uint col = cursor.col();
+
+ if (col > 0)
+ col--;
+
+ if ((textLine->length() - col) < 2)
+ return;
+
+ uint line = cursor.line();
+ QString s;
+
+ //clever swap code if first character on the line swap right&left
+ //otherwise left & right
+ s.append (textLine->getChar(col+1));
+ s.append (textLine->getChar(col));
+ //do the swap
+
+ // do it right, never ever manipulate a textline
+ editStart ();
+ editRemoveText (line, col, 2);
+ editInsertText (line, col, s);
+ editEnd ();
+}
+
+void KateDocument::backspace( KateView *view, const KateTextCursor& c )
+{
+ if ( !view->config()->persistentSelection() && view->hasSelection() ) {
+ view->removeSelectedText();
+ return;
+ }
+
+ uint col = kMax( c.col(), 0 );
+ uint line = kMax( c.line(), 0 );
+
+ if ((col == 0) && (line == 0))
+ return;
+
+ int complement = 0;
+ if (col > 0)
+ {
+ if (config()->configFlags() & KateDocument::cfAutoBrackets)
+ {
+ // if inside empty (), {}, [], '', "" delete both
+ KateTextLine::Ptr tl = m_buffer->plainLine(line);
+ if(!tl) return;
+ QChar prevChar = tl->getChar(col-1);
+ QChar nextChar = tl->getChar(col);
+
+ if ( (prevChar == '"' && nextChar == '"') ||
+ (prevChar == '\'' && nextChar == '\'') ||
+ (prevChar == '(' && nextChar == ')') ||
+ (prevChar == '[' && nextChar == ']') ||
+ (prevChar == '{' && nextChar == '}') )
+ {
+ complement = 1;
+ }
+ }
+ if (!(config()->configFlags() & KateDocument::cfBackspaceIndents))
+ {
+ // ordinary backspace
+ //c.cursor.col--;
+ removeText(line, col-1, line, col+complement);
+ }
+ else
+ {
+ // backspace indents: erase to next indent position
+ KateTextLine::Ptr textLine = m_buffer->plainLine(line);
+
+ // don't forget this check!!!! really!!!!
+ if (!textLine)
+ return;
+
+ int colX = textLine->cursorX(col, config()->tabWidth());
+ int pos = textLine->firstChar();
+ if (pos > 0)
+ pos = textLine->cursorX(pos, config()->tabWidth());
+
+ if (pos < 0 || pos >= (int)colX)
+ {
+ // only spaces on left side of cursor
+ indent( view, line, -1);
+ }
+ else
+ removeText(line, col-1, line, col+complement);
+ }
+ }
+ else
+ {
+ // col == 0: wrap to previous line
+ if (line >= 1)
+ {
+ KateTextLine::Ptr textLine = m_buffer->plainLine(line-1);
+
+ // don't forget this check!!!! really!!!!
+ if (!textLine)
+ return;
+
+ if (config()->wordWrap() && textLine->endingWith(QString::fromLatin1(" ")))
+ {
+ // gg: in hard wordwrap mode, backspace must also eat the trailing space
+ removeText (line-1, textLine->length()-1, line, 0);
+ }
+ else
+ removeText (line-1, textLine->length(), line, 0);
+ }
+ }
+
+ emit backspacePressed();
+}
+
+void KateDocument::del( KateView *view, const KateTextCursor& c )
+{
+ if ( !view->config()->persistentSelection() && view->hasSelection() ) {
+ view->removeSelectedText();
+ return;
+ }
+
+ if( c.col() < (int) m_buffer->plainLine(c.line())->length())
+ {
+ removeText(c.line(), c.col(), c.line(), c.col()+1);
+ }
+ else if ( (uint)c.line() < lastLine() )
+ {
+ removeText(c.line(), c.col(), c.line()+1, 0);
+ }
+}
+
+void KateDocument::paste ( KateView* view )
+{
+ QString s = QApplication::clipboard()->text();
+
+ if (s.isEmpty())
+ return;
+
+ uint lines = s.contains (QChar ('\n'));
+
+ m_undoDontMerge = true;
+
+ editStart ();
+
+ if (!view->config()->persistentSelection() && view->hasSelection() )
+ view->removeSelectedText();
+
+ uint line = view->cursorLine ();
+ uint column = view->cursorColumnReal ();
+
+ insertText ( line, column, s, view->blockSelectionMode() );
+
+ editEnd();
+
+ // move cursor right for block select, as the user is moved right internal
+ // even in that case, but user expects other behavior in block selection
+ // mode !
+ if (view->blockSelectionMode())
+ view->setCursorPositionInternal (line+lines, column);
+
+ if (m_indenter->canProcessLine()
+ && config()->configFlags() & KateDocumentConfig::cfIndentPastedText)
+ {
+ editStart();
+
+ KateDocCursor begin(line, 0, this);
+ KateDocCursor end(line + lines, 0, this);
+
+ m_indenter->processSection (begin, end);
+
+ editEnd();
+ }
+
+ if (!view->blockSelectionMode()) emit charactersSemiInteractivelyInserted (line, column, s);
+ m_undoDontMerge = true;
+}
+
+void KateDocument::insertIndentChars ( KateView *view )
+{
+ editStart ();
+
+ QString s;
+ if (config()->configFlags() & KateDocument::cfSpaceIndent)
+ {
+ int width = config()->indentationWidth();
+ s.fill (' ', width - (view->cursorColumnReal() % width));
+ }
+ else
+ s.append ('\t');
+
+ insertText (view->cursorLine(), view->cursorColumnReal(), s);
+
+ editEnd ();
+}
+
+void KateDocument::indent ( KateView *v, uint line, int change)
+{
+ editStart ();
+
+ if (!hasSelection())
+ {
+ // single line
+ optimizeLeadingSpace(line, config()->configFlags(), change);
+ }
+ else
+ {
+ int sl = v->selStartLine();
+ int el = v->selEndLine();
+ int ec = v->selEndCol();
+
+ if ((ec == 0) && ((el-1) >= 0))
+ {
+ el--; /* */
+ }
+
+ if (config()->configFlags() & KateDocument::cfKeepIndentProfile && change < 0) {
+ // unindent so that the existing indent profile doesn't get screwed
+ // if any line we may unindent is already full left, don't do anything
+ int adjustedChange = -change;
+
+ for (line = sl; (int) line <= el && adjustedChange > 0; line++) {
+ KateTextLine::Ptr textLine = m_buffer->plainLine(line);
+ int firstChar = textLine->firstChar();
+ if (firstChar >= 0 && (v->lineSelected(line) || v->lineHasSelected(line))) {
+ int maxUnindent = textLine->cursorX(firstChar, config()->tabWidth()) / config()->indentationWidth();
+ if (maxUnindent < adjustedChange)
+ adjustedChange = maxUnindent;
+ }
+ }
+
+ change = -adjustedChange;
+ }
+
+ const bool rts = config()->configFlags() & KateDocumentConfig::cfRemoveTrailingDyn;
+ for (line = sl; (int) line <= el; line++) {
+ if ((v->lineSelected(line) || v->lineHasSelected(line))
+ && (!rts || lineLength(line) > 0)) {
+ optimizeLeadingSpace(line, config()->configFlags(), change);
+ }
+ }
+ }
+
+ editEnd ();
+}
+
+void KateDocument::align(KateView *view, uint line)
+{
+ if (m_indenter->canProcessLine())
+ {
+ editStart ();
+
+ if (!view->hasSelection())
+ {
+ KateDocCursor curLine(line, 0, this);
+ m_indenter->processLine (curLine);
+ editEnd ();
+ activeView()->setCursorPosition (line, curLine.col());
+ }
+ else
+ {
+ m_indenter->processSection (view->selStart(), view->selEnd());
+ editEnd ();
+ }
+ }
+}
+
+/*
+ Optimize the leading whitespace for a single line.
+ If change is > 0, it adds indentation units (indentationChars)
+ if change is == 0, it only optimizes
+ If change is < 0, it removes indentation units
+ This will be used to indent, unindent, and optimal-fill a line.
+ If excess space is removed depends on the flag cfKeepExtraSpaces
+ which has to be set by the user
+*/
+void KateDocument::optimizeLeadingSpace(uint line, int flags, int change)
+{
+ KateTextLine::Ptr textline = m_buffer->plainLine(line);
+
+ int first_char = textline->firstChar();
+
+ int w = 0;
+ if (flags & KateDocument::cfSpaceIndent)
+ w = config()->indentationWidth();
+ else
+ w = config()->tabWidth();
+
+ if (first_char < 0)
+ first_char = textline->length();
+
+ int space = textline->cursorX(first_char, config()->tabWidth()) + change * w;
+ if (space < 0)
+ space = 0;
+
+ if (!(flags & KateDocument::cfKeepExtraSpaces))
+ {
+ uint extra = space % w;
+
+ space -= extra;
+ if (extra && change < 0) {
+ // otherwise it unindents too much (e.g. 12 chars when indentation is 8 chars wide)
+ space += w;
+ }
+ }
+
+ //kdDebug(13020) << "replace With Op: " << line << " " << first_char << " " << space << endl;
+ replaceWithOptimizedSpace(line, first_char, space, flags);
+}
+
+void KateDocument::replaceWithOptimizedSpace(uint line, uint upto_column, uint space, int flags)
+{
+ uint length;
+ QString new_space;
+
+ if (flags & KateDocument::cfSpaceIndent && ! (flags & KateDocumentConfig::cfMixedIndent) ) {
+ length = space;
+ new_space.fill(' ', length);
+ }
+ else {
+ length = space / config()->tabWidth();
+ new_space.fill('\t', length);
+
+ QString extra_space;
+ extra_space.fill(' ', space % config()->tabWidth());
+ length += space % config()->tabWidth();
+ new_space += extra_space;
+ }
+
+ KateTextLine::Ptr textline = m_buffer->plainLine(line);
+ uint change_from;
+ for (change_from = 0; change_from < upto_column && change_from < length; change_from++) {
+ if (textline->getChar(change_from) != new_space[change_from])
+ break;
+ }
+
+ editStart();
+
+ if (change_from < upto_column)
+ removeText(line, change_from, line, upto_column);
+
+ if (change_from < length)
+ insertText(line, change_from, new_space.right(length - change_from));
+
+ editEnd();
+}
+
+/*
+ Remove a given string at the begining
+ of the current line.
+*/
+bool KateDocument::removeStringFromBegining(int line, QString &str)
+{
+ KateTextLine::Ptr textline = m_buffer->plainLine(line);
+
+ int index = 0;
+ bool there = false;
+
+ if (textline->startingWith(str))
+ there = true;
+ else
+ {
+ index = textline->firstChar ();
+
+ if ((index >= 0) && (textline->length() >= (index + str.length())) && (textline->string(index, str.length()) == str))
+ there = true;
+ }
+
+ if (there)
+ {
+ // Remove some chars
+ removeText (line, index, line, index+str.length());
+ }
+
+ return there;
+}
+
+/*
+ Remove a given string at the end
+ of the current line.
+*/
+bool KateDocument::removeStringFromEnd(int line, QString &str)
+{
+ KateTextLine::Ptr textline = m_buffer->plainLine(line);
+
+ int index = 0;
+ bool there = false;
+
+ if(textline->endingWith(str))
+ {
+ index = textline->length() - str.length();
+ there = true;
+ }
+ else
+ {
+ index = textline->lastChar ()-str.length()+1;
+
+ if ((index >= 0) && (textline->length() >= (index + str.length())) && (textline->string(index, str.length()) == str))
+ there = true;
+ }
+
+ if (there)
+ {
+ // Remove some chars
+ removeText (line, index, line, index+str.length());
+ }
+
+ return there;
+}
+
+/*
+ Add to the current line a comment line mark at
+ the begining.
+*/
+void KateDocument::addStartLineCommentToSingleLine( int line, int attrib )
+{
+ if (highlight()->getCommentSingleLinePosition(attrib)==KateHighlighting::CSLPosColumn0)
+ {
+ QString commentLineMark = highlight()->getCommentSingleLineStart( attrib ) + " ";
+ insertText (line, 0, commentLineMark);
+ }
+ else
+ {
+ QString commentLineMark=highlight()->getCommentSingleLineStart(attrib);
+ KateTextLine::Ptr l = m_buffer->line(line);
+ int pos=l->firstChar();
+ if (pos >=0)
+ insertText(line,pos,commentLineMark);
+ }
+}
+
+/*
+ Remove from the current line a comment line mark at
+ the begining if there is one.
+*/
+bool KateDocument::removeStartLineCommentFromSingleLine( int line, int attrib )
+{
+ QString shortCommentMark = highlight()->getCommentSingleLineStart( attrib );
+ QString longCommentMark = shortCommentMark + " ";
+
+ editStart();
+
+ // Try to remove the long comment mark first
+ bool removed = (removeStringFromBegining(line, longCommentMark)
+ || removeStringFromBegining(line, shortCommentMark));
+
+ editEnd();
+
+ return removed;
+}
+
+/*
+ Add to the current line a start comment mark at the
+ begining and a stop comment mark at the end.
+*/
+void KateDocument::addStartStopCommentToSingleLine( int line, int attrib )
+{
+ QString startCommentMark = highlight()->getCommentStart( attrib ) + " ";
+ QString stopCommentMark = " " + highlight()->getCommentEnd( attrib );
+
+ editStart();
+
+ // Add the start comment mark
+ insertText (line, 0, startCommentMark);
+
+ // Go to the end of the line
+ int col = m_buffer->plainLine(line)->length();
+
+ // Add the stop comment mark
+ insertText (line, col, stopCommentMark);
+
+ editEnd();
+}
+
+/*
+ Remove from the current line a start comment mark at
+ the begining and a stop comment mark at the end.
+*/
+bool KateDocument::removeStartStopCommentFromSingleLine( int line, int attrib )
+{
+ QString shortStartCommentMark = highlight()->getCommentStart( attrib );
+ QString longStartCommentMark = shortStartCommentMark + " ";
+ QString shortStopCommentMark = highlight()->getCommentEnd( attrib );
+ QString longStopCommentMark = " " + shortStopCommentMark;
+
+ editStart();
+
+#ifdef __GNUC__
+#warning "that's a bad idea, can lead to stray endings, FIXME"
+#endif
+ // Try to remove the long start comment mark first
+ bool removedStart = (removeStringFromBegining(line, longStartCommentMark)
+ || removeStringFromBegining(line, shortStartCommentMark));
+
+ bool removedStop = false;
+ if (removedStart)
+ {
+ // Try to remove the long stop comment mark first
+ removedStop = (removeStringFromEnd(line, longStopCommentMark)
+ || removeStringFromEnd(line, shortStopCommentMark));
+ }
+
+ editEnd();
+
+ return (removedStart || removedStop);
+}
+
+/*
+ Add to the current selection a start comment
+ mark at the begining and a stop comment mark
+ at the end.
+*/
+void KateDocument::addStartStopCommentToSelection( KateView *view, int attrib )
+{
+ QString startComment = highlight()->getCommentStart( attrib );
+ QString endComment = highlight()->getCommentEnd( attrib );
+
+ int sl = view->selStartLine();
+ int el = view->selEndLine();
+ int sc = view->selStartCol();
+ int ec = view->selEndCol();
+
+ if ((ec == 0) && ((el-1) >= 0))
+ {
+ el--;
+ ec = m_buffer->plainLine (el)->length();
+ }
+
+ editStart();
+
+ insertText (el, ec, endComment);
+ insertText (sl, sc, startComment);
+
+ editEnd ();
+
+ // Set the new selection
+ ec += endComment.length() + ( (el == sl) ? startComment.length() : 0 );
+ view->setSelection(sl, sc, el, ec);
+}
+
+/*
+ Add to the current selection a comment line
+ mark at the begining of each line.
+*/
+void KateDocument::addStartLineCommentToSelection( KateView *view, int attrib )
+{
+ QString commentLineMark = highlight()->getCommentSingleLineStart( attrib ) + " ";
+
+ int sl = view->selStartLine();
+ int el = view->selEndLine();
+
+ if ((view->selEndCol() == 0) && ((el-1) >= 0))
+ {
+ el--;
+ }
+
+ editStart();
+
+ // For each line of the selection
+ for (int z = el; z >= sl; z--) {
+ //insertText (z, 0, commentLineMark);
+ addStartLineCommentToSingleLine(z, attrib );
+ }
+
+ editEnd ();
+
+ // Set the new selection
+
+ KateDocCursor end (view->selEnd());
+ end.setCol(view->selEndCol() + ((el == view->selEndLine()) ? commentLineMark.length() : 0) );
+
+ view->setSelection(view->selStartLine(), 0, end.line(), end.col());
+}
+
+bool KateDocument::nextNonSpaceCharPos(int &line, int &col)
+{
+ for(; line < (int)m_buffer->count(); line++) {
+ KateTextLine::Ptr textLine = m_buffer->plainLine(line);
+
+ if (!textLine)
+ break;
+
+ col = textLine->nextNonSpaceChar(col);
+ if(col != -1)
+ return true; // Next non-space char found
+ col = 0;
+ }
+ // No non-space char found
+ line = -1;
+ col = -1;
+ return false;
+}
+
+bool KateDocument::previousNonSpaceCharPos(int &line, int &col)
+{
+ while(true)
+ {
+ KateTextLine::Ptr textLine = m_buffer->plainLine(line);
+
+ if (!textLine)
+ break;
+
+ col = textLine->previousNonSpaceChar(col);
+ if(col != -1) return true;
+ if(line == 0) return false;
+ --line;
+ col = textLine->length();
+}
+ // No non-space char found
+ line = -1;
+ col = -1;
+ return false;
+}
+
+/*
+ Remove from the selection a start comment mark at
+ the begining and a stop comment mark at the end.
+*/
+bool KateDocument::removeStartStopCommentFromSelection( KateView *view, int attrib )
+{
+ QString startComment = highlight()->getCommentStart( attrib );
+ QString endComment = highlight()->getCommentEnd( attrib );
+
+ int sl = kMax<int> (0, view->selStartLine());
+ int el = kMin<int> (view->selEndLine(), lastLine());
+ int sc = view->selStartCol();
+ int ec = view->selEndCol();
+
+ // The selection ends on the char before selectEnd
+ if (ec != 0) {
+ ec--;
+ } else {
+ if (el > 0) {
+ el--;
+ ec = m_buffer->plainLine(el)->length() - 1;
+ }
+ }
+
+ int startCommentLen = startComment.length();
+ int endCommentLen = endComment.length();
+
+ // had this been perl or sed: s/^\s*$startComment(.+?)$endComment\s*/$1/
+
+ bool remove = nextNonSpaceCharPos(sl, sc)
+ && m_buffer->plainLine(sl)->stringAtPos(sc, startComment)
+ && previousNonSpaceCharPos(el, ec)
+ && ( (ec - endCommentLen + 1) >= 0 )
+ && m_buffer->plainLine(el)->stringAtPos(ec - endCommentLen + 1, endComment);
+
+ if (remove) {
+ editStart();
+
+ removeText (el, ec - endCommentLen + 1, el, ec + 1);
+ removeText (sl, sc, sl, sc + startCommentLen);
+
+ editEnd ();
+ // set new selection not necessary, as the selection cursors are KateSuperCursors
+ }
+
+ return remove;
+}
+
+bool KateDocument::removeStartStopCommentFromRegion(const KateTextCursor &start,const KateTextCursor &end,int attrib)
+{
+ QString startComment = highlight()->getCommentStart( attrib );
+ QString endComment = highlight()->getCommentEnd( attrib );
+ int startCommentLen = startComment.length();
+ int endCommentLen = endComment.length();
+
+ bool remove = m_buffer->plainLine(start.line())->stringAtPos(start.col(), startComment)
+ && ( (end.col() - endCommentLen ) >= 0 )
+ && m_buffer->plainLine(end.line())->stringAtPos(end.col() - endCommentLen , endComment);
+ if (remove) {
+ editStart();
+ removeText(end.line(),end.col()-endCommentLen,end.line(),end.col());
+ removeText(start.line(),start.col(),start.line(),start.col()+startCommentLen);
+ editEnd();
+ }
+ return remove;
+}
+
+/*
+ Remove from the begining of each line of the
+ selection a start comment line mark.
+*/
+bool KateDocument::removeStartLineCommentFromSelection( KateView *view, int attrib )
+{
+ QString shortCommentMark = highlight()->getCommentSingleLineStart( attrib );
+ QString longCommentMark = shortCommentMark + " ";
+
+ int sl = view->selStartLine();
+ int el = view->selEndLine();
+
+ if ((view->selEndCol() == 0) && ((el-1) >= 0))
+ {
+ el--;
+ }
+
+ // Find out how many char will be removed from the last line
+ int removeLength = 0;
+ if (m_buffer->plainLine(el)->startingWith(longCommentMark))
+ removeLength = longCommentMark.length();
+ else if (m_buffer->plainLine(el)->startingWith(shortCommentMark))
+ removeLength = shortCommentMark.length();
+
+ bool removed = false;
+
+ editStart();
+
+ // For each line of the selection
+ for (int z = el; z >= sl; z--)
+ {
+ // Try to remove the long comment mark first
+ removed = (removeStringFromBegining(z, longCommentMark)
+ || removeStringFromBegining(z, shortCommentMark)
+ || removed);
+ }
+
+ editEnd();
+ // updating selection already done by the KateSuperCursors
+ return removed;
+}
+
+/*
+ Comment or uncomment the selection or the current
+ line if there is no selection.
+*/
+void KateDocument::comment( KateView *v, uint line,uint column, int change)
+{
+ // We need to check that we can sanely comment the selectino or region.
+ // It is if the attribute of the first and last character of the range to
+ // comment belongs to the same language definition.
+ // for lines with no text, we need the attribute for the lines context.
+ bool hassel = v->hasSelection();
+ int startAttrib, endAttrib;
+ if ( hassel )
+ {
+ KateTextLine::Ptr ln = kateTextLine( v->selStartLine() );
+ int l = v->selStartLine(), c = v->selStartCol();
+ startAttrib = nextNonSpaceCharPos( l, c ) ? kateTextLine( l )->attribute( c ) : 0;
+
+ ln = kateTextLine( v->selEndLine() );
+ l = v->selEndLine(), c = v->selEndCol();
+ endAttrib = previousNonSpaceCharPos( l, c ) ? kateTextLine( l )->attribute( c ) : 0;
+ }
+ else
+ {
+ KateTextLine::Ptr ln = kateTextLine( line );
+ if ( ln->length() )
+ {
+ startAttrib = ln->attribute( ln->firstChar() );
+ endAttrib = ln->attribute( ln->lastChar() );
+ }
+ else
+ {
+ int l = line, c = 0;
+ if ( nextNonSpaceCharPos( l, c ) || previousNonSpaceCharPos( l, c ) )
+ startAttrib = endAttrib = kateTextLine( l )->attribute( c );
+ else
+ startAttrib = endAttrib = 0;
+ }
+ }
+
+ if ( ! highlight()->canComment( startAttrib, endAttrib ) )
+ {
+ kdDebug(13020)<<"canComment( "<<startAttrib<<", "<<endAttrib<<" ) returned false!"<<endl;
+ return;
+ }
+
+ bool hasStartLineCommentMark = !(highlight()->getCommentSingleLineStart( startAttrib ).isEmpty());
+ bool hasStartStopCommentMark = ( !(highlight()->getCommentStart( startAttrib ).isEmpty())
+ && !(highlight()->getCommentEnd( endAttrib ).isEmpty()) );
+
+ bool removed = false;
+
+ if (change > 0) // comment
+ {
+ if ( !hassel )
+ {
+ if ( hasStartLineCommentMark )
+ addStartLineCommentToSingleLine( line, startAttrib );
+ else if ( hasStartStopCommentMark )
+ addStartStopCommentToSingleLine( line, startAttrib );
+ }
+ else
+ {
+ // anders: prefer single line comment to avoid nesting probs
+ // If the selection starts after first char in the first line
+ // or ends before the last char of the last line, we may use
+ // multiline comment markers.
+ // TODO We should try to detect nesting.
+ // - if selection ends at col 0, most likely she wanted that
+ // line ignored
+ if ( hasStartStopCommentMark &&
+ ( !hasStartLineCommentMark || (
+ ( v->selStartCol() > m_buffer->plainLine( v->selStartLine() )->firstChar() ) ||
+ ( v->selEndCol() < ((int)m_buffer->plainLine( v->selEndLine() )->length()) )
+ ) ) )
+ addStartStopCommentToSelection( v, startAttrib );
+ else if ( hasStartLineCommentMark )
+ addStartLineCommentToSelection( v, startAttrib );
+ }
+ }
+ else // uncomment
+ {
+ if ( !hassel )
+ {
+ removed = ( hasStartLineCommentMark
+ && removeStartLineCommentFromSingleLine( line, startAttrib ) )
+ || ( hasStartStopCommentMark
+ && removeStartStopCommentFromSingleLine( line, startAttrib ) );
+ if ((!removed) && foldingTree()) {
+ kdDebug(13020)<<"easy approach for uncommenting did not work, trying harder (folding tree)"<<endl;
+ int commentRegion=(highlight()->commentRegion(startAttrib));
+ if (commentRegion){
+ KateCodeFoldingNode *n=foldingTree()->findNodeForPosition(line,column);
+ if (n) {
+ KateTextCursor start,end;
+ if ((n->nodeType()==commentRegion) && n->getBegin(foldingTree(), &start) && n->getEnd(foldingTree(), &end)) {
+ kdDebug(13020)<<"Enclosing region found:"<<start.col()<<"/"<<start.line()<<"-"<<end.col()<<"/"<<end.line()<<endl;
+ removeStartStopCommentFromRegion(start,end,startAttrib);
+ } else {
+ kdDebug(13020)<<"Enclosing region found, but not valid"<<endl;
+ kdDebug(13020)<<"Region found: "<<n->nodeType()<<" region needed: "<<commentRegion<<endl;
+ }
+ //perhaps nested regions should be hadled here too...
+ } else kdDebug(13020)<<"No enclosing region found"<<endl;
+ } else kdDebug(13020)<<"No comment region specified for current hl"<<endl;
+ }
+ }
+ else
+ {
+ // anders: this seems like it will work with above changes :)
+ removed = ( hasStartLineCommentMark
+ && removeStartLineCommentFromSelection( v, startAttrib ) )
+ || ( hasStartStopCommentMark
+ && removeStartStopCommentFromSelection( v, startAttrib ) );
+ }
+ }
+}
+
+void KateDocument::transform( KateView *v, const KateTextCursor &c,
+ KateDocument::TextTransform t )
+{
+ editStart();
+ uint cl( c.line() ), cc( c.col() );
+ bool selectionRestored = false;
+
+ if ( hasSelection() )
+ {
+ // cache the selection and cursor, so we can be sure to restore.
+ KateTextCursor selstart = v->selStart();
+ KateTextCursor selend = v->selEnd();
+
+ int ln = v->selStartLine();
+ while ( ln <= selend.line() )
+ {
+ uint start, end;
+ start = (ln == selstart.line() || v->blockSelectionMode()) ?
+ selstart.col() : 0;
+ end = (ln == selend.line() || v->blockSelectionMode()) ?
+ selend.col() : lineLength( ln );
+ if ( start > end )
+ {
+ uint t = start;
+ start = end;
+ end = t;
+ }
+ QString s = text( ln, start, ln, end );
+ QString o = s;
+
+ if ( t == Uppercase )
+ s = s.upper();
+ else if ( t == Lowercase )
+ s = s.lower();
+ else // Capitalize
+ {
+ KateTextLine::Ptr l = m_buffer->plainLine( ln );
+ uint p ( 0 );
+ while( p < s.length() )
+ {
+ // If bol or the character before is not in a word, up this one:
+ // 1. if both start and p is 0, upper char.
+ // 2. if blockselect or first line, and p == 0 and start-1 is not in a word, upper
+ // 3. if p-1 is not in a word, upper.
+ if ( ( ! start && ! p ) ||
+ ( ( ln == selstart.line() || v->blockSelectionMode() ) &&
+ ! p && ! highlight()->isInWord( l->getChar( start - 1 )) ) ||
+ ( p && ! highlight()->isInWord( s.at( p-1 ) ) )
+ )
+ s[p] = s.at(p).upper();
+ p++;
+ }
+ }
+
+ if ( o != s )
+ {
+ removeText( ln, start, ln, end );
+ insertText( ln, start, s );
+ }
+
+ ln++;
+ }
+
+ // restore selection
+ v->setSelection( selstart, selend );
+ selectionRestored = true;
+
+ } else { // no selection
+ QString o = text( cl, cc, cl, cc + 1 );
+ QString s;
+ int n ( cc );
+ switch ( t ) {
+ case Uppercase:
+ s = o.upper();
+ break;
+ case Lowercase:
+ s = o.lower();
+ break;
+ case Capitalize:
+ {
+ KateTextLine::Ptr l = m_buffer->plainLine( cl );
+ while ( n > 0 && highlight()->isInWord( l->getChar( n-1 ), l->attribute( n-1 ) ) )
+ n--;
+ o = text( cl, n, cl, n + 1 );
+ s = o.upper();
+ }
+ break;
+ default:
+ break;
+ }
+
+ if ( s != o )
+ {
+ removeText( cl, n, cl, n+1 );
+ insertText( cl, n, s );
+ }
+ }
+ editEnd();
+
+ if ( ! selectionRestored )
+ v->setCursorPosition( cl, cc );
+}
+
+void KateDocument::joinLines( uint first, uint last )
+{
+// if ( first == last ) last += 1;
+ editStart();
+ int line( first );
+ while ( first < last )
+ {
+ // Normalize the whitespace in the joined lines by making sure there's
+ // always exactly one space between the joined lines
+ // This cannot be done in editUnwrapLine, because we do NOT want this
+ // behaviour when deleting from the start of a line, just when explicitly
+ // calling the join command
+ KateTextLine::Ptr l = m_buffer->line( line );
+ KateTextLine::Ptr tl = m_buffer->line( line + 1 );
+
+ if ( !l || !tl )
+ {
+ editEnd();
+ return;
+ }
+
+ int pos = tl->firstChar();
+ if ( pos >= 0 )
+ {
+ if (pos != 0)
+ editRemoveText( line + 1, 0, pos );
+ if ( !( l->length() == 0 || l->getChar( l->length() - 1 ).isSpace() ) )
+ editInsertText( line + 1, 0, " " );
+ }
+ else
+ {
+ // Just remove the whitespace and let Kate handle the rest
+ editRemoveText( line + 1, 0, tl->length() );
+ }
+
+ editUnWrapLine( line );
+ first++;
+ }
+ editEnd();
+}
+
+QString KateDocument::getWord( const KateTextCursor& cursor ) {
+ int start, end, len;
+
+ KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
+ len = textLine->length();
+ start = end = cursor.col();
+ if (start > len) // Probably because of non-wrapping cursor mode.
+ return QString("");
+
+ while (start > 0 && highlight()->isInWord(textLine->getChar(start - 1), textLine->attribute(start - 1))) start--;
+ while (end < len && highlight()->isInWord(textLine->getChar(end), textLine->attribute(end))) end++;
+ len = end - start;
+ return QString(&textLine->text()[start], len);
+}
+
+void KateDocument::tagLines(int start, int end)
+{
+ for (uint z = 0; z < m_views.count(); z++)
+ m_views.at(z)->tagLines (start, end, true);
+}
+
+void KateDocument::tagLines(KateTextCursor start, KateTextCursor end)
+{
+ // May need to switch start/end cols if in block selection mode
+ if (blockSelectionMode() && start.col() > end.col()) {
+ int sc = start.col();
+ start.setCol(end.col());
+ end.setCol(sc);
+ }
+
+ for (uint z = 0; z < m_views.count(); z++)
+ m_views.at(z)->tagLines(start, end, true);
+}
+
+void KateDocument::repaintViews(bool paintOnlyDirty)
+{
+ for (uint z = 0; z < m_views.count(); z++)
+ m_views.at(z)->repaintText(paintOnlyDirty);
+}
+
+void KateDocument::tagAll()
+{
+ for (uint z = 0; z < m_views.count(); z++)
+ {
+ m_views.at(z)->tagAll();
+ m_views.at(z)->updateView (true);
+ }
+}
+
+uint KateDocument::configFlags ()
+{
+ return config()->configFlags();
+}
+
+void KateDocument::setConfigFlags (uint flags)
+{
+ config()->setConfigFlags(flags);
+}
+
+inline bool isStartBracket( const QChar& c ) { return c == '{' || c == '[' || c == '('; }
+inline bool isEndBracket ( const QChar& c ) { return c == '}' || c == ']' || c == ')'; }
+inline bool isBracket ( const QChar& c ) { return isStartBracket( c ) || isEndBracket( c ); }
+
+/*
+ Bracket matching uses the following algorithm:
+ If in overwrite mode, match the bracket currently underneath the cursor.
+ Otherwise, if the character to the right of the cursor is an starting bracket,
+ match it. Otherwise if the character to the left of the cursor is a
+ ending bracket, match it. Otherwise, if the the character to the left
+ of the cursor is an starting bracket, match it. Otherwise, if the character
+ to the right of the cursor is an ending bracket, match it. Otherwise, don't
+ match anything.
+*/
+void KateDocument::newBracketMark( const KateTextCursor& cursor, KateBracketRange& bm, int maxLines )
+{
+ bm.setValid(false);
+
+ bm.start() = cursor;
+
+ if( !findMatchingBracket( bm.start(), bm.end(), maxLines ) )
+ return;
+
+ bm.setValid(true);
+
+ const int tw = config()->tabWidth();
+ const int indentStart = m_buffer->plainLine(bm.start().line())->indentDepth(tw);
+ const int indentEnd = m_buffer->plainLine(bm.end().line())->indentDepth(tw);
+ bm.setIndentMin(kMin(indentStart, indentEnd));
+}
+
+bool KateDocument::findMatchingBracket( KateTextCursor& start, KateTextCursor& end, int maxLines )
+{
+ KateTextLine::Ptr textLine = m_buffer->plainLine( start.line() );
+ if( !textLine )
+ return false;
+
+ QChar right = textLine->getChar( start.col() );
+ QChar left = textLine->getChar( start.col() - 1 );
+ QChar bracket;
+
+ if ( config()->configFlags() & cfOvr ) {
+ if( isBracket( right ) ) {
+ bracket = right;
+ } else {
+ return false;
+ }
+ } else if ( isStartBracket( right ) ) {
+ bracket = right;
+ } else if ( isEndBracket( left ) ) {
+ start.setCol(start.col() - 1);
+ bracket = left;
+ } else if ( isBracket( left ) ) {
+ start.setCol(start.col() - 1);
+ bracket = left;
+ } else if ( isBracket( right ) ) {
+ bracket = right;
+ } else {
+ return false;
+ }
+
+ QChar opposite;
+
+ switch( bracket ) {
+ case '{': opposite = '}'; break;
+ case '}': opposite = '{'; break;
+ case '[': opposite = ']'; break;
+ case ']': opposite = '['; break;
+ case '(': opposite = ')'; break;
+ case ')': opposite = '('; break;
+ default: return false;
+ }
+
+ bool forward = isStartBracket( bracket );
+ int startAttr = textLine->attribute( start.col() );
+ uint count = 0;
+ int lines = 0;
+ end = start;
+
+ while( true ) {
+ /* Increment or decrement, check base cases */
+ if( forward ) {
+ end.setCol(end.col() + 1);
+ if( end.col() >= lineLength( end.line() ) ) {
+ if( end.line() >= (int)lastLine() )
+ return false;
+ end.setPos(end.line() + 1, 0);
+ textLine = m_buffer->plainLine( end.line() );
+ lines++;
+ }
+ } else {
+ end.setCol(end.col() - 1);
+ if( end.col() < 0 ) {
+ if( end.line() <= 0 )
+ return false;
+ end.setLine(end.line() - 1);
+ end.setCol(lineLength( end.line() ) - 1);
+ textLine = m_buffer->plainLine( end.line() );
+ lines++;
+ }
+ }
+
+ if ((maxLines != -1) && (lines > maxLines))
+ return false;
+
+ /* Easy way to skip comments */
+ if( textLine->attribute( end.col() ) != startAttr )
+ continue;
+
+ /* Check for match */
+ QChar c = textLine->getChar( end.col() );
+ if( c == bracket ) {
+ count++;
+ } else if( c == opposite ) {
+ if( count == 0 )
+ return true;
+ count--;
+ }
+
+ }
+}
+
+void KateDocument::guiActivateEvent( KParts::GUIActivateEvent *ev )
+{
+ KParts::ReadWritePart::guiActivateEvent( ev );
+ if ( ev->activated() )
+ emit selectionChanged();
+}
+
+void KateDocument::setDocName (QString name )
+{
+ if ( name == m_docName )
+ return;
+
+ if ( !name.isEmpty() )
+ {
+ // TODO check for similarly named documents
+ m_docName = name;
+ updateFileType (KateFactory::self()->fileTypeManager()->fileType (this));
+ emit nameChanged((Kate::Document *) this);
+ return;
+ }
+
+ // if the name is set, and starts with FILENAME, it should not be changed!
+ if ( ! url().isEmpty() && m_docName.startsWith( url().filename() ) ) return;
+
+ int count = -1;
+
+ for (uint z=0; z < KateFactory::self()->documents()->count(); z++)
+ {
+ if ( (KateFactory::self()->documents()->at(z) != this) && (KateFactory::self()->documents()->at(z)->url().filename() == url().filename()) )
+ if ( KateFactory::self()->documents()->at(z)->m_docNameNumber > count )
+ count = KateFactory::self()->documents()->at(z)->m_docNameNumber;
+ }
+
+ m_docNameNumber = count + 1;
+
+ m_docName = url().filename();
+
+ if (m_docName.isEmpty())
+ m_docName = i18n ("Untitled");
+
+ if (m_docNameNumber > 0)
+ m_docName = QString(m_docName + " (%1)").arg(m_docNameNumber+1);
+
+ updateFileType (KateFactory::self()->fileTypeManager()->fileType (this));
+ emit nameChanged ((Kate::Document *) this);
+}
+
+void KateDocument::slotModifiedOnDisk( Kate::View * /*v*/ )
+{
+ if ( m_isasking < 0 )
+ {
+ m_isasking = 0;
+ return;
+ }
+
+ if ( !s_fileChangedDialogsActivated || m_isasking )
+ return;
+
+ if (m_modOnHd && !url().isEmpty())
+ {
+ m_isasking = 1;
+
+ KateModOnHdPrompt p( this, m_modOnHdReason, reasonedMOHString(), widget() );
+ switch ( p.exec() )
+ {
+ case KateModOnHdPrompt::Save:
+ {
+ m_modOnHd = false;
+ KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveURLAndEncoding(config()->encoding(),
+ url().url(),QString::null,widget(),i18n("Save File"));
+
+ kdDebug(13020)<<"got "<<res.URLs.count()<<" URLs"<<endl;
+ if( ! res.URLs.isEmpty() && ! res.URLs.first().isEmpty() && checkOverwrite( res.URLs.first() ) )
+ {
+ setEncoding( res.encoding );
+
+ if( ! saveAs( res.URLs.first() ) )
+ {
+ KMessageBox::error( widget(), i18n("Save failed") );
+ m_modOnHd = true;
+ }
+ else
+ emit modifiedOnDisc( this, false, 0 );
+ }
+ else // the save as dialog was cancelled, we are still modified on disk
+ {
+ m_modOnHd = true;
+ }
+
+ m_isasking = 0;
+ break;
+ }
+
+ case KateModOnHdPrompt::Reload:
+ m_modOnHd = false;
+ emit modifiedOnDisc( this, false, 0 );
+ reloadFile();
+ m_isasking = 0;
+ break;
+
+ case KateModOnHdPrompt::Ignore:
+ m_modOnHd = false;
+ emit modifiedOnDisc( this, false, 0 );
+ m_isasking = 0;
+ break;
+
+ case KateModOnHdPrompt::Overwrite:
+ m_modOnHd = false;
+ emit modifiedOnDisc( this, false, 0 );
+ m_isasking = 0;
+ save();
+ break;
+
+ default: // cancel: ignore next focus event
+ m_isasking = -1;
+ }
+ }
+}
+
+void KateDocument::setModifiedOnDisk( int reason )
+{
+ m_modOnHdReason = reason;
+ m_modOnHd = (reason > 0);
+ emit modifiedOnDisc( this, (reason > 0), reason );
+}
+
+class KateDocumentTmpMark
+{
+ public:
+ QString line;
+ KTextEditor::Mark mark;
+};
+
+void KateDocument::reloadFile()
+{
+ if ( !url().isEmpty() )
+ {
+ if (m_modOnHd && s_fileChangedDialogsActivated)
+ {
+ int i = KMessageBox::warningYesNoCancel
+ (0, reasonedMOHString() + "\n\n" + i18n("What do you want to do?"),
+ i18n("File Was Changed on Disk"), i18n("&Reload File"), i18n("&Ignore Changes"));
+
+ if ( i != KMessageBox::Yes)
+ {
+ if (i == KMessageBox::No)
+ {
+ m_modOnHd = false;
+ m_modOnHdReason = 0;
+ emit modifiedOnDisc (this, m_modOnHd, 0);
+ }
+
+ return;
+ }
+ }
+
+ QValueList<KateDocumentTmpMark> tmp;
+
+ for( QIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it )
+ {
+ KateDocumentTmpMark m;
+
+ m.line = textLine (it.current()->line);
+ m.mark = *it.current();
+
+ tmp.append (m);
+ }
+
+ uint mode = hlMode ();
+ bool byUser = hlSetByUser;
+
+ m_storedVariables.clear();
+
+ m_reloading = true;
+
+ QValueList<int> lines, cols;
+ for ( uint i=0; i < m_views.count(); i++ )
+ {
+ lines.append( m_views.at( i )->cursorLine() );
+ cols.append( m_views.at( i )->cursorColumn() );
+ }
+
+ KateDocument::openURL( url() );
+
+ for ( uint i=0; i < m_views.count(); i++ )
+ m_views.at( i )->setCursorPositionInternal( lines[ i ], cols[ i ], m_config->tabWidth(), false );
+
+ m_reloading = false;
+
+ for ( QValueList<int>::size_type z=0; z < tmp.size(); z++ )
+ {
+ if (z < numLines())
+ {
+ if (textLine(tmp[z].mark.line) == tmp[z].line)
+ setMark (tmp[z].mark.line, tmp[z].mark.type);
+ }
+ }
+
+ if (byUser)
+ setHlMode (mode);
+ }
+}
+
+void KateDocument::flush ()
+{
+ closeURL ();
+}
+
+void KateDocument::setWordWrap (bool on)
+{
+ config()->setWordWrap (on);
+}
+
+bool KateDocument::wordWrap ()
+{
+ return config()->wordWrap ();
+}
+
+void KateDocument::setWordWrapAt (uint col)
+{
+ config()->setWordWrapAt (col);
+}
+
+unsigned int KateDocument::wordWrapAt ()
+{
+ return config()->wordWrapAt ();
+}
+
+void KateDocument::applyWordWrap ()
+{
+ // dummy to make the API happy
+}
+
+void KateDocument::setPageUpDownMovesCursor (bool on)
+{
+ config()->setPageUpDownMovesCursor (on);
+}
+
+bool KateDocument::pageUpDownMovesCursor ()
+{
+ return config()->pageUpDownMovesCursor ();
+}
+
+void KateDocument::dumpRegionTree()
+{
+ m_buffer->foldingTree()->debugDump();
+}
+//END
+
+//BEGIN KTextEditor::CursorInterface stuff
+
+KTextEditor::Cursor *KateDocument::createCursor ( )
+{
+ return new KateSuperCursor (this, false, 0, 0, this);
+}
+
+void KateDocument::tagArbitraryLines(KateView* view, KateSuperRange* range)
+{
+ if (view)
+ view->tagLines(range->start(), range->end());
+ else
+ tagLines(range->start(), range->end());
+}
+
+void KateDocument::lineInfo (KateLineInfo *info, unsigned int line)
+{
+ m_buffer->lineInfo(info,line);
+}
+
+KateCodeFoldingTree *KateDocument::foldingTree ()
+{
+ return m_buffer->foldingTree();
+}
+
+void KateDocument::setEncoding (const QString &e)
+{
+ if ( m_encodingSticky )
+ return;
+
+ QString ce = m_config->encoding().lower();
+ if ( e.lower() == ce )
+ return;
+
+ m_config->setEncoding( e );
+ if ( ! m_loading )
+ reloadFile();
+}
+
+QString KateDocument::encoding() const
+{
+ return m_config->encoding();
+}
+
+void KateDocument::updateConfig ()
+{
+ emit undoChanged ();
+ tagAll();
+
+ for (KateView * view = m_views.first(); view != 0L; view = m_views.next() )
+ {
+ view->updateDocumentConfig ();
+ }
+
+ // switch indenter if needed
+ if (m_indenter->modeNumber() != m_config->indentationMode())
+ {
+ delete m_indenter;
+ m_indenter = KateAutoIndent::createIndenter ( this, m_config->indentationMode() );
+ }
+
+ m_indenter->updateConfig();
+
+ m_buffer->setTabWidth (config()->tabWidth());
+
+ // plugins
+ for (uint i=0; i<KateFactory::self()->plugins().count(); i++)
+ {
+ if (config()->plugin (i))
+ loadPlugin (i);
+ else
+ unloadPlugin (i);
+ }
+}
+
+//BEGIN Variable reader
+// "local variable" feature by anders, 2003
+/* TODO
+ add config options (how many lines to read, on/off)
+ add interface for plugins/apps to set/get variables
+ add view stuff
+*/
+QRegExp KateDocument::kvLine = QRegExp("kate:(.*)");
+QRegExp KateDocument::kvLineWildcard = QRegExp("kate-wildcard\\((.*)\\):(.*)");
+QRegExp KateDocument::kvLineMime = QRegExp("kate-mimetype\\((.*)\\):(.*)");
+QRegExp KateDocument::kvVar = QRegExp("([\\w\\-]+)\\s+([^;]+)");
+
+void KateDocument::readVariables(bool onlyViewAndRenderer)
+{
+ if (!onlyViewAndRenderer)
+ m_config->configStart();
+
+ // views!
+ KateView *v;
+ for (v = m_views.first(); v != 0L; v= m_views.next() )
+ {
+ v->config()->configStart();
+ v->renderer()->config()->configStart();
+ }
+ // read a number of lines in the top/bottom of the document
+ for (uint i=0; i < kMin( 9U, numLines() ); ++i )
+ {
+ readVariableLine( textLine( i ), onlyViewAndRenderer );
+ }
+ if ( numLines() > 10 )
+ {
+ for ( uint i = kMax(10U, numLines() - 10); i < numLines(); ++i )
+ {
+ readVariableLine( textLine( i ), onlyViewAndRenderer );
+ }
+ }
+
+ if (!onlyViewAndRenderer)
+ m_config->configEnd();
+
+ for (v = m_views.first(); v != 0L; v= m_views.next() )
+ {
+ v->config()->configEnd();
+ v->renderer()->config()->configEnd();
+ }
+}
+
+void KateDocument::readVariableLine( QString t, bool onlyViewAndRenderer )
+{
+ // simple check first, no regex
+ // no kate inside, no vars, simple...
+ if (t.find("kate") < 0)
+ return;
+
+ // found vars, if any
+ QString s;
+
+ if ( kvLine.search( t ) > -1 )
+ {
+ s = kvLine.cap(1);
+
+ kdDebug (13020) << "normal variable line kate: matched: " << s << endl;
+ }
+ else if (kvLineWildcard.search( t ) > -1) // regex given
+ {
+ QStringList wildcards (QStringList::split(';', kvLineWildcard.cap(1)));
+ QString nameOfFile = url().fileName();
+
+ bool found = false;
+ for (QStringList::size_type i = 0; !found && i < wildcards.size(); ++i)
+ {
+ QRegExp wildcard (wildcards[i], true/*Qt::CaseSensitive*/, true/*QRegExp::Wildcard*/);
+
+ found = wildcard.exactMatch (nameOfFile);
+ }
+
+ // nothing usable found...
+ if (!found)
+ return;
+
+ s = kvLineWildcard.cap(2);
+
+ kdDebug (13020) << "guarded variable line kate-wildcard: matched: " << s << endl;
+ }
+ else if (kvLineMime.search( t ) > -1) // mime-type given
+ {
+ QStringList types (QStringList::split(';', kvLineMime.cap(1)));
+
+ // no matching type found
+ if (!types.contains (mimeType ()))
+ return;
+
+ s = kvLineMime.cap(2);
+
+ kdDebug (13020) << "guarded variable line kate-mimetype: matched: " << s << endl;
+ }
+ else // nothing found
+ {
+ return;
+ }
+
+ QStringList vvl; // view variable names
+ vvl << "dynamic-word-wrap" << "dynamic-word-wrap-indicators"
+ << "line-numbers" << "icon-border" << "folding-markers"
+ << "bookmark-sorting" << "auto-center-lines"
+ << "icon-bar-color"
+ // renderer
+ << "background-color" << "selection-color"
+ << "current-line-color" << "bracket-highlight-color"
+ << "word-wrap-marker-color"
+ << "font" << "font-size" << "scheme";
+ int p( 0 );
+
+ QString var, val;
+ while ( (p = kvVar.search( s, p )) > -1 )
+ {
+ p += kvVar.matchedLength();
+ var = kvVar.cap( 1 );
+ val = kvVar.cap( 2 ).stripWhiteSpace();
+ bool state; // store booleans here
+ int n; // store ints here
+
+ // only apply view & renderer config stuff
+ if (onlyViewAndRenderer)
+ {
+ if ( vvl.contains( var ) ) // FIXME define above
+ setViewVariable( var, val );
+ }
+ else
+ {
+ // BOOL SETTINGS
+ if ( var == "word-wrap" && checkBoolValue( val, &state ) )
+ setWordWrap( state ); // ??? FIXME CHECK
+ else if ( var == "block-selection" && checkBoolValue( val, &state ) )
+ setBlockSelectionMode( state );
+ // KateConfig::configFlags
+ // FIXME should this be optimized to only a few calls? how?
+ else if ( var == "backspace-indents" && checkBoolValue( val, &state ) )
+ m_config->setConfigFlags( KateDocumentConfig::cfBackspaceIndents, state );
+ else if ( var == "replace-tabs" && checkBoolValue( val, &state ) )
+ m_config->setConfigFlags( KateDocumentConfig::cfReplaceTabsDyn, state );
+ else if ( var == "remove-trailing-space" && checkBoolValue( val, &state ) )
+ m_config->setConfigFlags( KateDocumentConfig::cfRemoveTrailingDyn, state );
+ else if ( var == "wrap-cursor" && checkBoolValue( val, &state ) )
+ m_config->setConfigFlags( KateDocumentConfig::cfWrapCursor, state );
+ else if ( var == "auto-brackets" && checkBoolValue( val, &state ) )
+ m_config->setConfigFlags( KateDocumentConfig::cfAutoBrackets, state );
+ else if ( var == "overwrite-mode" && checkBoolValue( val, &state ) )
+ m_config->setConfigFlags( KateDocumentConfig::cfOvr, state );
+ else if ( var == "keep-indent-profile" && checkBoolValue( val, &state ) )
+ m_config->setConfigFlags( KateDocumentConfig::cfKeepIndentProfile, state );
+ else if ( var == "keep-extra-spaces" && checkBoolValue( val, &state ) )
+ m_config->setConfigFlags( KateDocumentConfig::cfKeepExtraSpaces, state );
+ else if ( var == "tab-indents" && checkBoolValue( val, &state ) )
+ m_config->setConfigFlags( KateDocumentConfig::cfTabIndents, state );
+ else if ( var == "show-tabs" && checkBoolValue( val, &state ) )
+ m_config->setConfigFlags( KateDocumentConfig::cfShowTabs, state );
+ else if ( var == "space-indent" && checkBoolValue( val, &state ) )
+ m_config->setConfigFlags( KateDocumentConfig::cfSpaceIndent, state );
+ else if ( var == "smart-home" && checkBoolValue( val, &state ) )
+ m_config->setConfigFlags( KateDocumentConfig::cfSmartHome, state );
+ else if ( var == "replace-trailing-space-save" && checkBoolValue( val, &state ) )
+ m_config->setConfigFlags( KateDocumentConfig::cfRemoveSpaces, state );
+ else if ( var == "auto-insert-doxygen" && checkBoolValue( val, &state) )
+ m_config->setConfigFlags( KateDocumentConfig::cfDoxygenAutoTyping, state);
+ else if ( var == "mixed-indent" && checkBoolValue( val, &state ) )
+ m_config->setConfigFlags( KateDocumentConfig::cfMixedIndent, state );
+
+ // INTEGER SETTINGS
+ else if ( var == "tab-width" && checkIntValue( val, &n ) )
+ m_config->setTabWidth( n );
+ else if ( var == "indent-width" && checkIntValue( val, &n ) )
+ m_config->setIndentationWidth( n );
+ else if ( var == "indent-mode" )
+ {
+ if ( checkIntValue( val, &n ) )
+ m_config->setIndentationMode( n );
+ else
+ m_config->setIndentationMode( KateAutoIndent::modeNumber( val) );
+ }
+ else if ( var == "word-wrap-column" && checkIntValue( val, &n ) && n > 0 ) // uint, but hard word wrap at 0 will be no fun ;)
+ m_config->setWordWrapAt( n );
+ else if ( var == "undo-steps" && checkIntValue( val, &n ) && n >= 0 )
+ setUndoSteps( n );
+
+ // STRING SETTINGS
+ else if ( var == "eol" || var == "end-of-line" )
+ {
+ QStringList l;
+ l << "unix" << "dos" << "mac";
+ if ( (n = l.findIndex( val.lower() )) != -1 )
+ m_config->setEol( n );
+ }
+ else if ( var == "encoding" )
+ m_config->setEncoding( val );
+ else if ( var == "syntax" || var == "hl" )
+ {
+ for ( uint i=0; i < hlModeCount(); i++ )
+ {
+ if ( hlModeName( i ).lower() == val.lower() )
+ {
+ setHlMode( i );
+ break;
+ }
+ }
+ }
+
+ // VIEW SETTINGS
+ else if ( vvl.contains( var ) )
+ setViewVariable( var, val );
+ else
+ {
+ m_storedVariables.insert( var, val );
+ emit variableChanged( var, val );
+ }
+ }
+ }
+}
+
+void KateDocument::setViewVariable( QString var, QString val )
+{
+ KateView *v;
+ bool state;
+ int n;
+ QColor c;
+ for (v = m_views.first(); v != 0L; v= m_views.next() )
+ {
+ if ( var == "dynamic-word-wrap" && checkBoolValue( val, &state ) )
+ v->config()->setDynWordWrap( state );
+ else if ( var == "persistent-selection" && checkBoolValue( val, &state ) )
+ v->config()->setPersistentSelection( state );
+ //else if ( var = "dynamic-word-wrap-indicators" )
+ else if ( var == "line-numbers" && checkBoolValue( val, &state ) )
+ v->config()->setLineNumbers( state );
+ else if (var == "icon-border" && checkBoolValue( val, &state ) )
+ v->config()->setIconBar( state );
+ else if (var == "folding-markers" && checkBoolValue( val, &state ) )
+ v->config()->setFoldingBar( state );
+ else if ( var == "auto-center-lines" && checkIntValue( val, &n ) )
+ v->config()->setAutoCenterLines( n ); // FIXME uint, > N ??
+ else if ( var == "icon-bar-color" && checkColorValue( val, c ) )
+ v->renderer()->config()->setIconBarColor( c );
+ // RENDERER
+ else if ( var == "background-color" && checkColorValue( val, c ) )
+ v->renderer()->config()->setBackgroundColor( c );
+ else if ( var == "selection-color" && checkColorValue( val, c ) )
+ v->renderer()->config()->setSelectionColor( c );
+ else if ( var == "current-line-color" && checkColorValue( val, c ) )
+ v->renderer()->config()->setHighlightedLineColor( c );
+ else if ( var == "bracket-highlight-color" && checkColorValue( val, c ) )
+ v->renderer()->config()->setHighlightedBracketColor( c );
+ else if ( var == "word-wrap-marker-color" && checkColorValue( val, c ) )
+ v->renderer()->config()->setWordWrapMarkerColor( c );
+ else if ( var == "font" || ( var == "font-size" && checkIntValue( val, &n ) ) )
+ {
+ QFont _f( *v->renderer()->config()->font( ) );
+
+ if ( var == "font" )
+ {
+ _f.setFamily( val );
+ _f.setFixedPitch( QFont( val ).fixedPitch() );
+ }
+ else
+ _f.setPointSize( n );
+
+ v->renderer()->config()->setFont( _f );
+ }
+ else if ( var == "scheme" )
+ {
+ v->renderer()->config()->setSchema( KateFactory::self()->schemaManager()->number( val ) );
+ }
+ }
+}
+
+bool KateDocument::checkBoolValue( QString val, bool *result )
+{
+ val = val.stripWhiteSpace().lower();
+ QStringList l;
+ l << "1" << "on" << "true";
+ if ( l.contains( val ) )
+ {
+ *result = true;
+ return true;
+ }
+ l.clear();
+ l << "0" << "off" << "false";
+ if ( l.contains( val ) )
+ {
+ *result = false;
+ return true;
+ }
+ return false;
+}
+
+bool KateDocument::checkIntValue( QString val, int *result )
+{
+ bool ret( false );
+ *result = val.toInt( &ret );
+ return ret;
+}
+
+bool KateDocument::checkColorValue( QString val, QColor &c )
+{
+ c.setNamedColor( val );
+ return c.isValid();
+}
+
+// KTextEditor::variable
+QString KateDocument::variable( const QString &name ) const
+{
+ if ( m_storedVariables.contains( name ) )
+ return m_storedVariables[ name ];
+
+ return "";
+}
+
+//END
+
+void KateDocument::slotModOnHdDirty (const QString &path)
+{
+ if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 1))
+ {
+ // compare md5 with the one we have (if we have one)
+ if ( ! m_digest.isEmpty() )
+ {
+ QCString tmp;
+ if ( createDigest( tmp ) && tmp == m_digest )
+ return;
+ }
+
+ m_modOnHd = true;
+ m_modOnHdReason = 1;
+
+ // reenable dialog if not running atm
+ if (m_isasking == -1)
+ m_isasking = false;
+
+ emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason);
+ }
+}
+
+void KateDocument::slotModOnHdCreated (const QString &path)
+{
+ if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 2))
+ {
+ m_modOnHd = true;
+ m_modOnHdReason = 2;
+
+ // reenable dialog if not running atm
+ if (m_isasking == -1)
+ m_isasking = false;
+
+ emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason);
+ }
+}
+
+void KateDocument::slotModOnHdDeleted (const QString &path)
+{
+ if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 3))
+ {
+ m_modOnHd = true;
+ m_modOnHdReason = 3;
+
+ // reenable dialog if not running atm
+ if (m_isasking == -1)
+ m_isasking = false;
+
+ emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason);
+ }
+}
+
+bool KateDocument::createDigest( QCString &result )
+{
+ bool ret = false;
+ result = "";
+ if ( url().isLocalFile() )
+ {
+ QFile f ( url().path() );
+ if ( f.open( IO_ReadOnly) )
+ {
+ KMD5 md5;
+ ret = md5.update( f );
+ md5.hexDigest( result );
+ f.close();
+ ret = true;
+ }
+ }
+ return ret;
+}
+
+QString KateDocument::reasonedMOHString() const
+{
+ switch( m_modOnHdReason )
+ {
+ case 1:
+ return i18n("The file '%1' was modified by another program.").arg( url().prettyURL() );
+ break;
+ case 2:
+ return i18n("The file '%1' was created by another program.").arg( url().prettyURL() );
+ break;
+ case 3:
+ return i18n("The file '%1' was deleted by another program.").arg( url().prettyURL() );
+ break;
+ default:
+ return QString();
+ }
+}
+
+void KateDocument::removeTrailingSpace( uint line )
+{
+ // remove trailing spaces from left line if required
+ if ( config()->configFlags() & KateDocumentConfig::cfRemoveTrailingDyn )
+ {
+ KateTextLine::Ptr ln = kateTextLine( line );
+
+ if ( ! ln ) return;
+
+ if ( line == activeView()->cursorLine()
+ && activeView()->cursorColumnReal() >= (uint)kMax(0,ln->lastChar()) )
+ return;
+
+ if ( ln->length() )
+ {
+ uint p = ln->lastChar() + 1;
+ uint l = ln->length() - p;
+ if ( l )
+ editRemoveText( line, p, l);
+ }
+ }
+}
+
+void KateDocument::updateFileType (int newType, bool user)
+{
+ if (user || !m_fileTypeSetByUser)
+ {
+ const KateFileType *t = 0;
+ if ((newType == -1) || (t = KateFactory::self()->fileTypeManager()->fileType (newType)))
+ {
+ m_fileType = newType;
+
+ if (t)
+ {
+ m_config->configStart();
+ // views!
+ KateView *v;
+ for (v = m_views.first(); v != 0L; v= m_views.next() )
+ {
+ v->config()->configStart();
+ v->renderer()->config()->configStart();
+ }
+
+ readVariableLine( t->varLine );
+
+ m_config->configEnd();
+ for (v = m_views.first(); v != 0L; v= m_views.next() )
+ {
+ v->config()->configEnd();
+ v->renderer()->config()->configEnd();
+ }
+ }
+ }
+ }
+}
+
+uint KateDocument::documentNumber () const
+{
+ return KTextEditor::Document::documentNumber ();
+}
+
+
+
+
+void KateDocument::slotQueryClose_save(bool *handled, bool* abortClosing) {
+ *handled=true;
+ *abortClosing=true;
+ if (m_url.isEmpty())
+ {
+ KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveURLAndEncoding(config()->encoding(),
+ QString::null,QString::null,0,i18n("Save File"));
+
+ if( res.URLs.isEmpty() || !checkOverwrite( res.URLs.first() ) ) {
+ *abortClosing=true;
+ return;
+ }
+ setEncoding( res.encoding );
+ saveAs( res.URLs.first() );
+ *abortClosing=false;
+ }
+ else
+ {
+ save();
+ *abortClosing=false;
+ }
+
+}
+
+bool KateDocument::checkOverwrite( KURL u )
+{
+ if( !u.isLocalFile() )
+ return true;
+
+ QFileInfo info( u.path() );
+ if( !info.exists() )
+ return true;
+
+ return KMessageBox::Cancel != KMessageBox::warningContinueCancel( 0,
+ i18n( "A file named \"%1\" already exists. "
+ "Are you sure you want to overwrite it?" ).arg( info.fileName() ),
+ i18n( "Overwrite File?" ),
+ i18n( "&Overwrite" ) );
+}
+
+void KateDocument::setDefaultEncoding (const QString &encoding)
+{
+ s_defaultEncoding = encoding;
+}
+
+//BEGIN KTextEditor::TemplateInterface
+bool KateDocument::insertTemplateTextImplementation ( uint line, uint column, const QString &templateString, const QMap<QString,QString> &initialValues, QWidget *) {
+ return (new KateTemplateHandler(this,line,column,templateString,initialValues))->initOk();
+}
+
+void KateDocument::testTemplateCode() {
+ int col=activeView()->cursorColumn();
+ int line=activeView()->cursorLine();
+ insertTemplateText(line,col,"for ${index} \\${NOPLACEHOLDER} ${index} ${blah} ${fullname} \\$${Placeholder} \\${${PLACEHOLDER2}}\n next line:${ANOTHERPLACEHOLDER} $${DOLLARBEFOREPLACEHOLDER} {NOTHING} {\n${cursor}\n}",QMap<QString,QString>());
+}
+
+bool KateDocument::invokeTabInterceptor(KKey key) {
+ if (m_tabInterceptor) return (*m_tabInterceptor)(key);
+ return false;
+}
+
+bool KateDocument::setTabInterceptor(KateKeyInterceptorFunctor *interceptor) {
+ if (m_tabInterceptor) return false;
+ m_tabInterceptor=interceptor;
+ return true;
+}
+
+bool KateDocument::removeTabInterceptor(KateKeyInterceptorFunctor *interceptor) {
+ if (m_tabInterceptor!=interceptor) return false;
+ m_tabInterceptor=0;
+ return true;
+}
+//END KTextEditor::TemplateInterface
+
+//BEGIN DEPRECATED STUFF
+ bool KateDocument::setSelection ( uint startLine, uint startCol, uint endLine, uint endCol )
+{ if (m_activeView) return m_activeView->setSelection (startLine, startCol, endLine, endCol); return false; }
+
+ bool KateDocument::clearSelection ()
+ { if (m_activeView) return m_activeView->clearSelection(); return false; }
+
+ bool KateDocument::hasSelection () const
+ { if (m_activeView) return m_activeView->hasSelection (); return false; }
+
+ QString KateDocument::selection () const
+ { if (m_activeView) return m_activeView->selection (); return QString(""); }
+
+ bool KateDocument::removeSelectedText ()
+ { if (m_activeView) return m_activeView->removeSelectedText (); return false; }
+
+ bool KateDocument::selectAll()
+ { if (m_activeView) return m_activeView->selectAll (); return false; }
+
+ int KateDocument::selStartLine()
+ { if (m_activeView) return m_activeView->selStartLine (); return 0; }
+
+ int KateDocument::selStartCol()
+ { if (m_activeView) return m_activeView->selStartCol (); return 0; }
+
+ int KateDocument::selEndLine()
+ { if (m_activeView) return m_activeView->selEndLine (); return 0; }
+
+ int KateDocument::selEndCol()
+ { if (m_activeView) return m_activeView->selEndCol (); return 0; }
+
+ bool KateDocument::blockSelectionMode ()
+ { if (m_activeView) return m_activeView->blockSelectionMode (); return false; }
+
+bool KateDocument::setBlockSelectionMode (bool on)
+ { if (m_activeView) return m_activeView->setBlockSelectionMode (on); return false; }
+
+bool KateDocument::toggleBlockSelectionMode ()
+ { if (m_activeView) return m_activeView->toggleBlockSelectionMode (); return false; }
+//END DEPRECATED
+
+//END DEPRECATED STUFF
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katedocument.h b/kate/part/katedocument.h
new file mode 100644
index 000000000..c1c5ab169
--- /dev/null
+++ b/kate/part/katedocument.h
@@ -0,0 +1,1073 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2001-2004 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
+ Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef _KATE_DOCUMENT_H_
+#define _KATE_DOCUMENT_H_
+
+#include "katesupercursor.h"
+#include "katetextline.h"
+#include "kateundo.h"
+#include "katebuffer.h"
+#include "katecodefoldinghelpers.h"
+
+#include "../interfaces/document.h"
+
+#include <ktexteditor/configinterfaceextension.h>
+#include <ktexteditor/encodinginterface.h>
+#include <ktexteditor/sessionconfiginterface.h>
+#include <ktexteditor/editinterfaceext.h>
+#include <ktexteditor/templateinterface.h>
+
+#include <dcopobject.h>
+
+#include <kmimetype.h>
+#include <klocale.h>
+
+#include <qintdict.h>
+#include <qmap.h>
+#include <qdatetime.h>
+
+namespace KTextEditor { class Plugin; }
+
+namespace KIO { class TransferJob; }
+
+class KateUndoGroup;
+class KateCmd;
+class KateAttribute;
+class KateAutoIndent;
+class KateCodeFoldingTree;
+class KateBuffer;
+class KateView;
+class KateViewInternal;
+class KateArbitraryHighlight;
+class KateSuperRange;
+class KateLineInfo;
+class KateBrowserExtension;
+class KateDocumentConfig;
+class KateHighlighting;
+class KatePartPluginItem;
+class KatePartPluginInfo;
+
+class KTempFile;
+
+class QTimer;
+
+class KateKeyInterceptorFunctor;
+
+//
+// Kate KTextEditor::Document class (and even KTextEditor::Editor ;)
+//
+class KateDocument : public Kate::Document,
+ public Kate::DocumentExt,
+ public KTextEditor::ConfigInterfaceExtension,
+ public KTextEditor::EncodingInterface,
+ public KTextEditor::SessionConfigInterface,
+ public KTextEditor::EditInterfaceExt,
+ public KTextEditor::TemplateInterface,
+ public DCOPObject
+{
+ K_DCOP
+ Q_OBJECT
+
+ friend class KateViewInternal;
+ friend class KateRenderer;
+
+ public:
+ KateDocument (bool bSingleViewMode=false, bool bBrowserView=false, bool bReadOnly=false,
+ QWidget *parentWidget = 0, const char *widgetName = 0, QObject * = 0, const char * = 0);
+ ~KateDocument ();
+
+ bool closeURL();
+
+ //
+ // Plugins section
+ //
+ public:
+ void unloadAllPlugins ();
+
+ void enableAllPluginsGUI (KateView *view);
+ void disableAllPluginsGUI (KateView *view);
+
+ void loadPlugin (uint pluginIndex);
+ void unloadPlugin (uint pluginIndex);
+
+ void enablePluginGUI (KTextEditor::Plugin *plugin, KateView *view);
+ void enablePluginGUI (KTextEditor::Plugin *plugin);
+
+ void disablePluginGUI (KTextEditor::Plugin *plugin, KateView *view);
+ void disablePluginGUI (KTextEditor::Plugin *plugin);
+
+ private:
+ QMemArray<KTextEditor::Plugin *> m_plugins;
+
+ public:
+ bool readOnly () const { return m_bReadOnly; }
+ bool browserView () const { return m_bBrowserView; }
+ bool singleViewMode () const { return m_bSingleViewMode; }
+ KateBrowserExtension *browserExtension () { return m_extension; }
+
+ private:
+ // only to make part work, don't change it !
+ bool m_bSingleViewMode;
+ bool m_bBrowserView;
+ bool m_bReadOnly;
+ KateBrowserExtension *m_extension;
+
+ //
+ // KTextEditor::Document stuff
+ //
+ public:
+ KTextEditor::View *createView( QWidget *parent, const char *name );
+ QPtrList<KTextEditor::View> views () const;
+
+ inline KateView *activeView () const { return m_activeView; }
+
+ private:
+ QPtrList<KateView> m_views;
+ QPtrList<KTextEditor::View> m_textEditViews;
+ KateView *m_activeView;
+
+ /**
+ * set the active view.
+ *
+ * If @p view is allready the active view, nothing is done.
+ *
+ * If the document is modified on disk, ask the user what to do.
+ *
+ * @since Kate 2.4
+ */
+ void setActiveView( KateView *view );
+
+ //
+ // KTextEditor::ConfigInterfaceExtension stuff
+ //
+ public slots:
+ uint configPages () const;
+ KTextEditor::ConfigPage *configPage (uint number = 0, QWidget *parent = 0, const char *name=0 );
+ QString configPageName (uint number = 0) const;
+ QString configPageFullName (uint number = 0) const;
+ QPixmap configPagePixmap (uint number = 0, int size = KIcon::SizeSmall) const;
+
+ //
+ // KTextEditor::EditInterface stuff
+ //
+ public slots:
+ QString text() const;
+
+ QString text ( uint startLine, uint startCol, uint endLine, uint endCol ) const;
+ QString text ( uint startLine, uint startCol, uint endLine, uint endCol, bool blockwise ) const;
+
+ QString textLine ( uint line ) const;
+
+ bool setText(const QString &);
+ bool clear ();
+
+ bool insertText ( uint line, uint col, const QString &s );
+ bool insertText ( uint line, uint col, const QString &s, bool blockwise );
+
+ bool removeText ( uint startLine, uint startCol, uint endLine, uint endCol );
+ bool removeText ( uint startLine, uint startCol, uint endLine, uint endCol, bool blockwise );
+
+ bool insertLine ( uint line, const QString &s );
+ bool removeLine ( uint line );
+
+ uint numLines() const;
+ uint numVisLines() const;
+ uint length () const;
+ int lineLength ( uint line ) const;
+
+ signals:
+ void textChanged ();
+ void charactersInteractivelyInserted(int ,int ,const QString&);
+ void charactersSemiInteractivelyInserted(int ,int ,const QString&);
+ void backspacePressed();
+
+ public:
+//BEGIN editStart/editEnd (start, end, undo, cursor update, view update)
+ /**
+ * Enclose editor actions with @p editStart() and @p editEnd() to group
+ * them.
+ * @param withUndo if true, add undo history
+ */
+ void editStart (bool withUndo = true);
+ /** Same as editStart() with undo */
+ void editBegin () { editStart(); }
+ /**
+ * End a editor operation.
+ * @see editStart()
+ */
+ void editEnd ();
+ private:
+ bool m_isInUndo; ///< set to true in undo/redo
+
+//END editStart/editEnd
+
+//BEGIN LINE BASED INSERT/REMOVE STUFF (editStart() and editEnd() included)
+ public:
+ /**
+ * Add a string in the given line/column
+ * @param line line number
+ * @param col column
+ * @param s string to be inserted
+ * @return true on success
+ */
+ bool editInsertText ( uint line, uint col, const QString &s );
+ /**
+ * Remove a string in the given line/column
+ * @param line line number
+ * @param col column
+ * @param len length of text to be removed
+ * @return true on success
+ */
+ bool editRemoveText ( uint line, uint col, uint len );
+
+ /**
+ * Mark @p line as @p autowrapped. This is necessary if static word warp is
+ * enabled, because we have to know whether to insert a new line or add the
+ * wrapped words to the followin line.
+ * @param line line number
+ * @param autowrapped autowrapped?
+ * @return true on success
+ */
+ bool editMarkLineAutoWrapped ( uint line, bool autowrapped );
+
+ /**
+ * Wrap @p line. If @p newLine is true, ignore the textline's flag
+ * KateTextLine::flagAutoWrapped and force a new line. Whether a new line
+ * was needed/added you can grab with @p newLineAdded.
+ * @param line line number
+ * @param col column
+ * @param newLine if true, force a new line
+ * @param newLineAdded return value is true, if new line was added (may be 0)
+ * @return true on success
+ */
+ bool editWrapLine ( uint line, uint col, bool newLine = true, bool *newLineAdded = 0 );
+ /**
+ * Unwrap @p line. If @p removeLine is true, we force to join the lines. If
+ * @p removeLine is true, @p length is ignored (eg not needed).
+ * @param line line number
+ * @param removeLine if true, force to remove the next line
+ * @param length length of the line
+ * @return true on success
+ */
+ bool editUnWrapLine ( uint line, bool removeLine = true, uint length = 0 );
+
+ /**
+ * Insert a string at the given line.
+ * @param line line number
+ * @param s string to insert
+ * @return true on success
+ */
+ bool editInsertLine ( uint line, const QString &s );
+ /**
+ * Remove a line
+ * @param line line number
+ * @return true on success
+ */
+ bool editRemoveLine ( uint line );
+
+ /**
+ * Remove a line
+ * @param startLine line to begin wrapping
+ * @param endLine line to stop wrapping
+ * @return true on success
+ */
+ bool wrapText (uint startLine, uint endLine);
+//END LINE BASED INSERT/REMOVE STUFF
+
+ signals:
+ /**
+ * Emitted each time text is inserted into a pre-existing line, including appends.
+ * Does not include newly inserted lines at the moment. ### needed?
+ */
+ void editTextInserted ( uint line, uint col, uint len);
+
+ /**
+ * Emitted each time text is removed from a line, including truncates and space removal.
+ */
+ void editTextRemoved ( uint line, uint col, uint len);
+
+ /**
+ * Emmitted when text from @p line was wrapped at position pos onto line @p nextLine.
+ */
+ void editLineWrapped ( uint line, uint col, uint len );
+
+ /**
+ * Emitted each time text from @p nextLine was upwrapped onto @p line.
+ */
+ void editLineUnWrapped ( uint line, uint col );
+
+ /**
+ * Emitted whenever a line is inserted before @p line, becoming itself line @ line.
+ */
+ void editLineInserted ( uint line );
+
+ /**
+ * Emitted when a line is deleted.
+ */
+ void editLineRemoved ( uint line );
+
+ private:
+ void undoStart();
+ void undoEnd();
+ void undoSafePoint();
+
+ private slots:
+ void undoCancel();
+
+ private:
+ void editAddUndo (KateUndoGroup::UndoType type, uint line, uint col, uint len, const QString &text);
+
+ uint editSessionNumber;
+ bool editIsRunning;
+ bool editWithUndo;
+ bool m_undoComplexMerge;
+ KateUndoGroup* m_editCurrentUndo;
+
+ //
+ // KTextEditor::UndoInterface stuff
+ //
+ public slots:
+ void undo ();
+ void redo ();
+ void clearUndo ();
+ void clearRedo ();
+
+ uint undoCount () const;
+ uint redoCount () const;
+
+ uint undoSteps () const;
+ void setUndoSteps ( uint steps );
+
+ private:
+ friend class KateTemplateHandler;
+
+ private:
+ QPtrList<KateSuperCursor> m_superCursors;
+
+ //
+ // some internals for undo/redo
+ //
+ QPtrList<KateUndoGroup> undoItems;
+ QPtrList<KateUndoGroup> redoItems;
+ bool m_undoDontMerge; //create a setter later on and remove the friend declaration
+ bool m_undoIgnoreCancel;
+ QTimer* m_undoMergeTimer;
+ // these two variables are for resetting the document to
+ // non-modified if all changes have been undone...
+ KateUndoGroup* lastUndoGroupWhenSaved;
+ KateUndoGroup* lastRedoGroupWhenSaved;
+ bool docWasSavedWhenUndoWasEmpty;
+ bool docWasSavedWhenRedoWasEmpty;
+
+ // this sets
+ void updateModified();
+
+ signals:
+ void undoChanged ();
+ void textInserted(int line,int column);
+
+ //
+ // KTextEditor::CursorInterface stuff
+ //
+ public slots:
+ KTextEditor::Cursor *createCursor ();
+ QPtrList<KTextEditor::Cursor> cursors () const;
+
+ private:
+ QPtrList<KTextEditor::Cursor> myCursors;
+
+ //
+ // KTextEditor::SearchInterface stuff
+ //
+ public slots:
+ bool searchText (unsigned int startLine, unsigned int startCol,
+ const QString &text, unsigned int *foundAtLine, unsigned int *foundAtCol,
+ unsigned int *matchLen, bool casesensitive = true, bool backwards = false);
+ bool searchText (unsigned int startLine, unsigned int startCol,
+ const QRegExp &regexp, unsigned int *foundAtLine, unsigned int *foundAtCol,
+ unsigned int *matchLen, bool backwards = false);
+
+ //
+ // KTextEditor::HighlightingInterface stuff
+ //
+ public slots:
+ uint hlMode ();
+ bool setHlMode (uint mode);
+ uint hlModeCount ();
+ QString hlModeName (uint mode);
+ QString hlModeSectionName (uint mode);
+
+ public:
+ void bufferHlChanged ();
+
+ private:
+ void setDontChangeHlOnSave();
+
+ signals:
+ void hlChanged ();
+
+ //
+ // Kate::ArbitraryHighlightingInterface stuff
+ //
+ public:
+ KateArbitraryHighlight* arbitraryHL() const { return m_arbitraryHL; };
+
+ private slots:
+ void tagArbitraryLines(KateView* view, KateSuperRange* range);
+
+ //
+ // KTextEditor::ConfigInterface stuff
+ //
+ public slots:
+ void readConfig ();
+ void writeConfig ();
+ void readConfig (KConfig *);
+ void writeConfig (KConfig *);
+ void readSessionConfig (KConfig *);
+ void writeSessionConfig (KConfig *);
+ void configDialog ();
+
+ //
+ // KTextEditor::MarkInterface and MarkInterfaceExtension
+ //
+ public slots:
+ uint mark( uint line );
+
+ void setMark( uint line, uint markType );
+ void clearMark( uint line );
+
+ void addMark( uint line, uint markType );
+ void removeMark( uint line, uint markType );
+
+ QPtrList<KTextEditor::Mark> marks();
+ void clearMarks();
+
+ void setPixmap( MarkInterface::MarkTypes, const QPixmap& );
+ void setDescription( MarkInterface::MarkTypes, const QString& );
+ QString markDescription( MarkInterface::MarkTypes );
+ QPixmap *markPixmap( MarkInterface::MarkTypes );
+ QColor markColor( MarkInterface::MarkTypes );
+
+ void setMarksUserChangable( uint markMask );
+ uint editableMarks();
+
+ signals:
+ void marksChanged();
+ void markChanged( KTextEditor::Mark, KTextEditor::MarkInterfaceExtension::MarkChangeAction );
+
+ private:
+ QIntDict<KTextEditor::Mark> m_marks;
+ QIntDict<QPixmap> m_markPixmaps;
+ QIntDict<QString> m_markDescriptions;
+ uint m_editableMarks;
+
+ //
+ // KTextEditor::PrintInterface
+ //
+ public slots:
+ bool printDialog ();
+ bool print ();
+
+ //
+ // KTextEditor::DocumentInfoInterface ( ### unfinished )
+ //
+ public:
+ /**
+ * @return the name of the mimetype for the document.
+ *
+ * This method is using KMimeType::findByURL, and if the pointer
+ * is then still the default MimeType for a nonlocal or unsaved file,
+ * uses mimeTypeForContent().
+ *
+ * @since Kate 2.3
+ */
+ QString mimeType();
+
+ /**
+ * @return the calculated size in bytes that the document would have when saved to
+ * disk.
+ *
+ * @since Kate 2.3
+ * @todo implement this (it returns 0 right now)
+ */
+ long fileSize();
+
+ /**
+ * @return the calculated size the document would have when saved to disk
+ * as a human readable string.
+ *
+ * @since Kate 2.3
+ * @todo implement this (it returns "UNKNOWN")
+ */
+ QString niceFileSize();
+
+ /**
+ * @return a pointer to the KMimeType for this document, found by analyzing the
+ * actual content.
+ *
+ * Note that this method is *not* part of the DocumentInfoInterface.
+ *
+ * @since Kate 2.3
+ */
+ KMimeType::Ptr mimeTypeForContent();
+
+ //
+ // KTextEditor::VariableInterface
+ //
+ public:
+ QString variable( const QString &name ) const;
+
+ signals:
+ void variableChanged( const QString &, const QString & );
+
+ private:
+ QMap<QString, QString> m_storedVariables;
+
+ //
+ // KParts::ReadWrite stuff
+ //
+ public:
+ bool openURL( const KURL &url );
+
+ /* Anders:
+ I reimplemented this, since i need to check if backup succeeded
+ if requested */
+ bool save();
+
+ /* Anders: Reimplemented to do kate specific stuff */
+ bool saveAs( const KURL &url );
+
+ bool openFile (KIO::Job * job);
+ bool openFile ();
+
+ bool saveFile ();
+
+ void setReadWrite ( bool readwrite = true );
+
+ void setModified( bool m );
+
+ private slots:
+ void slotDataKate ( KIO::Job* kio_job, const QByteArray &data );
+ void slotFinishedKate ( KIO::Job * job );
+
+ private:
+ void abortLoadKate();
+
+ void activateDirWatch ();
+ void deactivateDirWatch ();
+
+ QString m_dirWatchFile;
+
+ //
+ // Kate::Document stuff, this is all deprecated!!!!!!!!!!
+ //
+ public:
+ Kate::ConfigPage *colorConfigPage (QWidget *) { return 0; }
+ Kate::ConfigPage *fontConfigPage (QWidget *) { return 0; }
+ Kate::ConfigPage *indentConfigPage (QWidget *) { return 0; }
+ Kate::ConfigPage *selectConfigPage (QWidget *) { return 0; }
+ Kate::ConfigPage *editConfigPage (QWidget *) { return 0; }
+ Kate::ConfigPage *keysConfigPage (QWidget *) { return 0; }
+ Kate::ConfigPage *hlConfigPage (QWidget *) { return 0; }
+ Kate::ConfigPage *viewDefaultsConfigPage (QWidget *) { return 0; }
+ Kate::ConfigPage *saveConfigPage( QWidget * ) { return 0; }
+
+ Kate::ActionMenu *hlActionMenu (const QString& /* text */, QObject* /* parent */ = 0, const char* /* name */ = 0) { return 0; }
+ Kate::ActionMenu *exportActionMenu (const QString& /* text */, QObject* /* parent */ = 0, const char* /* name */ = 0) { return 0; }
+
+ public:
+ /**
+ * Type chars in a view
+ */
+ bool typeChars ( KateView *type, const QString &chars );
+
+ /**
+ * gets the last line number (numLines() -1)
+ */
+ inline uint lastLine() const { return numLines()-1; }
+
+ uint configFlags ();
+ void setConfigFlags (uint flags);
+
+ // Repaint all of all of the views
+ void repaintViews(bool paintOnlyDirty = true);
+
+ inline KateHighlighting *highlight () { return m_buffer->highlight(); }
+
+ inline KateHighlighting *highlight () const { return m_buffer->highlight(); }
+
+ public slots: //please keep prototypes and implementations in same order
+ void tagLines(int start, int end);
+ void tagLines(KateTextCursor start, KateTextCursor end);
+
+ //export feature, obsolute
+ public slots:
+ void exportAs(const QString&) { };
+
+ signals:
+ void modifiedChanged ();
+ void preHighlightChanged(uint);
+
+ private slots:
+ void internalHlChanged();
+
+ public:
+ void addView(KTextEditor::View *);
+ void removeView(KTextEditor::View *);
+
+ void addSuperCursor(class KateSuperCursor *, bool privateC);
+ void removeSuperCursor(class KateSuperCursor *, bool privateC);
+
+ bool ownedView(KateView *);
+ bool isLastView(int numViews);
+
+ uint currentColumn( const KateTextCursor& );
+ void newLine( KateTextCursor&, KateViewInternal * ); // Changes input
+ void backspace( KateView *view, const KateTextCursor& );
+ void del( KateView *view, const KateTextCursor& );
+ void transpose( const KateTextCursor& );
+
+ void paste ( KateView* view );
+
+ public:
+ void insertIndentChars ( KateView *view );
+
+ void indent ( KateView *view, uint line, int change );
+ void comment ( KateView *view, uint line, uint column, int change );
+ void align ( KateView *view, uint line );
+
+ enum TextTransform { Uppercase, Lowercase, Capitalize };
+
+ /**
+ Handling uppercase, lowercase and capitalize for the view.
+
+ If there is a selection, that is transformed, otherwise for uppercase or
+ lowercase the character right of the cursor is transformed, for capitalize
+ the word under the cursor is transformed.
+ */
+ void transform ( KateView *view, const KateTextCursor &, TextTransform );
+ /**
+ Unwrap a range of lines.
+ */
+ void joinLines( uint first, uint last );
+
+ private:
+ void optimizeLeadingSpace( uint line, int flags, int change );
+ void replaceWithOptimizedSpace( uint line, uint upto_column, uint space, int flags );
+
+ bool removeStringFromBegining(int line, QString &str);
+ bool removeStringFromEnd(int line, QString &str);
+
+ /**
+ Find the position (line and col) of the next char
+ that is not a space. If found line and col point to the found character.
+ Otherwise they have both the value -1.
+ @param line Line of the character which is examined first.
+ @param col Column of the character which is examined first.
+ @return True if the specified or a following character is not a space
+ Otherwise false.
+ */
+ bool nextNonSpaceCharPos(int &line, int &col);
+
+ /**
+ Find the position (line and col) of the previous char
+ that is not a space. If found line and col point to the found character.
+ Otherwise they have both the value -1.
+ @return True if the specified or a preceding character is not a space.
+ Otherwise false.
+ */
+ bool previousNonSpaceCharPos(int &line, int &col);
+
+ /**
+ * Sets a comment marker as defined by the language providing the attribute
+ * @p attrib on the line @p line
+ */
+ void addStartLineCommentToSingleLine(int line, int attrib=0);
+ /**
+ * Removes a comment marker as defined by the language providing the attribute
+ * @p attrib on the line @p line
+ */
+ bool removeStartLineCommentFromSingleLine(int line, int attrib=0);
+
+ /**
+ * @see addStartLineCommentToSingleLine.
+ */
+ void addStartStopCommentToSingleLine(int line, int attrib=0);
+ /**
+ *@see removeStartLineCommentFromSingleLine.
+ */
+ bool removeStartStopCommentFromSingleLine(int line, int attrib=0);
+ /**
+ *@see removeStartLineCommentFromSingleLine.
+ */
+ bool removeStartStopCommentFromRegion(const KateTextCursor &start, const KateTextCursor &end, int attrib=0);
+
+ /**
+ * Add a comment marker as defined by the language providing the attribute
+ * @p attrib to each line in the selection.
+ */
+ void addStartStopCommentToSelection( KateView *view, int attrib=0 );
+ /**
+ * @see addStartStopCommentToSelection.
+ */
+ void addStartLineCommentToSelection( KateView *view, int attrib=0 );
+
+ /**
+ * Removes comment markers relevant to the language providing
+ * the attribuge @p attrib from each line in the selection.
+ *
+ * @return whether the operation succeded.
+ */
+ bool removeStartStopCommentFromSelection( KateView *view, int attrib=0 );
+ /**
+ * @see removeStartStopCommentFromSelection.
+ */
+ bool removeStartLineCommentFromSelection( KateView *view, int attrib=0 );
+
+ public:
+ QString getWord( const KateTextCursor& cursor );
+
+ public:
+ void tagAll();
+
+ void newBracketMark( const KateTextCursor& start, KateBracketRange& bm, int maxLines = -1 );
+ bool findMatchingBracket( KateTextCursor& start, KateTextCursor& end, int maxLines = -1 );
+
+ private:
+ void guiActivateEvent( KParts::GUIActivateEvent *ev );
+
+ public:
+
+ QString docName () {return m_docName;};
+
+ void setDocName (QString docName);
+
+ void lineInfo (KateLineInfo *info, unsigned int line);
+
+ KateCodeFoldingTree *foldingTree ();
+
+ public:
+ /**
+ * @return wheather the document is modified on disc since last saved.
+ *
+ * @since 3.3
+ */
+ bool isModifiedOnDisc() { return m_modOnHd; };
+
+ /** @deprecated */
+ void isModOnHD( bool =false ) {};
+
+ void setModifiedOnDisk( int reason );
+
+ public slots:
+ /**
+ * Ask the user what to do, if the file has been modified on disc.
+ * Reimplemented from Kate::Document.
+ *
+ * @since 3.3
+ */
+ void slotModifiedOnDisk( Kate::View *v=0 );
+
+ /**
+ * Reloads the current document from disc if possible
+ */
+ void reloadFile();
+
+ private:
+ int m_isasking; // don't reenter slotModifiedOnDisk when this is true
+ // -1: ignore once, 0: false, 1: true
+
+ public slots:
+ void setEncoding (const QString &e);
+ QString encoding() const;
+
+ public slots:
+ void setWordWrap (bool on);
+ bool wordWrap ();
+
+ void setWordWrapAt (uint col);
+ uint wordWrapAt ();
+
+ public slots:
+ void setPageUpDownMovesCursor(bool on);
+ bool pageUpDownMovesCursor();
+
+ signals:
+ void modStateChanged (Kate::Document *doc);
+ void nameChanged (Kate::Document *doc);
+
+ public slots:
+ // clear buffer/filename - update the views
+ void flush ();
+
+ signals:
+ /**
+ * The file has been saved (perhaps the name has changed). The main window
+ * can use this to change its caption
+ */
+ void fileNameChanged ();
+
+ public slots:
+ void applyWordWrap ();
+
+ // code folding
+ public:
+ inline uint getRealLine(unsigned int virtualLine)
+ {
+ return m_buffer->lineNumber (virtualLine);
+ }
+
+ inline uint getVirtualLine(unsigned int realLine)
+ {
+ return m_buffer->lineVisibleNumber (realLine);
+ }
+
+ inline uint visibleLines ()
+ {
+ return m_buffer->countVisible ();
+ }
+
+ inline KateTextLine::Ptr kateTextLine(uint i)
+ {
+ return m_buffer->line (i);
+ }
+
+ inline KateTextLine::Ptr plainKateTextLine(uint i)
+ {
+ return m_buffer->plainLine (i);
+ }
+
+ signals:
+ void codeFoldingUpdated();
+ void aboutToRemoveText(const KateTextRange&);
+ void textRemoved();
+
+ private slots:
+ void slotModOnHdDirty (const QString &path);
+ void slotModOnHdCreated (const QString &path);
+ void slotModOnHdDeleted (const QString &path);
+
+ private:
+ /**
+ * create a MD5 digest of the file, if it is a local file,
+ * and fill it into the string @p result.
+ * This is using KMD5::hexDigest().
+ *
+ * @return wheather the operation was attempted and succeded.
+ *
+ * @since 3.3
+ */
+ bool createDigest ( QCString &result );
+
+ /**
+ * create a string for the modonhd warnings, giving the reason.
+ *
+ * @since 3.3
+ */
+ QString reasonedMOHString() const;
+
+ /**
+ * Removes all trailing whitespace form @p line, if
+ * the cfRemoveTrailingDyn confg flag is set,
+ * and the active view cursor is not on line and behind
+ * the last nonspace character.
+ *
+ * @since 3.3
+ */
+ void removeTrailingSpace( uint line );
+
+ public:
+ void updateFileType (int newType, bool user = false);
+
+ int fileType () const { return m_fileType; };
+
+ //
+ // REALLY internal data ;)
+ //
+ private:
+ // text buffer
+ KateBuffer *m_buffer;
+
+ KateArbitraryHighlight* m_arbitraryHL;
+
+ KateAutoIndent *m_indenter;
+
+ bool hlSetByUser;
+
+ bool m_modOnHd;
+ unsigned char m_modOnHdReason;
+ QCString m_digest; // MD5 digest, updated on load/save
+
+ QString m_docName;
+ int m_docNameNumber;
+
+ // file type !!!
+ int m_fileType;
+ bool m_fileTypeSetByUser;
+
+ /**
+ * document is still reloading a file
+ */
+ bool m_reloading;
+ bool m_loading; ///< true in openFile() untill the data is read.
+ bool m_encodingSticky; ///< true when requests to set encoding should be ignored.
+
+ public slots:
+ void slotQueryClose_save(bool *handled, bool* abortClosing);
+
+ public:
+ void makeAttribs (bool needInvalidate = true);
+
+ static bool checkOverwrite( KURL u );
+
+ static void setDefaultEncoding (const QString &encoding);
+
+ void setEncodingSticky( bool e ) { m_encodingSticky = e; }
+
+ /**
+ * Configuration
+ */
+ public:
+ inline KateDocumentConfig *config () { return m_config; };
+
+ void updateConfig ();
+
+ private:
+ KateDocumentConfig *m_config;
+
+ /**
+ * Variable Reader
+ * TODO add register functionality/ktexteditor interface
+ */
+ private:
+ /**
+ * read dir config file
+ */
+ void readDirConfig ();
+
+ /**
+ Reads all the variables in the document.
+ Called when opening/saving a document
+ */
+ void readVariables(bool onlyViewAndRenderer = false);
+
+ /**
+ Reads and applies the variables in a single line
+ TODO registered variables gets saved in a [map]
+ */
+ void readVariableLine( QString t, bool onlyViewAndRenderer = false );
+ /**
+ Sets a view variable in all the views.
+ */
+ void setViewVariable( QString var, QString val );
+ /**
+ @return weather a string value could be converted
+ to a bool value as supported.
+ The value is put in *result.
+ */
+ static bool checkBoolValue( QString value, bool *result );
+ /**
+ @return weather a string value could be converted
+ to a integer value.
+ The value is put in *result.
+ */
+ static bool checkIntValue( QString value, int *result );
+ /**
+ Feeds value into @p col using QColor::setNamedColor() and returns
+ wheather the color is valid
+ */
+ static bool checkColorValue( QString value, QColor &col );
+
+ /**
+ * helper regex to capture the document variables
+ */
+ static QRegExp kvLine;
+ static QRegExp kvLineWildcard;
+ static QRegExp kvLineMime;
+ static QRegExp kvVar;
+
+ KIO::TransferJob *m_job;
+ KTempFile *m_tempFile;
+
+ // TemplateInterface
+ public:
+ bool setTabInterceptor(KateKeyInterceptorFunctor *interceptor); /* perhaps make it moregeneral like an eventfilter*/
+ bool removeTabInterceptor(KateKeyInterceptorFunctor *interceptor);
+ bool invokeTabInterceptor(KKey);
+
+ protected:
+ virtual bool insertTemplateTextImplementation ( uint line, uint column, const QString &templateString, const QMap<QString,QString> &initialValues, QWidget *parentWindow=0 );
+ KateKeyInterceptorFunctor *m_tabInterceptor;
+
+ protected slots:
+ void testTemplateCode();
+ void dumpRegionTree();
+
+ //BEGIN DEPRECATED
+ //
+ // KTextEditor::SelectionInterface stuff
+ // DEPRECATED, this will be removed for KDE 4.x !!!!!!!!!!!!!!!!!!!!
+ //
+ public slots:
+ bool setSelection ( uint startLine, uint startCol, uint endLine, uint endCol );
+ bool clearSelection ();
+ bool hasSelection () const;
+ QString selection () const;
+ bool removeSelectedText ();
+ bool selectAll();
+
+ //
+ // KTextEditor::SelectionInterfaceExt
+ //
+ int selStartLine();
+ int selStartCol();
+ int selEndLine();
+ int selEndCol();
+
+
+ // hack, only there to still support the deprecated stuff, will be removed for KDE 4.x
+ #undef signals
+ #define signals public
+ signals:
+ #undef signals
+ #define signals protected
+ void selectionChanged ();
+
+ //
+ // KTextEditor::BlockSelectionInterface stuff
+ // DEPRECATED, this will be removed for KDE 4.x !!!!!!!!!!!!!!!!!!!!
+ //
+ public slots:
+ bool blockSelectionMode ();
+ bool setBlockSelectionMode (bool on);
+ bool toggleBlockSelectionMode ();
+
+ private:
+//END DEPRECATED
+
+ k_dcop:
+ uint documentNumber () const;
+};
+
+#endif
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
+
diff --git a/kate/part/katedocumenthelpers.cpp b/kate/part/katedocumenthelpers.cpp
new file mode 100644
index 000000000..9055e7ec3
--- /dev/null
+++ b/kate/part/katedocumenthelpers.cpp
@@ -0,0 +1,56 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
+ Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "katedocumenthelpers.h"
+#include "katedocumenthelpers.moc"
+
+#include "katedocument.h"
+#include "kateview.h"
+
+#include <kpopupmenu.h>
+#include <klocale.h>
+
+KateBrowserExtension::KateBrowserExtension( KateDocument* doc )
+: KParts::BrowserExtension( doc, "katepartbrowserextension" ),
+ m_doc (doc)
+{
+ connect( doc, SIGNAL( selectionChanged() ),
+ this, SLOT( slotSelectionChanged() ) );
+ emit enableAction( "print", true );
+}
+
+void KateBrowserExtension::copy()
+{
+ if (m_doc->activeView())
+ m_doc->activeView()->copy();
+}
+
+void KateBrowserExtension::print()
+{
+ m_doc->printDialog();
+}
+
+void KateBrowserExtension::slotSelectionChanged()
+{
+ if (m_doc->activeView())
+ emit enableAction( "copy", m_doc->activeView()->hasSelection() );
+}
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katedocumenthelpers.h b/kate/part/katedocumenthelpers.h
new file mode 100644
index 000000000..8346ffa18
--- /dev/null
+++ b/kate/part/katedocumenthelpers.h
@@ -0,0 +1,71 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
+ Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KATE_DOCUMENT_HELPERS__
+#define __KATE_DOCUMENT_HELPERS__
+
+#include "../interfaces/document.h"
+
+#include <kparts/browserextension.h>
+
+#include <qstringlist.h>
+#include <qguardedptr.h>
+
+class KateDocument;
+
+/**
+ * Interface for embedding KateDocument into a browser
+ */
+class KateBrowserExtension : public KParts::BrowserExtension
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * Constructor
+ * @param doc parent document
+ */
+ KateBrowserExtension( KateDocument* doc );
+
+ public slots:
+ /**
+ * copy text to clipboard
+ */
+ void copy();
+
+ /**
+ * selection has changed
+ */
+ void slotSelectionChanged();
+
+ /**
+ * print the current file
+ */
+ void print();
+
+ private:
+ /**
+ * parent document
+ */
+ KateDocument* m_doc;
+};
+
+#endif
+
diff --git a/kate/part/katefactory.cpp b/kate/part/katefactory.cpp
new file mode 100644
index 000000000..a02d00fe7
--- /dev/null
+++ b/kate/part/katefactory.cpp
@@ -0,0 +1,276 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2001-2004 Christoph Cullmann <cullmann@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "config.h"
+#include "katefactory.h"
+
+#include "katedocument.h"
+#include "kateview.h"
+#include "katerenderer.h"
+#include "katecmds.h"
+#include "katefiletype.h"
+#include "kateschema.h"
+#include "katesearch.h"
+#include "kateconfig.h"
+#ifndef Q_WS_WIN //todo
+#include "katejscript.h"
+#endif
+#include "kateluaindentscript.h"
+#include "../interfaces/katecmd.h"
+
+#include <kvmallocator.h>
+#include <klocale.h>
+#include <kdirwatch.h>
+#include <kstaticdeleter.h>
+
+#include <qapplication.h>
+
+/**
+ * dummy wrapper factory to be sure nobody external deletes our katefactory
+ */
+class KateFactoryPublic : public KParts::Factory
+{
+ public:
+ /**
+ * reimplemented create object method
+ * @param parentWidget parent widget
+ * @param widgetName widget name
+ * @param parent QObject parent
+ * @param name object name
+ * @param classname class name
+ * @param args additional arguments
+ * @return constructed part object
+ */
+ KParts::Part *createPartObject ( QWidget *parentWidget, const char *widgetName, QObject *parent, const char *name, const char *classname, const QStringList &args )
+ {
+ return KateFactory::self()->createPartObject (parentWidget, widgetName, parent, name, classname, args);
+ }
+};
+
+K_EXPORT_COMPONENT_FACTORY( libkatepart, KateFactoryPublic )
+
+KateFactory *KateFactory::s_self = 0;
+
+KateFactory::KateFactory ()
+ : m_aboutData ("katepart", I18N_NOOP("Kate Part"), KATEPART_VERSION,
+ I18N_NOOP( "Embeddable editor component" ), KAboutData::License_LGPL_V2,
+ I18N_NOOP( "(c) 2000-2004 The Kate Authors" ), 0, "http://kate.kde.org")
+ , m_instance (&m_aboutData)
+ , m_plugins (KTrader::self()->query("KTextEditor/Plugin"))
+ , m_jscript (0)
+{
+ // set s_self
+ s_self = this;
+
+ //
+ // fill about data
+ //
+ m_aboutData.addAuthor ("Christoph Cullmann", I18N_NOOP("Maintainer"), "cullmann@kde.org", "http://www.babylon2k.de");
+ m_aboutData.addAuthor ("Anders Lund", I18N_NOOP("Core Developer"), "anders@alweb.dk", "http://www.alweb.dk");
+ m_aboutData.addAuthor ("Joseph Wenninger", I18N_NOOP("Core Developer"), "jowenn@kde.org","http://stud3.tuwien.ac.at/~e9925371");
+ m_aboutData.addAuthor ("Hamish Rodda",I18N_NOOP("Core Developer"), "rodda@kde.org");
+ m_aboutData.addAuthor ("Waldo Bastian", I18N_NOOP( "The cool buffersystem" ), "bastian@kde.org" );
+ m_aboutData.addAuthor ("Charles Samuels", I18N_NOOP("The Editing Commands"), "charles@kde.org");
+ m_aboutData.addAuthor ("Matt Newell", I18N_NOOP("Testing, ..."), "newellm@proaxis.com");
+ m_aboutData.addAuthor ("Michael Bartl", I18N_NOOP("Former Core Developer"), "michael.bartl1@chello.at");
+ m_aboutData.addAuthor ("Michael McCallum", I18N_NOOP("Core Developer"), "gholam@xtra.co.nz");
+ m_aboutData.addAuthor ("Jochen Wilhemly", I18N_NOOP( "KWrite Author" ), "digisnap@cs.tu-berlin.de" );
+ m_aboutData.addAuthor ("Michael Koch",I18N_NOOP("KWrite port to KParts"), "koch@kde.org");
+ m_aboutData.addAuthor ("Christian Gebauer", 0, "gebauer@kde.org" );
+ m_aboutData.addAuthor ("Simon Hausmann", 0, "hausmann@kde.org" );
+ m_aboutData.addAuthor ("Glen Parker",I18N_NOOP("KWrite Undo History, Kspell integration"), "glenebob@nwlink.com");
+ m_aboutData.addAuthor ("Scott Manson",I18N_NOOP("KWrite XML Syntax highlighting support"), "sdmanson@alltel.net");
+ m_aboutData.addAuthor ("John Firebaugh",I18N_NOOP("Patches and more"), "jfirebaugh@kde.org");
+ m_aboutData.addAuthor ("Dominik Haumann", I18N_NOOP("Developer & Highlight wizard"), "dhdev@gmx.de");
+
+ m_aboutData.addCredit ("Matteo Merli",I18N_NOOP("Highlighting for RPM Spec-Files, Perl, Diff and more"), "merlim@libero.it");
+ m_aboutData.addCredit ("Rocky Scaletta",I18N_NOOP("Highlighting for VHDL"), "rocky@purdue.edu");
+ m_aboutData.addCredit ("Yury Lebedev",I18N_NOOP("Highlighting for SQL"),"");
+ m_aboutData.addCredit ("Chris Ross",I18N_NOOP("Highlighting for Ferite"),"");
+ m_aboutData.addCredit ("Nick Roux",I18N_NOOP("Highlighting for ILERPG"),"");
+ m_aboutData.addCredit ("Carsten Niehaus", I18N_NOOP("Highlighting for LaTeX"),"");
+ m_aboutData.addCredit ("Per Wigren", I18N_NOOP("Highlighting for Makefiles, Python"),"");
+ m_aboutData.addCredit ("Jan Fritz", I18N_NOOP("Highlighting for Python"),"");
+ m_aboutData.addCredit ("Daniel Naber","","");
+ m_aboutData.addCredit ("Roland Pabel",I18N_NOOP("Highlighting for Scheme"),"");
+ m_aboutData.addCredit ("Cristi Dumitrescu",I18N_NOOP("PHP Keyword/Datatype list"),"");
+ m_aboutData.addCredit ("Carsten Pfeiffer", I18N_NOOP("Very nice help"), "");
+ m_aboutData.addCredit (I18N_NOOP("All people who have contributed and I have forgotten to mention"),"","");
+
+ m_aboutData.setTranslator(I18N_NOOP("_: NAME OF TRANSLATORS\nYour names"), I18N_NOOP("_: EMAIL OF TRANSLATORS\nYour emails"));
+
+ //
+ // dir watch
+ //
+ m_dirWatch = new KDirWatch ();
+
+ //
+ // filetype man
+ //
+ m_fileTypeManager = new KateFileTypeManager ();
+
+ //
+ // schema man
+ //
+ m_schemaManager = new KateSchemaManager ();
+
+ // config objects
+ m_documentConfig = new KateDocumentConfig ();
+ m_viewConfig = new KateViewConfig ();
+ m_rendererConfig = new KateRendererConfig ();
+
+ // vm allocator
+ m_vm = new KVMAllocator ();
+
+#ifndef Q_WS_WIN //todo
+ // create script man (search scripts) + register commands
+ m_jscriptManager = new KateJScriptManager ();
+ KateCmd::self()->registerCommand (m_jscriptManager);
+ m_indentScriptManagers.append(new KateIndentJScriptManager());
+#else
+ m_jscriptManager = 0;
+#endif
+#ifdef HAVE_LUA
+ m_indentScriptManagers.append(new KateLUAIndentScriptManager());
+#endif
+ //
+ // init the cmds
+ //
+ m_cmds.push_back (new KateCommands::CoreCommands());
+ m_cmds.push_back (new KateCommands::SedReplace ());
+ m_cmds.push_back (new KateCommands::Character ());
+ m_cmds.push_back (new KateCommands::Date ());
+ m_cmds.push_back (new SearchCommand());
+
+ for ( QValueList<Kate::Command *>::iterator it = m_cmds.begin(); it != m_cmds.end(); ++it )
+ KateCmd::self()->registerCommand (*it);
+}
+
+KateFactory::~KateFactory()
+{
+ /* ?hack? If MainApplication-Interface::quit is called by dcop the factory gets destroyed before all documents are destroyed eg in kwrite.
+ This could happen in other apps too. Since the documents try to unregister a new factory is created (in the ::self call) and registered with a
+ KStaticDeleter which causes a crash. That's why I ensure here that all documents are destroyed before the factory goes down (JOWENN)*/
+ while (KateDocument *doc=m_documents.first()) {
+ s_self=this; /* this is needed because the KStaticDeleter sets the global reference to 0, before it deletes the object it handles.
+ To prevent a crash again restore the factory pointer temporarily. (jowenn)*/
+ delete doc;
+ s_self=0;
+ }
+ /*another solution would be to set a flag in the documents, and inhibit calling of the deregistering methods, but I don't see a problem
+ if all created objects are deleted before their factory. If somebody sees a problem, let me know*/
+
+ delete m_documentConfig;
+ delete m_viewConfig;
+ delete m_rendererConfig;
+
+ delete m_fileTypeManager;
+ delete m_schemaManager;
+
+ delete m_dirWatch;
+
+ delete m_vm;
+
+ for ( QValueList<Kate::Command *>::iterator it = m_cmds.begin(); it != m_cmds.end(); ++it )
+ delete *it;
+
+ // cu manager
+ delete m_jscriptManager;
+ m_indentScriptManagers.setAutoDelete(true);
+ // cu jscript
+ delete m_jscript;
+}
+
+static KStaticDeleter<KateFactory> sdFactory;
+
+KateFactory *KateFactory::self ()
+{
+ if (!s_self) {
+ sdFactory.setObject(s_self, new KateFactory ());
+ }
+ return s_self;
+}
+
+KParts::Part *KateFactory::createPartObject ( QWidget *parentWidget, const char *widgetName, QObject *parent, const char *name, const char *_classname, const QStringList & )
+{
+ QCString classname( _classname );
+ bool bWantSingleView = ( classname != "KTextEditor::Document" && classname != "Kate::Document" );
+ bool bWantBrowserView = ( classname == "Browser/View" );
+ bool bWantReadOnly = (bWantBrowserView || ( classname == "KParts::ReadOnlyPart" ));
+
+ KParts::ReadWritePart *part = new KateDocument (bWantSingleView, bWantBrowserView, bWantReadOnly, parentWidget, widgetName, parent, name);
+ part->setReadWrite( !bWantReadOnly );
+
+ return part;
+}
+
+void KateFactory::registerDocument ( KateDocument *doc )
+{
+ m_documents.append( doc );
+}
+
+void KateFactory::deregisterDocument ( KateDocument *doc )
+{
+ m_documents.removeRef( doc );
+}
+
+void KateFactory::registerView ( KateView *view )
+{
+ m_views.append( view );
+}
+
+void KateFactory::deregisterView ( KateView *view )
+{
+ m_views.removeRef( view );
+}
+
+void KateFactory::registerRenderer ( KateRenderer *renderer )
+{
+ m_renderers.append( renderer );
+}
+
+void KateFactory::deregisterRenderer ( KateRenderer *renderer )
+{
+ m_renderers.removeRef( renderer );
+}
+
+KateJScript *KateFactory::jscript ()
+{
+#ifndef Q_WS_WIN //todo
+ if (m_jscript)
+ return m_jscript;
+
+ return m_jscript = new KateJScript ();
+#else
+ return 0;
+#endif
+}
+
+
+KateIndentScript KateFactory::indentScript (const QString &scriptname)
+{
+ KateIndentScript result;
+ for(uint i=0;i<m_indentScriptManagers.count();i++)
+ {
+ result=m_indentScriptManagers.at(i)->script(scriptname);
+ if (!result.isNull()) return result;
+ }
+ return result;
+}
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katefactory.h b/kate/part/katefactory.h
new file mode 100644
index 000000000..5e3986161
--- /dev/null
+++ b/kate/part/katefactory.h
@@ -0,0 +1,312 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2001-2004 Christoph Cullmann <cullmann@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KATE_FACTORY_H__
+#define __KATE_FACTORY_H__
+
+#include "katejscript.h"
+#include <kparts/factory.h>
+
+#include <ktrader.h>
+#include <kinstance.h>
+#include <kaboutdata.h>
+
+// katepart version must be a string in double quotes, format: "x.x"
+#define KATEPART_VERSION "2.5"
+
+class KateCmd;
+class KateFileTypeManager;
+class KateSchemaManager;
+class KateDocumentConfig;
+class KateViewConfig;
+class KateRendererConfig;
+class KateDocument;
+class KateRenderer;
+class KateView;
+class KateJScript;
+class KateJScriptManager;
+class KateIndentScriptManagerAbstract;
+class KDirWatch;
+class KVMAllocator;
+
+namespace Kate {
+ class Command;
+}
+
+
+class KateFactory
+{
+ private:
+ /**
+ * Default constructor, private, as singleton
+ */
+ KateFactory ();
+
+ public:
+ /**
+ * Destructor
+ */
+ ~KateFactory ();
+
+ /**
+ * singleton accessor
+ * @return instance of the factory
+ */
+ static KateFactory *self ();
+
+ /**
+ * reimplemented create object method
+ * @param parentWidget parent widget
+ * @param widgetName widget name
+ * @param parent QObject parent
+ * @param name object name
+ * @param classname class parent
+ * @param args additional arguments
+ * @return constructed part object
+ */
+ KParts::Part *createPartObject ( QWidget *parentWidget, const char *widgetName,
+ QObject *parent, const char *name, const char *classname,
+ const QStringList &args );
+
+ /**
+ * public accessor to the instance
+ * @return instance
+ */
+ inline KInstance *instance () { return &m_instance; };
+
+ /**
+ * register document at the factory
+ * this allows us to loop over all docs for example on config changes
+ * @param doc document to register
+ */
+ void registerDocument ( KateDocument *doc );
+
+ /**
+ * unregister document at the factory
+ * @param doc document to register
+ */
+ void deregisterDocument ( KateDocument *doc );
+
+ /**
+ * register view at the factory
+ * this allows us to loop over all views for example on config changes
+ * @param view view to register
+ */
+ void registerView ( KateView *view );
+
+ /**
+ * unregister view at the factory
+ * @param view view to unregister
+ */
+ void deregisterView ( KateView *view );
+
+ /**
+ * register renderer at the factory
+ * this allows us to loop over all views for example on config changes
+ * @param renderer renderer to register
+ */
+ void registerRenderer ( KateRenderer *renderer );
+
+ /**
+ * unregister renderer at the factory
+ * @param renderer renderer to unregister
+ */
+ void deregisterRenderer ( KateRenderer *renderer );
+
+ /**
+ * return a list of all registered docs
+ * @return all known documents
+ */
+ inline QPtrList<KateDocument> *documents () { return &m_documents; };
+
+ /**
+ * return a list of all registered views
+ * @return all known views
+ */
+ inline QPtrList<KateView> *views () { return &m_views; };
+
+ /**
+ * return a list of all registered renderers
+ * @return all known renderers
+ */
+ inline QPtrList<KateRenderer> *renderers () { return &m_renderers; };
+
+ /**
+ * on start detected plugins
+ * @return list of all at launch detected ktexteditor::plugins
+ */
+ inline const KTrader::OfferList &plugins () { return m_plugins; };
+
+ /**
+ * global dirwatch
+ * @return dirwatch instance
+ */
+ inline KDirWatch *dirWatch () { return m_dirWatch; };
+
+ /**
+ * global filetype manager
+ * used to manage the file types centrally
+ * @return filetype manager
+ */
+ inline KateFileTypeManager *fileTypeManager () { return m_fileTypeManager; };
+
+ /**
+ * manager for the katepart schemas
+ * @return schema manager
+ */
+ inline KateSchemaManager *schemaManager () { return m_schemaManager; };
+
+ /**
+ * fallback document config
+ * @return default config for all documents
+ */
+ inline KateDocumentConfig *documentConfig () { return m_documentConfig; }
+
+ /**
+ * fallback view config
+ * @return default config for all views
+ */
+ inline KateViewConfig *viewConfig () { return m_viewConfig; }
+
+ /**
+ * fallback renderer config
+ * @return default config for all renderers
+ */
+ inline KateRendererConfig *rendererConfig () { return m_rendererConfig; }
+
+ /**
+ * Global allocator for swapping
+ * @return allocator
+ */
+ inline KVMAllocator *vm () { return m_vm; }
+
+ /**
+ * global interpreter, for nice js stuff
+ */
+ KateJScript *jscript ();
+
+ /**
+ * Global javascript collection
+ */
+ KateJScriptManager *jscriptManager () { return m_jscriptManager; }
+
+
+ /**
+ * looks up a script given by name. If there are more than
+ * one matching, the first found will be taken
+ */
+ KateIndentScript indentScript (const QString &scriptname);
+
+ private:
+ /**
+ * instance of this factory
+ */
+ static KateFactory *s_self;
+
+ /**
+ * about data (authors and more)
+ */
+ KAboutData m_aboutData;
+
+ /**
+ * our kinstance
+ */
+ KInstance m_instance;
+
+ /**
+ * registered docs
+ */
+ QPtrList<KateDocument> m_documents;
+
+ /**
+ * registered views
+ */
+ QPtrList<KateView> m_views;
+
+ /**
+ * registered renderers
+ */
+ QPtrList<KateRenderer> m_renderers;
+
+ /**
+ * global dirwatch object
+ */
+ KDirWatch *m_dirWatch;
+
+ /**
+ * filetype manager
+ */
+ KateFileTypeManager *m_fileTypeManager;
+
+ /**
+ * schema manager
+ */
+ KateSchemaManager *m_schemaManager;
+
+ /**
+ * at start found plugins
+ */
+ KTrader::OfferList m_plugins;
+
+ /**
+ * fallback document config
+ */
+ KateDocumentConfig *m_documentConfig;
+
+ /**
+ * fallback view config
+ */
+ KateViewConfig *m_viewConfig;
+
+ /**
+ * fallback renderer config
+ */
+ KateRendererConfig *m_rendererConfig;
+
+ /**
+ * vm allocator
+ */
+ KVMAllocator *m_vm;
+
+ /**
+ * internal commands
+ */
+ QValueList<Kate::Command *> m_cmds;
+
+ /**
+ * js interpreter
+ */
+ KateJScript *m_jscript;
+
+
+ /**
+ * js script manager
+ */
+ KateJScriptManager *m_jscriptManager;
+
+
+ /**
+ * manager for js based indenters
+ */
+ QPtrList<KateIndentScriptManagerAbstract> m_indentScriptManagers;
+
+};
+
+#endif
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katefiletype.cpp b/kate/part/katefiletype.cpp
new file mode 100644
index 000000000..ea3a487cb
--- /dev/null
+++ b/kate/part/katefiletype.cpp
@@ -0,0 +1,596 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2001-2003 Christoph Cullmann <cullmann@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+//BEGIN Includes
+#include "katefiletype.h"
+#include "katefiletype.moc"
+
+#include "katedocument.h"
+#include "kateconfig.h"
+#include "kateview.h"
+#include "katefactory.h"
+
+#include <kconfig.h>
+#include <kmimemagic.h>
+#include <kmimetype.h>
+#include <kmimetypechooser.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <knuminput.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+
+#include <qregexp.h>
+#include <qcheckbox.h>
+#include <qcombobox.h>
+#include <qgroupbox.h>
+#include <qhbox.h>
+#include <qheader.h>
+#include <qhgroupbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qlineedit.h>
+#include <qpushbutton.h>
+#include <qtoolbutton.h>
+#include <qvbox.h>
+#include <qvgroupbox.h>
+#include <qwhatsthis.h>
+#include <qwidgetstack.h>
+
+#define KATE_FT_HOWMANY 1024
+//END Includes
+
+//BEGIN KateFileTypeManager
+KateFileTypeManager::KateFileTypeManager ()
+{
+ m_types.setAutoDelete (true);
+
+ update ();
+}
+
+KateFileTypeManager::~KateFileTypeManager ()
+{
+}
+
+//
+// read the types from config file and update the internal list
+//
+void KateFileTypeManager::update ()
+{
+ KConfig config ("katefiletyperc", false, false);
+
+ QStringList g (config.groupList());
+ g.sort ();
+
+ m_types.clear ();
+ for (uint z=0; z < g.count(); z++)
+ {
+ config.setGroup (g[z]);
+
+ KateFileType *type = new KateFileType ();
+
+ type->number = z;
+ type->name = g[z];
+ type->section = config.readEntry ("Section");
+ type->wildcards = config.readListEntry ("Wildcards", ';');
+ type->mimetypes = config.readListEntry ("Mimetypes", ';');
+ type->priority = config.readNumEntry ("Priority");
+ type->varLine = config.readEntry ("Variables");
+
+ m_types.append (type);
+ }
+}
+
+//
+// save the given list to config file + update
+//
+void KateFileTypeManager::save (QPtrList<KateFileType> *v)
+{
+ KConfig config ("katefiletyperc", false, false);
+
+ QStringList newg;
+ for (uint z=0; z < v->count(); z++)
+ {
+ config.setGroup (v->at(z)->name);
+
+ config.writeEntry ("Section", v->at(z)->section);
+ config.writeEntry ("Wildcards", v->at(z)->wildcards, ';');
+ config.writeEntry ("Mimetypes", v->at(z)->mimetypes, ';');
+ config.writeEntry ("Priority", v->at(z)->priority);
+
+ QString varLine = v->at(z)->varLine;
+ if (QRegExp("kate:(.*)").search(varLine) < 0)
+ varLine.prepend ("kate: ");
+
+ config.writeEntry ("Variables", varLine);
+
+ newg << v->at(z)->name;
+ }
+
+ QStringList g (config.groupList());
+
+ for (uint z=0; z < g.count(); z++)
+ {
+ if (newg.findIndex (g[z]) == -1)
+ config.deleteGroup (g[z]);
+ }
+
+ config.sync ();
+
+ update ();
+}
+
+int KateFileTypeManager::fileType (KateDocument *doc)
+{
+ kdDebug(13020)<<k_funcinfo<<endl;
+ if (!doc)
+ return -1;
+
+ if (m_types.isEmpty())
+ return -1;
+
+ QString fileName = doc->url().prettyURL();
+ int length = doc->url().prettyURL().length();
+
+ int result;
+
+ // Try wildcards
+ if ( ! fileName.isEmpty() )
+ {
+ static QStringList commonSuffixes = QStringList::split (";", ".orig;.new;~;.bak;.BAK");
+
+ if ((result = wildcardsFind(fileName)) != -1)
+ return result;
+
+ QString backupSuffix = KateDocumentConfig::global()->backupSuffix();
+ if (fileName.endsWith(backupSuffix)) {
+ if ((result = wildcardsFind(fileName.left(length - backupSuffix.length()))) != -1)
+ return result;
+ }
+
+ for (QStringList::Iterator it = commonSuffixes.begin(); it != commonSuffixes.end(); ++it) {
+ if (*it != backupSuffix && fileName.endsWith(*it)) {
+ if ((result = wildcardsFind(fileName.left(length - (*it).length()))) != -1)
+ return result;
+ }
+ }
+ }
+
+ // Even try the document name, if the URL is empty
+ // This is usefull if the document name is set for example by a plugin which
+ // created the document
+ else if ( (result = wildcardsFind(doc->docName())) != -1)
+ {
+ kdDebug(13020)<<"KateFiletype::filetype(): got type "<<result<<" using docName '"<<doc->docName()<<"'"<<endl;
+ return result;
+ }
+
+ // Try content-based mimetype
+ KMimeType::Ptr mt = doc->mimeTypeForContent();
+
+ QPtrList<KateFileType> types;
+
+ for (uint z=0; z < m_types.count(); z++)
+ {
+ if (m_types.at(z)->mimetypes.findIndex (mt->name()) > -1)
+ types.append (m_types.at(z));
+ }
+
+ if ( !types.isEmpty() )
+ {
+ int pri = -1;
+ int hl = -1;
+
+ for (KateFileType *type = types.first(); type != 0L; type = types.next())
+ {
+ if (type->priority > pri)
+ {
+ pri = type->priority;
+ hl = type->number;
+ }
+ }
+
+ return hl;
+ }
+
+
+ return -1;
+}
+
+int KateFileTypeManager::wildcardsFind (const QString &fileName)
+{
+ QPtrList<KateFileType> types;
+
+ for (uint z=0; z < m_types.count(); z++)
+ {
+ for( QStringList::Iterator it = m_types.at(z)->wildcards.begin(); it != m_types.at(z)->wildcards.end(); ++it )
+ {
+ // anders: we need to be sure to match the end of string, as eg a css file
+ // would otherwise end up with the c hl
+ QRegExp re(*it, true, true);
+ if ( ( re.search( fileName ) > -1 ) && ( re.matchedLength() == (int)fileName.length() ) )
+ types.append (m_types.at(z));
+ }
+ }
+
+ if ( !types.isEmpty() )
+ {
+ int pri = -1;
+ int hl = -1;
+
+ for (KateFileType *type = types.first(); type != 0L; type = types.next())
+ {
+ if (type->priority > pri)
+ {
+ pri = type->priority;
+ hl = type->number;
+ }
+ }
+
+ return hl;
+ }
+
+ return -1;
+}
+
+const KateFileType *KateFileTypeManager::fileType (uint number)
+{
+ if (number < m_types.count())
+ return m_types.at(number);
+
+ return 0;
+}
+//END KateFileTypeManager
+
+//BEGIN KateFileTypeConfigTab
+KateFileTypeConfigTab::KateFileTypeConfigTab( QWidget *parent )
+ : KateConfigPage( parent )
+{
+ m_types.setAutoDelete (true);
+ m_lastType = 0;
+
+ QVBoxLayout *layout = new QVBoxLayout(this, 0, KDialog::spacingHint() );
+
+ // hl chooser
+ QHBox *hbHl = new QHBox( this );
+ layout->add (hbHl);
+ hbHl->setSpacing( KDialog::spacingHint() );
+ QLabel *lHl = new QLabel( i18n("&Filetype:"), hbHl );
+ typeCombo = new QComboBox( false, hbHl );
+ lHl->setBuddy( typeCombo );
+ connect( typeCombo, SIGNAL(activated(int)),
+ this, SLOT(typeChanged(int)) );
+
+ QPushButton *btnnew = new QPushButton( i18n("&New"), hbHl );
+ connect( btnnew, SIGNAL(clicked()), this, SLOT(newType()) );
+
+ btndel = new QPushButton( i18n("&Delete"), hbHl );
+ connect( btndel, SIGNAL(clicked()), this, SLOT(deleteType()) );
+
+ gbProps = new QGroupBox( 2, Qt::Horizontal, i18n("Properties"), this );
+ layout->add (gbProps);
+
+ // file & mime types
+ QLabel *lname = new QLabel( i18n("N&ame:"), gbProps );
+ name = new QLineEdit( gbProps );
+ lname->setBuddy( name );
+
+ // file & mime types
+ QLabel *lsec = new QLabel( i18n("&Section:"), gbProps );
+ section = new QLineEdit( gbProps );
+ lsec->setBuddy( section );
+
+ // file & mime types
+ QLabel *lvar = new QLabel( i18n("&Variables:"), gbProps );
+ varLine = new QLineEdit( gbProps );
+ lvar->setBuddy( varLine );
+
+ // file & mime types
+ QLabel *lFileExts = new QLabel( i18n("File e&xtensions:"), gbProps );
+ wildcards = new QLineEdit( gbProps );
+ lFileExts->setBuddy( wildcards );
+
+ QLabel *lMimeTypes = new QLabel( i18n("MIME &types:"), gbProps);
+ QHBox *hbMT = new QHBox (gbProps);
+ mimetypes = new QLineEdit( hbMT );
+ lMimeTypes->setBuddy( mimetypes );
+
+ QToolButton *btnMTW = new QToolButton(hbMT);
+ btnMTW->setIconSet(QIconSet(SmallIcon("wizard")));
+ connect(btnMTW, SIGNAL(clicked()), this, SLOT(showMTDlg()));
+
+ QLabel *lprio = new QLabel( i18n("Prio&rity:"), gbProps);
+ priority = new KIntNumInput( gbProps );
+ lprio->setBuddy( priority );
+
+ layout->addStretch();
+
+ reload();
+
+ connect( name, SIGNAL( textChanged ( const QString & ) ), this, SLOT( slotChanged() ) );
+ connect( section, SIGNAL( textChanged ( const QString & ) ), this, SLOT( slotChanged() ) );
+ connect( varLine, SIGNAL( textChanged ( const QString & ) ), this, SLOT( slotChanged() ) );
+ connect( wildcards, SIGNAL( textChanged ( const QString & ) ), this, SLOT( slotChanged() ) );
+ connect( mimetypes, SIGNAL( textChanged ( const QString & ) ), this, SLOT( slotChanged() ) );
+ connect( priority, SIGNAL( valueChanged ( int ) ), this, SLOT( slotChanged() ) );
+
+ QWhatsThis::add( btnnew, i18n("Create a new file type.") );
+ QWhatsThis::add( btndel, i18n("Delete the current file type.") );
+ QWhatsThis::add( name, i18n(
+ "The name of the filetype will be the text of the corresponding menu item.") );
+ QWhatsThis::add( section, i18n(
+ "The section name is used to organize the file types in menus.") );
+ QWhatsThis::add( varLine, i18n(
+ "<p>This string allows you to configure Kate's settings for the files "
+ "selected by this mimetype using Kate variables. You can set almost any "
+ "configuration option, such as highlight, indent-mode, encoding, etc.</p>"
+ "<p>For a full list of known variables, see the manual.</p>") );
+ QWhatsThis::add( wildcards, i18n(
+ "The wildcards mask allows you to select files by filename. A typical "
+ "mask uses an asterisk and the file extension, for example "
+ "<code>*.txt; *.text</code>. The string is a semicolon-separated list "
+ "of masks.") );
+ QWhatsThis::add( mimetypes, i18n(
+ "The mime type mask allows you to select files by mimetype. The string is "
+ "a semicolon-separated list of mimetypes, for example "
+ "<code>text/plain; text/english</code>.") );
+ QWhatsThis::add( btnMTW, i18n(
+ "Displays a wizard that helps you easily select mimetypes.") );
+ QWhatsThis::add( priority, i18n(
+ "Sets a priority for this file type. If more than one file type selects the same "
+ "file, the one with the highest priority will be used." ) );
+}
+
+void KateFileTypeConfigTab::apply()
+{
+ if (!changed())
+ return;
+
+ save ();
+
+ KateFactory::self()->fileTypeManager()->save(&m_types);
+}
+
+void KateFileTypeConfigTab::reload()
+{
+ m_types.clear();
+ for (uint z=0; z < KateFactory::self()->fileTypeManager()->list()->count(); z++)
+ {
+ KateFileType *type = new KateFileType ();
+
+ *type = *KateFactory::self()->fileTypeManager()->list()->at(z);
+
+ m_types.append (type);
+ }
+
+ update ();
+}
+
+void KateFileTypeConfigTab::reset()
+{
+ reload ();
+}
+
+void KateFileTypeConfigTab::defaults()
+{
+ reload ();
+}
+
+void KateFileTypeConfigTab::update ()
+{
+ m_lastType = 0;
+
+ typeCombo->clear ();
+
+ for( uint i = 0; i < m_types.count(); i++) {
+ if (m_types.at(i)->section.length() > 0)
+ typeCombo->insertItem(m_types.at(i)->section + QString ("/") + m_types.at(i)->name);
+ else
+ typeCombo->insertItem(m_types.at(i)->name);
+ }
+
+ typeCombo->setCurrentItem (0);
+
+ typeChanged (0);
+
+ typeCombo->setEnabled (typeCombo->count() > 0);
+}
+
+void KateFileTypeConfigTab::deleteType ()
+{
+ int type = typeCombo->currentItem ();
+
+ if ((type > -1) && ((uint)type < m_types.count()))
+ {
+ m_types.remove (type);
+ update ();
+ }
+}
+
+void KateFileTypeConfigTab::newType ()
+{
+ QString newN = i18n("New Filetype");
+
+ for( uint i = 0; i < m_types.count(); i++) {
+ if (m_types.at(i)->name == newN)
+ {
+ typeCombo->setCurrentItem (i);
+ typeChanged (i);
+ return;
+ }
+ }
+
+ KateFileType *newT = new KateFileType ();
+ newT->priority = 0;
+ newT->name = newN;
+
+ m_types.prepend (newT);
+
+ update ();
+}
+
+void KateFileTypeConfigTab::save ()
+{
+ if (m_lastType)
+ {
+ m_lastType->name = name->text ();
+ m_lastType->section = section->text ();
+ m_lastType->varLine = varLine->text ();
+ m_lastType->wildcards = QStringList::split (";", wildcards->text ());
+ m_lastType->mimetypes = QStringList::split (";", mimetypes->text ());
+ m_lastType->priority = priority->value();
+ }
+}
+
+void KateFileTypeConfigTab::typeChanged (int type)
+{
+ save ();
+
+ KateFileType *t = 0;
+
+ if ((type > -1) && ((uint)type < m_types.count()))
+ t = m_types.at(type);
+
+ if (t)
+ {
+ gbProps->setTitle (i18n("Properties of %1").arg (typeCombo->currentText()));
+
+ gbProps->setEnabled (true);
+ btndel->setEnabled (true);
+
+ name->setText(t->name);
+ section->setText(t->section);
+ varLine->setText(t->varLine);
+ wildcards->setText(t->wildcards.join (";"));
+ mimetypes->setText(t->mimetypes.join (";"));
+ priority->setValue(t->priority);
+ }
+ else
+ {
+ gbProps->setTitle (i18n("Properties"));
+
+ gbProps->setEnabled (false);
+ btndel->setEnabled (false);
+
+ name->clear();
+ section->clear();
+ varLine->clear();
+ wildcards->clear();
+ mimetypes->clear();
+ priority->setValue(0);
+ }
+
+ m_lastType = t;
+}
+
+void KateFileTypeConfigTab::showMTDlg()
+{
+
+ QString text = i18n("Select the MimeTypes you want for this file type.\nPlease note that this will automatically edit the associated file extensions as well.");
+ QStringList list = QStringList::split( QRegExp("\\s*;\\s*"), mimetypes->text() );
+ KMimeTypeChooserDialog d( i18n("Select Mime Types"), text, list, "text", this );
+ if ( d.exec() == KDialogBase::Accepted ) {
+ // do some checking, warn user if mime types or patterns are removed.
+ // if the lists are empty, and the fields not, warn.
+ wildcards->setText( d.chooser()->patterns().join(";") );
+ mimetypes->setText( d.chooser()->mimeTypes().join(";") );
+ }
+}
+//END KateFileTypeConfigTab
+
+//BEGIN KateViewFileTypeAction
+void KateViewFileTypeAction::init()
+{
+ m_doc = 0;
+ subMenus.setAutoDelete( true );
+
+ popupMenu()->insertItem ( i18n("None"), this, SLOT(setType(int)), 0, 0);
+
+ connect(popupMenu(),SIGNAL(aboutToShow()),this,SLOT(slotAboutToShow()));
+}
+
+void KateViewFileTypeAction::updateMenu (Kate::Document *doc)
+{
+ m_doc = (KateDocument *)doc;
+}
+
+void KateViewFileTypeAction::slotAboutToShow()
+{
+ KateDocument *doc=m_doc;
+ int count = KateFactory::self()->fileTypeManager()->list()->count();
+
+ for (int z=0; z<count; z++)
+ {
+ QString hlName = KateFactory::self()->fileTypeManager()->list()->at(z)->name;
+ QString hlSection = KateFactory::self()->fileTypeManager()->list()->at(z)->section;
+
+ if ( !hlSection.isEmpty() && (names.contains(hlName) < 1) )
+ {
+ if (subMenusName.contains(hlSection) < 1)
+ {
+ subMenusName << hlSection;
+ QPopupMenu *menu = new QPopupMenu ();
+ subMenus.append(menu);
+ popupMenu()->insertItem (hlSection, menu);
+ }
+
+ int m = subMenusName.findIndex (hlSection);
+ names << hlName;
+ subMenus.at(m)->insertItem ( hlName, this, SLOT(setType(int)), 0, z+1);
+ }
+ else if (names.contains(hlName) < 1)
+ {
+ names << hlName;
+ popupMenu()->insertItem ( hlName, this, SLOT(setType(int)), 0, z+1);
+ }
+ }
+
+ if (!doc) return;
+
+ for (uint i=0;i<subMenus.count();i++)
+ {
+ for (uint i2=0;i2<subMenus.at(i)->count();i2++)
+ subMenus.at(i)->setItemChecked(subMenus.at(i)->idAt(i2),false);
+ }
+ popupMenu()->setItemChecked (0, false);
+
+ if (doc->fileType() == -1)
+ popupMenu()->setItemChecked (0, true);
+ else
+ {
+ const KateFileType *t = 0;
+ if ((t = KateFactory::self()->fileTypeManager()->fileType (doc->fileType())))
+ {
+ int i = subMenusName.findIndex (t->section);
+ if (i >= 0 && subMenus.at(i))
+ subMenus.at(i)->setItemChecked (doc->fileType()+1, true);
+ else
+ popupMenu()->setItemChecked (0, true);
+ }
+ }
+}
+
+void KateViewFileTypeAction::setType (int mode)
+{
+ KateDocument *doc=m_doc;
+
+ if (doc)
+ doc->updateFileType(mode-1, true);
+}
+//END KateViewFileTypeAction
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katefiletype.h b/kate/part/katefiletype.h
new file mode 100644
index 000000000..cd343019c
--- /dev/null
+++ b/kate/part/katefiletype.h
@@ -0,0 +1,142 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2001-2003 Christoph Cullmann <cullmann@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __kate_filetype_h__
+#define __kate_filetype_h__
+
+#include <qstringlist.h>
+#include <qptrlist.h>
+#include <qpopupmenu.h> // for QPtrList<QPopupMenu>, compile with gcc 3.4
+#include <qguardedptr.h>
+
+#include "katedialogs.h"
+
+class KateDocument;
+
+class KateFileType
+{
+ public:
+ int number;
+ QString name;
+ QString section;
+ QStringList wildcards;
+ QStringList mimetypes;
+ int priority;
+ QString varLine;
+};
+
+class KateFileTypeManager
+{
+ public:
+ KateFileTypeManager ();
+ ~KateFileTypeManager ();
+
+ /**
+ * File Type Config changed, update all docs (which will take care of views/renderers)
+ */
+ void update ();
+
+ void save (QPtrList<KateFileType> *v);
+
+ /**
+ * get the right fileType for the given document
+ * -1 if none found !
+ */
+ int fileType (KateDocument *doc);
+
+ /**
+ * Don't store the pointer somewhere longer times, won't be valid after the next update()
+ */
+ const KateFileType *fileType (uint number);
+
+ /**
+ * Don't modify
+ */
+ QPtrList<KateFileType> *list () { return &m_types; }
+
+ private:
+ int wildcardsFind (const QString &fileName);
+
+ private:
+ QPtrList<KateFileType> m_types;
+};
+
+class KateFileTypeConfigTab : public KateConfigPage
+{
+ Q_OBJECT
+
+ public:
+ KateFileTypeConfigTab( QWidget *parent );
+
+ public slots:
+ void apply();
+ void reload();
+ void reset();
+ void defaults();
+
+ private slots:
+ void update ();
+ void deleteType ();
+ void newType ();
+ void typeChanged (int type);
+ void showMTDlg();
+ void save ();
+
+ private:
+ class QGroupBox *gbProps;
+ class QPushButton *btndel;
+ class QComboBox *typeCombo;
+ class QLineEdit *wildcards;
+ class QLineEdit *mimetypes;
+ class KIntNumInput *priority;
+ class QLineEdit *name;
+ class QLineEdit *section;
+ class QLineEdit *varLine;
+
+ QPtrList<KateFileType> m_types;
+ KateFileType *m_lastType;
+};
+
+class KateViewFileTypeAction : public Kate::ActionMenu
+{
+ Q_OBJECT
+
+ public:
+ KateViewFileTypeAction(const QString& text, QObject* parent = 0, const char* name = 0)
+ : Kate::ActionMenu(text, parent, name) { init(); };
+
+ ~KateViewFileTypeAction(){;};
+
+ void updateMenu (Kate::Document *doc);
+
+ private:
+ void init();
+
+ QGuardedPtr<KateDocument> m_doc;
+ QStringList subMenusName;
+ QStringList names;
+ QPtrList<QPopupMenu> subMenus;
+
+ public slots:
+ void slotAboutToShow();
+
+ private slots:
+ void setType (int mode);
+};
+
+#endif
diff --git a/kate/part/katefont.cpp b/kate/part/katefont.cpp
new file mode 100644
index 000000000..efab0268c
--- /dev/null
+++ b/kate/part/katefont.cpp
@@ -0,0 +1,127 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2002 Christian Couder <christian@kdevelop.org>
+ Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
+ Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "katefont.h"
+
+#include <kglobalsettings.h>
+
+#include <qfontinfo.h>
+
+//
+// KateFontMetrics implementation
+//
+
+KateFontMetrics::KateFontMetrics(const QFont& f) : QFontMetrics(f)
+{
+ for (int i=0; i<256; i++) warray[i]=0;
+}
+
+KateFontMetrics::~KateFontMetrics()
+{
+ for (int i=0; i<256; i++)
+ delete[] warray[i];
+}
+
+short * KateFontMetrics::createRow (short *wa, uchar row)
+{
+ wa=warray[row]=new short[256];
+
+ for (int i=0; i<256; i++) wa[i]=-1;
+
+ return wa;
+}
+
+int KateFontMetrics::width(QChar c)
+{
+ uchar cell=c.cell();
+ uchar row=c.row();
+ short *wa=warray[row];
+
+ if (!wa)
+ wa = createRow (wa, row);
+
+ if (wa[cell]<0) wa[cell]=(short) QFontMetrics::width(c);
+
+ return (int)wa[cell];
+}
+
+//
+// KateFontStruct implementation
+//
+
+KateFontStruct::KateFontStruct()
+: myFont(KGlobalSettings::fixedFont()),
+ myFontBold(KGlobalSettings::fixedFont()),
+ myFontItalic(KGlobalSettings::fixedFont()),
+ myFontBI(KGlobalSettings::fixedFont()),
+ myFontMetrics(myFont),
+ myFontMetricsBold(myFontBold),
+ myFontMetricsItalic(myFontItalic),
+ myFontMetricsBI(myFontBI),
+ m_fixedPitch (false)
+{
+ updateFontData ();
+}
+
+KateFontStruct::~KateFontStruct()
+{
+}
+
+void KateFontStruct::updateFontData ()
+{
+ int maxAscent = myFontMetrics.ascent();
+ int maxDescent = myFontMetrics.descent();
+
+ fontHeight = maxAscent + maxDescent + 1;
+ fontAscent = maxAscent;
+
+ m_fixedPitch = QFontInfo( myFont ).fixedPitch();
+}
+
+void KateFontStruct::setFont (const QFont & font)
+{
+ QFontMetrics testFM (font);
+
+ // no valid font tried
+ if ((testFM.ascent() + testFM.descent() + 1) < 1)
+ return;
+
+ myFont = font;
+
+ myFontBold = QFont (font);
+ myFontBold.setBold (true);
+
+ myFontItalic = QFont (font);
+ myFontItalic.setItalic (true);
+
+ myFontBI = QFont (font);
+ myFontBI.setBold (true);
+ myFontBI.setItalic (true);
+
+ myFontMetrics = KateFontMetrics (myFont);
+ myFontMetricsBold = KateFontMetrics (myFontBold);
+ myFontMetricsItalic = KateFontMetrics (myFontItalic);
+ myFontMetricsBI = KateFontMetrics (myFontBI);
+
+ updateFontData ();
+}
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katefont.h b/kate/part/katefont.h
new file mode 100644
index 000000000..02c2106c4
--- /dev/null
+++ b/kate/part/katefont.h
@@ -0,0 +1,114 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2002 Christian Couder <christian@kdevelop.org>
+ Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
+ Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __kate_font_h__
+#define __kate_font_h__
+
+#include <qfont.h>
+#include <qfontmetrics.h>
+
+//
+// KateFontMetrics implementation
+//
+
+class KateFontMetrics : public QFontMetrics
+{
+ public:
+ KateFontMetrics(const QFont& f);
+ ~KateFontMetrics();
+
+ int width(QChar c);
+
+ int width(QString s) { return QFontMetrics::width(s); }
+
+ private:
+ short *createRow (short *wa, uchar row);
+
+ private:
+ short *warray[256];
+};
+
+//
+// KateFontStruct definition
+//
+
+class KateFontStruct
+{
+ public:
+ KateFontStruct();
+ ~KateFontStruct();
+
+ void setFont(const QFont & font);
+
+ private:
+ void updateFontData ();
+
+ public:
+ inline int width (const QString& text, int col, bool bold, bool italic, int tabWidth)
+ {
+ if (text[col] == QChar('\t'))
+ return tabWidth * myFontMetrics.width(' ');
+
+ return (bold) ?
+ ( (italic) ?
+ myFontMetricsBI.charWidth(text, col) :
+ myFontMetricsBold.charWidth(text, col) ) :
+ ( (italic) ?
+ myFontMetricsItalic.charWidth(text, col) :
+ myFontMetrics.charWidth(text, col) );
+ }
+
+ inline int width (const QChar& c, bool bold, bool italic, int tabWidth)
+ {
+ if (c == QChar('\t'))
+ return tabWidth * myFontMetrics.width(' ');
+
+ return (bold) ?
+ ( (italic) ?
+ myFontMetricsBI.width(c) :
+ myFontMetricsBold.width(c) ) :
+ ( (italic) ?
+ myFontMetricsItalic.width(c) :
+ myFontMetrics.width(c) );
+ }
+
+ inline const QFont& font(bool bold, bool italic) const
+ {
+ return (bold) ?
+ ( (italic) ? myFontBI : myFontBold ) :
+ ( (italic) ? myFontItalic : myFont );
+ }
+
+ inline bool fixedPitch() const { return m_fixedPitch; }
+
+ public:
+ QFont myFont, myFontBold, myFontItalic, myFontBI;
+
+ KateFontMetrics myFontMetrics, myFontMetricsBold, myFontMetricsItalic, myFontMetricsBI;
+
+ int fontHeight;
+ int fontAscent;
+
+ private:
+ bool m_fixedPitch;
+};
+
+#endif
diff --git a/kate/part/katehighlight.cpp b/kate/part/katehighlight.cpp
new file mode 100644
index 000000000..84bfc67fd
--- /dev/null
+++ b/kate/part/katehighlight.cpp
@@ -0,0 +1,3473 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2003, 2004 Anders Lund <anders@alweb.dk>
+ Copyright (C) 2003 Hamish Rodda <rodda@kde.org>
+ Copyright (C) 2001,2002 Joseph Wenninger <jowenn@kde.org>
+ Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+//BEGIN INCLUDES
+#include "katehighlight.h"
+#include "katehighlight.moc"
+
+#include "katetextline.h"
+#include "katedocument.h"
+#include "katesyntaxdocument.h"
+#include "katerenderer.h"
+#include "katefactory.h"
+#include "kateschema.h"
+#include "kateconfig.h"
+
+#include <kconfig.h>
+#include <kglobal.h>
+#include <kinstance.h>
+#include <kmimetype.h>
+#include <klocale.h>
+#include <kregexp.h>
+#include <kpopupmenu.h>
+#include <kglobalsettings.h>
+#include <kdebug.h>
+#include <kstandarddirs.h>
+#include <kmessagebox.h>
+#include <kstaticdeleter.h>
+#include <kapplication.h>
+
+#include <qstringlist.h>
+#include <qtextstream.h>
+//END
+
+//BEGIN defines
+// same as in kmimemagic, no need to feed more data
+#define KATE_HL_HOWMANY 1024
+
+// min. x seconds between two dynamic contexts reset
+static const int KATE_DYNAMIC_CONTEXTS_RESET_DELAY = 30 * 1000;
+
+// x is a QString. if x is "true" or "1" this expression returns "true"
+#define IS_TRUE(x) x.lower() == QString("true") || x.toInt() == 1
+//END defines
+
+//BEGIN Prviate HL classes
+
+inline bool kateInsideString (const QString &str, QChar ch)
+{
+ const QChar *unicode = str.unicode();
+ const uint len = str.length();
+ for (uint i=0; i < len; i++)
+ if (unicode[i] == ch)
+ return true;
+
+ return false;
+}
+
+class KateHlItem
+{
+ public:
+ KateHlItem(int attribute, int context,signed char regionId, signed char regionId2);
+ virtual ~KateHlItem();
+
+ public:
+ // caller must keep in mind: LEN > 0 is a must !!!!!!!!!!!!!!!!!!!!!1
+ // Now, the function returns the offset detected, or 0 if no match is found.
+ // bool linestart isn't needed, this is equivalent to offset == 0.
+ virtual int checkHgl(const QString& text, int offset, int len) = 0;
+
+ virtual bool lineContinue(){return false;}
+
+ virtual QStringList *capturedTexts() {return 0;}
+ virtual KateHlItem *clone(const QStringList *) {return this;}
+
+ static void dynamicSubstitute(QString& str, const QStringList *args);
+
+ QMemArray<KateHlItem*> subItems;
+ int attr;
+ int ctx;
+ signed char region;
+ signed char region2;
+
+ bool lookAhead;
+
+ bool dynamic;
+ bool dynamicChild;
+ bool firstNonSpace;
+ bool onlyConsume;
+ int column;
+
+ // start enable flags, nicer than the virtual methodes
+ // saves function calls
+ bool alwaysStartEnable;
+ bool customStartEnable;
+};
+
+class KateHlContext
+{
+ public:
+ KateHlContext(const QString &_hlId, int attribute, int lineEndContext,int _lineBeginContext,
+ bool _fallthrough, int _fallthroughContext, bool _dynamic,bool _noIndentationBasedFolding);
+ virtual ~KateHlContext();
+ KateHlContext *clone(const QStringList *args);
+
+ QValueVector<KateHlItem*> items;
+ QString hlId; ///< A unique highlight identifier. Used to look up correct properties.
+ int attr;
+ int ctx;
+ int lineBeginContext;
+ /** @internal anders: possible escape if no rules matches.
+ false unless 'fallthrough="1|true"' (insensitive)
+ if true, go to ftcxt w/o eating of string.
+ ftctx is "fallthroughContext" in xml files, valid values are int or #pop[..]
+ see in KateHighlighting::doHighlight */
+ bool fallthrough;
+ int ftctx; // where to go after no rules matched
+
+ bool dynamic;
+ bool dynamicChild;
+ bool noIndentationBasedFolding;
+};
+
+class KateEmbeddedHlInfo
+{
+ public:
+ KateEmbeddedHlInfo() {loaded=false;context0=-1;}
+ KateEmbeddedHlInfo(bool l, int ctx0) {loaded=l;context0=ctx0;}
+
+ public:
+ bool loaded;
+ int context0;
+};
+
+class KateHlIncludeRule
+{
+ public:
+ KateHlIncludeRule(int ctx_=0, uint pos_=0, const QString &incCtxN_="", bool incAttrib=false)
+ : ctx(ctx_)
+ , pos( pos_)
+ , incCtxN( incCtxN_ )
+ , includeAttrib( incAttrib )
+ {
+ incCtx=-1;
+ }
+ //KateHlIncludeRule(int ctx_, uint pos_, bool incAttrib) {ctx=ctx_;pos=pos_;incCtx=-1;incCtxN="";includeAttrib=incAttrib}
+
+ public:
+ int ctx;
+ uint pos;
+ int incCtx;
+ QString incCtxN;
+ bool includeAttrib;
+};
+
+class KateHlCharDetect : public KateHlItem
+{
+ public:
+ KateHlCharDetect(int attribute, int context,signed char regionId,signed char regionId2, QChar);
+
+ virtual int checkHgl(const QString& text, int offset, int len);
+ virtual KateHlItem *clone(const QStringList *args);
+
+ private:
+ QChar sChar;
+};
+
+class KateHl2CharDetect : public KateHlItem
+{
+ public:
+ KateHl2CharDetect(int attribute, int context, signed char regionId,signed char regionId2, QChar ch1, QChar ch2);
+ KateHl2CharDetect(int attribute, int context,signed char regionId,signed char regionId2, const QChar *ch);
+
+ virtual int checkHgl(const QString& text, int offset, int len);
+ virtual KateHlItem *clone(const QStringList *args);
+
+ private:
+ QChar sChar1;
+ QChar sChar2;
+};
+
+class KateHlStringDetect : public KateHlItem
+{
+ public:
+ KateHlStringDetect(int attribute, int context, signed char regionId,signed char regionId2, const QString &, bool inSensitive=false);
+
+ virtual int checkHgl(const QString& text, int offset, int len);
+ virtual KateHlItem *clone(const QStringList *args);
+
+ private:
+ const QString str;
+ const int strLen;
+ const bool _inSensitive;
+};
+
+class KateHlRangeDetect : public KateHlItem
+{
+ public:
+ KateHlRangeDetect(int attribute, int context, signed char regionId,signed char regionId2, QChar ch1, QChar ch2);
+
+ virtual int checkHgl(const QString& text, int offset, int len);
+
+ private:
+ QChar sChar1;
+ QChar sChar2;
+};
+
+class KateHlKeyword : public KateHlItem
+{
+ public:
+ KateHlKeyword(int attribute, int context,signed char regionId,signed char regionId2, bool insensitive, const QString& delims);
+ virtual ~KateHlKeyword ();
+
+ void addList(const QStringList &);
+ virtual int checkHgl(const QString& text, int offset, int len);
+
+ private:
+ QMemArray< QDict<bool>* > dict;
+ bool _insensitive;
+ const QString& deliminators;
+ int minLen;
+ int maxLen;
+};
+
+class KateHlInt : public KateHlItem
+{
+ public:
+ KateHlInt(int attribute, int context, signed char regionId,signed char regionId2);
+
+ virtual int checkHgl(const QString& text, int offset, int len);
+};
+
+class KateHlFloat : public KateHlItem
+{
+ public:
+ KateHlFloat(int attribute, int context, signed char regionId,signed char regionId2);
+ virtual ~KateHlFloat () {}
+
+ virtual int checkHgl(const QString& text, int offset, int len);
+};
+
+class KateHlCFloat : public KateHlFloat
+{
+ public:
+ KateHlCFloat(int attribute, int context, signed char regionId,signed char regionId2);
+
+ virtual int checkHgl(const QString& text, int offset, int len);
+ int checkIntHgl(const QString& text, int offset, int len);
+};
+
+class KateHlCOct : public KateHlItem
+{
+ public:
+ KateHlCOct(int attribute, int context, signed char regionId,signed char regionId2);
+
+ virtual int checkHgl(const QString& text, int offset, int len);
+};
+
+class KateHlCHex : public KateHlItem
+{
+ public:
+ KateHlCHex(int attribute, int context, signed char regionId,signed char regionId2);
+
+ virtual int checkHgl(const QString& text, int offset, int len);
+};
+
+class KateHlLineContinue : public KateHlItem
+{
+ public:
+ KateHlLineContinue(int attribute, int context, signed char regionId,signed char regionId2);
+
+ virtual bool endEnable(QChar c) {return c == '\0';}
+ virtual int checkHgl(const QString& text, int offset, int len);
+ virtual bool lineContinue(){return true;}
+};
+
+class KateHlCStringChar : public KateHlItem
+{
+ public:
+ KateHlCStringChar(int attribute, int context, signed char regionId,signed char regionId2);
+
+ virtual int checkHgl(const QString& text, int offset, int len);
+};
+
+class KateHlCChar : public KateHlItem
+{
+ public:
+ KateHlCChar(int attribute, int context,signed char regionId,signed char regionId2);
+
+ virtual int checkHgl(const QString& text, int offset, int len);
+};
+
+class KateHlAnyChar : public KateHlItem
+{
+ public:
+ KateHlAnyChar(int attribute, int context, signed char regionId,signed char regionId2, const QString& charList);
+
+ virtual int checkHgl(const QString& text, int offset, int len);
+
+ private:
+ const QString _charList;
+};
+
+class KateHlRegExpr : public KateHlItem
+{
+ public:
+ KateHlRegExpr(int attribute, int context,signed char regionId,signed char regionId2 ,QString expr, bool insensitive, bool minimal);
+ ~KateHlRegExpr() { delete Expr; };
+
+ virtual int checkHgl(const QString& text, int offset, int len);
+ virtual QStringList *capturedTexts();
+ virtual KateHlItem *clone(const QStringList *args);
+
+ private:
+ QRegExp *Expr;
+ bool handlesLinestart;
+ QString _regexp;
+ bool _insensitive;
+ bool _minimal;
+};
+
+class KateHlDetectSpaces : public KateHlItem
+{
+ public:
+ KateHlDetectSpaces (int attribute, int context,signed char regionId,signed char regionId2)
+ : KateHlItem(attribute,context,regionId,regionId2) {}
+
+ virtual int checkHgl(const QString& text, int offset, int len)
+ {
+ int len2 = offset + len;
+ while ((offset < len2) && text[offset].isSpace()) offset++;
+ return offset;
+ }
+};
+
+class KateHlDetectIdentifier : public KateHlItem
+{
+ public:
+ KateHlDetectIdentifier (int attribute, int context,signed char regionId,signed char regionId2)
+ : KateHlItem(attribute,context,regionId,regionId2) { alwaysStartEnable = false; }
+
+ virtual int checkHgl(const QString& text, int offset, int len)
+ {
+ // first char should be a letter or underscore
+ if ( text[offset].isLetter() || text[offset] == QChar ('_') )
+ {
+ // memorize length
+ int len2 = offset+len;
+
+ // one char seen
+ offset++;
+
+ // now loop for all other thingies
+ while (
+ (offset < len2)
+ && (text[offset].isLetterOrNumber() || (text[offset] == QChar ('_')))
+ )
+ offset++;
+
+ return offset;
+ }
+
+ return 0;
+ }
+};
+
+//END
+
+//BEGIN STATICS
+KateHlManager *KateHlManager::s_self = 0;
+
+static const bool trueBool = true;
+static const QString stdDeliminator = QString (" \t.():!+,-<=>%&*/;?[]^{|}~\\");
+//END
+
+//BEGIN NON MEMBER FUNCTIONS
+static KateHlItemData::ItemStyles getDefStyleNum(QString name)
+{
+ if (name=="dsNormal") return KateHlItemData::dsNormal;
+ else if (name=="dsKeyword") return KateHlItemData::dsKeyword;
+ else if (name=="dsDataType") return KateHlItemData::dsDataType;
+ else if (name=="dsDecVal") return KateHlItemData::dsDecVal;
+ else if (name=="dsBaseN") return KateHlItemData::dsBaseN;
+ else if (name=="dsFloat") return KateHlItemData::dsFloat;
+ else if (name=="dsChar") return KateHlItemData::dsChar;
+ else if (name=="dsString") return KateHlItemData::dsString;
+ else if (name=="dsComment") return KateHlItemData::dsComment;
+ else if (name=="dsOthers") return KateHlItemData::dsOthers;
+ else if (name=="dsAlert") return KateHlItemData::dsAlert;
+ else if (name=="dsFunction") return KateHlItemData::dsFunction;
+ else if (name=="dsRegionMarker") return KateHlItemData::dsRegionMarker;
+ else if (name=="dsError") return KateHlItemData::dsError;
+
+ return KateHlItemData::dsNormal;
+}
+//END
+
+//BEGIN KateHlItem
+KateHlItem::KateHlItem(int attribute, int context,signed char regionId,signed char regionId2)
+ : attr(attribute),
+ ctx(context),
+ region(regionId),
+ region2(regionId2),
+ lookAhead(false),
+ dynamic(false),
+ dynamicChild(false),
+ firstNonSpace(false),
+ onlyConsume(false),
+ column (-1),
+ alwaysStartEnable (true),
+ customStartEnable (false)
+{
+}
+
+KateHlItem::~KateHlItem()
+{
+ //kdDebug(13010)<<"In hlItem::~KateHlItem()"<<endl;
+ for (uint i=0; i < subItems.size(); i++)
+ delete subItems[i];
+}
+
+void KateHlItem::dynamicSubstitute(QString &str, const QStringList *args)
+{
+ for (uint i = 0; i < str.length() - 1; ++i)
+ {
+ if (str[i] == '%')
+ {
+ char c = str[i + 1].latin1();
+ if (c == '%')
+ str.replace(i, 1, "");
+ else if (c >= '0' && c <= '9')
+ {
+ if ((uint)(c - '0') < args->size())
+ {
+ str.replace(i, 2, (*args)[c - '0']);
+ i += ((*args)[c - '0']).length() - 1;
+ }
+ else
+ {
+ str.replace(i, 2, "");
+ --i;
+ }
+ }
+ }
+ }
+}
+//END
+
+//BEGIN KateHlCharDetect
+KateHlCharDetect::KateHlCharDetect(int attribute, int context, signed char regionId,signed char regionId2, QChar c)
+ : KateHlItem(attribute,context,regionId,regionId2)
+ , sChar(c)
+{
+}
+
+int KateHlCharDetect::checkHgl(const QString& text, int offset, int /*len*/)
+{
+ if (text[offset] == sChar)
+ return offset + 1;
+
+ return 0;
+}
+
+KateHlItem *KateHlCharDetect::clone(const QStringList *args)
+{
+ char c = sChar.latin1();
+
+ if (c < '0' || c > '9' || (unsigned)(c - '0') >= args->size())
+ return this;
+
+ KateHlCharDetect *ret = new KateHlCharDetect(attr, ctx, region, region2, (*args)[c - '0'][0]);
+ ret->dynamicChild = true;
+ return ret;
+}
+//END
+
+//BEGIN KateHl2CharDetect
+KateHl2CharDetect::KateHl2CharDetect(int attribute, int context, signed char regionId,signed char regionId2, QChar ch1, QChar ch2)
+ : KateHlItem(attribute,context,regionId,regionId2)
+ , sChar1 (ch1)
+ , sChar2 (ch2)
+{
+}
+
+int KateHl2CharDetect::checkHgl(const QString& text, int offset, int len)
+{
+ if ((len >= 2) && text[offset++] == sChar1 && text[offset++] == sChar2)
+ return offset;
+
+ return 0;
+}
+
+KateHlItem *KateHl2CharDetect::clone(const QStringList *args)
+{
+ char c1 = sChar1.latin1();
+ char c2 = sChar2.latin1();
+
+ if (c1 < '0' || c1 > '9' || (unsigned)(c1 - '0') >= args->size())
+ return this;
+
+ if (c2 < '0' || c2 > '9' || (unsigned)(c2 - '0') >= args->size())
+ return this;
+
+ KateHl2CharDetect *ret = new KateHl2CharDetect(attr, ctx, region, region2, (*args)[c1 - '0'][0], (*args)[c2 - '0'][0]);
+ ret->dynamicChild = true;
+ return ret;
+}
+//END
+
+//BEGIN KateHlStringDetect
+KateHlStringDetect::KateHlStringDetect(int attribute, int context, signed char regionId,signed char regionId2,const QString &s, bool inSensitive)
+ : KateHlItem(attribute, context,regionId,regionId2)
+ , str(inSensitive ? s.upper() : s)
+ , strLen (str.length())
+ , _inSensitive(inSensitive)
+{
+}
+
+int KateHlStringDetect::checkHgl(const QString& text, int offset, int len)
+{
+ if (len < strLen)
+ return 0;
+
+ if (_inSensitive)
+ {
+ for (int i=0; i < strLen; i++)
+ if (text[offset++].upper() != str[i])
+ return 0;
+
+ return offset;
+ }
+ else
+ {
+ for (int i=0; i < strLen; i++)
+ if (text[offset++] != str[i])
+ return 0;
+
+ return offset;
+ }
+
+ return 0;
+}
+
+KateHlItem *KateHlStringDetect::clone(const QStringList *args)
+{
+ QString newstr = str;
+
+ dynamicSubstitute(newstr, args);
+
+ if (newstr == str)
+ return this;
+
+ KateHlStringDetect *ret = new KateHlStringDetect(attr, ctx, region, region2, newstr, _inSensitive);
+ ret->dynamicChild = true;
+ return ret;
+}
+//END
+
+//BEGIN KateHlRangeDetect
+KateHlRangeDetect::KateHlRangeDetect(int attribute, int context, signed char regionId,signed char regionId2, QChar ch1, QChar ch2)
+ : KateHlItem(attribute,context,regionId,regionId2)
+ , sChar1 (ch1)
+ , sChar2 (ch2)
+{
+}
+
+int KateHlRangeDetect::checkHgl(const QString& text, int offset, int len)
+{
+ if (text[offset] == sChar1)
+ {
+ do
+ {
+ offset++;
+ len--;
+ if (len < 1) return 0;
+ }
+ while (text[offset] != sChar2);
+
+ return offset + 1;
+ }
+ return 0;
+}
+//END
+
+//BEGIN KateHlKeyword
+KateHlKeyword::KateHlKeyword (int attribute, int context, signed char regionId,signed char regionId2, bool insensitive, const QString& delims)
+ : KateHlItem(attribute,context,regionId,regionId2)
+ , _insensitive(insensitive)
+ , deliminators(delims)
+ , minLen (0xFFFFFF)
+ , maxLen (0)
+{
+ alwaysStartEnable = false;
+ customStartEnable = true;
+}
+
+KateHlKeyword::~KateHlKeyword ()
+{
+ for (uint i=0; i < dict.size(); ++i)
+ delete dict[i];
+}
+
+void KateHlKeyword::addList(const QStringList& list)
+{
+ for(uint i=0; i < list.count(); ++i)
+ {
+ int len = list[i].length();
+
+ if (minLen > len)
+ minLen = len;
+
+ if (maxLen < len)
+ maxLen = len;
+
+ if ((uint)len >= dict.size())
+ {
+ uint oldSize = dict.size();
+ dict.resize (len+1);
+
+ for (uint m=oldSize; m < dict.size(); ++m)
+ dict[m] = 0;
+ }
+
+ if (!dict[len])
+ dict[len] = new QDict<bool> (17, !_insensitive);
+
+ dict[len]->insert(list[i], &trueBool);
+ }
+}
+
+int KateHlKeyword::checkHgl(const QString& text, int offset, int len)
+{
+ int offset2 = offset;
+ int wordLen = 0;
+
+ while ((len > wordLen) && !kateInsideString (deliminators, text[offset2]))
+ {
+ offset2++;
+ wordLen++;
+
+ if (wordLen > maxLen) return 0;
+ }
+
+ if (wordLen < minLen) return 0;
+
+ if ( dict[wordLen] && dict[wordLen]->find(QConstString(text.unicode() + offset, wordLen).string()) )
+ return offset2;
+
+ return 0;
+}
+//END
+
+//BEGIN KateHlInt
+KateHlInt::KateHlInt(int attribute, int context, signed char regionId,signed char regionId2)
+ : KateHlItem(attribute,context,regionId,regionId2)
+{
+ alwaysStartEnable = false;
+}
+
+int KateHlInt::checkHgl(const QString& text, int offset, int len)
+{
+ int offset2 = offset;
+
+ while ((len > 0) && text[offset2].isDigit())
+ {
+ offset2++;
+ len--;
+ }
+
+ if (offset2 > offset)
+ {
+ if (len > 0)
+ {
+ for (uint i=0; i < subItems.size(); i++)
+ {
+ if ( (offset = subItems[i]->checkHgl(text, offset2, len)) )
+ return offset;
+ }
+ }
+
+ return offset2;
+ }
+
+ return 0;
+}
+//END
+
+//BEGIN KateHlFloat
+KateHlFloat::KateHlFloat(int attribute, int context, signed char regionId,signed char regionId2)
+ : KateHlItem(attribute,context, regionId,regionId2)
+{
+ alwaysStartEnable = false;
+}
+
+int KateHlFloat::checkHgl(const QString& text, int offset, int len)
+{
+ bool b = false;
+ bool p = false;
+
+ while ((len > 0) && text[offset].isDigit())
+ {
+ offset++;
+ len--;
+ b = true;
+ }
+
+ if ((len > 0) && (p = (text[offset] == '.')))
+ {
+ offset++;
+ len--;
+
+ while ((len > 0) && text[offset].isDigit())
+ {
+ offset++;
+ len--;
+ b = true;
+ }
+ }
+
+ if (!b)
+ return 0;
+
+ if ((len > 0) && ((text[offset] & 0xdf) == 'E'))
+ {
+ offset++;
+ len--;
+ }
+ else
+ {
+ if (!p)
+ return 0;
+ else
+ {
+ if (len > 0)
+ {
+ for (uint i=0; i < subItems.size(); i++)
+ {
+ int offset2 = subItems[i]->checkHgl(text, offset, len);
+
+ if (offset2)
+ return offset2;
+ }
+ }
+
+ return offset;
+ }
+ }
+
+ if ((len > 0) && (text[offset] == '-' || text[offset] =='+'))
+ {
+ offset++;
+ len--;
+ }
+
+ b = false;
+
+ while ((len > 0) && text[offset].isDigit())
+ {
+ offset++;
+ len--;
+ b = true;
+ }
+
+ if (b)
+ {
+ if (len > 0)
+ {
+ for (uint i=0; i < subItems.size(); i++)
+ {
+ int offset2 = subItems[i]->checkHgl(text, offset, len);
+
+ if (offset2)
+ return offset2;
+ }
+ }
+
+ return offset;
+ }
+
+ return 0;
+}
+//END
+
+//BEGIN KateHlCOct
+KateHlCOct::KateHlCOct(int attribute, int context, signed char regionId,signed char regionId2)
+ : KateHlItem(attribute,context,regionId,regionId2)
+{
+ alwaysStartEnable = false;
+}
+
+int KateHlCOct::checkHgl(const QString& text, int offset, int len)
+{
+ if (text[offset] == '0')
+ {
+ offset++;
+ len--;
+
+ int offset2 = offset;
+
+ while ((len > 0) && (text[offset2] >= '0' && text[offset2] <= '7'))
+ {
+ offset2++;
+ len--;
+ }
+
+ if (offset2 > offset)
+ {
+ if ((len > 0) && ((text[offset2] & 0xdf) == 'L' || (text[offset] & 0xdf) == 'U' ))
+ offset2++;
+
+ return offset2;
+ }
+ }
+
+ return 0;
+}
+//END
+
+//BEGIN KateHlCHex
+KateHlCHex::KateHlCHex(int attribute, int context,signed char regionId,signed char regionId2)
+ : KateHlItem(attribute,context,regionId,regionId2)
+{
+ alwaysStartEnable = false;
+}
+
+int KateHlCHex::checkHgl(const QString& text, int offset, int len)
+{
+ if ((len > 1) && (text[offset++] == '0') && ((text[offset++] & 0xdf) == 'X' ))
+ {
+ len -= 2;
+
+ int offset2 = offset;
+
+ while ((len > 0) && (text[offset2].isDigit() || ((text[offset2] & 0xdf) >= 'A' && (text[offset2] & 0xdf) <= 'F')))
+ {
+ offset2++;
+ len--;
+ }
+
+ if (offset2 > offset)
+ {
+ if ((len > 0) && ((text[offset2] & 0xdf) == 'L' || (text[offset2] & 0xdf) == 'U' ))
+ offset2++;
+
+ return offset2;
+ }
+ }
+
+ return 0;
+}
+//END
+
+//BEGIN KateHlCFloat
+KateHlCFloat::KateHlCFloat(int attribute, int context, signed char regionId,signed char regionId2)
+ : KateHlFloat(attribute,context,regionId,regionId2)
+{
+ alwaysStartEnable = false;
+}
+
+int KateHlCFloat::checkIntHgl(const QString& text, int offset, int len)
+{
+ int offset2 = offset;
+
+ while ((len > 0) && text[offset].isDigit()) {
+ offset2++;
+ len--;
+ }
+
+ if (offset2 > offset)
+ return offset2;
+
+ return 0;
+}
+
+int KateHlCFloat::checkHgl(const QString& text, int offset, int len)
+{
+ int offset2 = KateHlFloat::checkHgl(text, offset, len);
+
+ if (offset2)
+ {
+ if ((text[offset2] & 0xdf) == 'F' )
+ offset2++;
+
+ return offset2;
+ }
+ else
+ {
+ offset2 = checkIntHgl(text, offset, len);
+
+ if (offset2 && ((text[offset2] & 0xdf) == 'F' ))
+ return ++offset2;
+ else
+ return 0;
+ }
+}
+//END
+
+//BEGIN KateHlAnyChar
+KateHlAnyChar::KateHlAnyChar(int attribute, int context, signed char regionId,signed char regionId2, const QString& charList)
+ : KateHlItem(attribute, context,regionId,regionId2)
+ , _charList(charList)
+{
+}
+
+int KateHlAnyChar::checkHgl(const QString& text, int offset, int)
+{
+ if (kateInsideString (_charList, text[offset]))
+ return ++offset;
+
+ return 0;
+}
+//END
+
+//BEGIN KateHlRegExpr
+KateHlRegExpr::KateHlRegExpr( int attribute, int context, signed char regionId,signed char regionId2, QString regexp, bool insensitive, bool minimal)
+ : KateHlItem(attribute, context, regionId,regionId2)
+ , handlesLinestart (regexp.startsWith("^"))
+ , _regexp(regexp)
+ , _insensitive(insensitive)
+ , _minimal(minimal)
+{
+ if (!handlesLinestart)
+ regexp.prepend("^");
+
+ Expr = new QRegExp(regexp, !_insensitive);
+ Expr->setMinimal(_minimal);
+}
+
+int KateHlRegExpr::checkHgl(const QString& text, int offset, int /*len*/)
+{
+ if (offset && handlesLinestart)
+ return 0;
+
+ int offset2 = Expr->search( text, offset, QRegExp::CaretAtOffset );
+
+ if (offset2 == -1) return 0;
+
+ return (offset + Expr->matchedLength());
+}
+
+QStringList *KateHlRegExpr::capturedTexts()
+{
+ return new QStringList(Expr->capturedTexts());
+}
+
+KateHlItem *KateHlRegExpr::clone(const QStringList *args)
+{
+ QString regexp = _regexp;
+ QStringList escArgs = *args;
+
+ for (QStringList::Iterator it = escArgs.begin(); it != escArgs.end(); ++it)
+ {
+ (*it).replace(QRegExp("(\\W)"), "\\\\1");
+ }
+
+ dynamicSubstitute(regexp, &escArgs);
+
+ if (regexp == _regexp)
+ return this;
+
+ // kdDebug (13010) << "clone regexp: " << regexp << endl;
+
+ KateHlRegExpr *ret = new KateHlRegExpr(attr, ctx, region, region2, regexp, _insensitive, _minimal);
+ ret->dynamicChild = true;
+ return ret;
+}
+//END
+
+//BEGIN KateHlLineContinue
+KateHlLineContinue::KateHlLineContinue(int attribute, int context, signed char regionId,signed char regionId2)
+ : KateHlItem(attribute,context,regionId,regionId2) {
+}
+
+int KateHlLineContinue::checkHgl(const QString& text, int offset, int len)
+{
+ if ((len == 1) && (text[offset] == '\\'))
+ return ++offset;
+
+ return 0;
+}
+//END
+
+//BEGIN KateHlCStringChar
+KateHlCStringChar::KateHlCStringChar(int attribute, int context,signed char regionId,signed char regionId2)
+ : KateHlItem(attribute,context,regionId,regionId2) {
+}
+
+// checks for C escaped chars \n and escaped hex/octal chars
+static int checkEscapedChar(const QString& text, int offset, int& len)
+{
+ int i;
+ if (text[offset] == '\\' && len > 1)
+ {
+ offset++;
+ len--;
+
+ switch(text[offset])
+ {
+ case 'a': // checks for control chars
+ case 'b': // we want to fall through
+ case 'e':
+ case 'f':
+
+ case 'n':
+ case 'r':
+ case 't':
+ case 'v':
+ case '\'':
+ case '\"':
+ case '?' : // added ? ANSI C classifies this as an escaped char
+ case '\\':
+ offset++;
+ len--;
+ break;
+
+ case 'x': // if it's like \xff
+ offset++; // eat the x
+ len--;
+ // these for loops can probably be
+ // replaced with something else but
+ // for right now they work
+ // check for hexdigits
+ for (i = 0; (len > 0) && (i < 2) && (text[offset] >= '0' && text[offset] <= '9' || (text[offset] & 0xdf) >= 'A' && (text[offset] & 0xdf) <= 'F'); i++)
+ {
+ offset++;
+ len--;
+ }
+
+ if (i == 0)
+ return 0; // takes care of case '\x'
+
+ break;
+
+ case '0': case '1': case '2': case '3' :
+ case '4': case '5': case '6': case '7' :
+ for (i = 0; (len > 0) && (i < 3) && (text[offset] >='0'&& text[offset] <='7'); i++)
+ {
+ offset++;
+ len--;
+ }
+ break;
+
+ default:
+ return 0;
+ }
+
+ return offset;
+ }
+
+ return 0;
+}
+
+int KateHlCStringChar::checkHgl(const QString& text, int offset, int len)
+{
+ return checkEscapedChar(text, offset, len);
+}
+//END
+
+//BEGIN KateHlCChar
+KateHlCChar::KateHlCChar(int attribute, int context,signed char regionId,signed char regionId2)
+ : KateHlItem(attribute,context,regionId,regionId2) {
+}
+
+int KateHlCChar::checkHgl(const QString& text, int offset, int len)
+{
+ if ((len > 1) && (text[offset] == '\'') && (text[offset+1] != '\''))
+ {
+ int oldl;
+ oldl = len;
+
+ len--;
+
+ int offset2 = checkEscapedChar(text, offset + 1, len);
+
+ if (!offset2)
+ {
+ if (oldl > 2)
+ {
+ offset2 = offset + 2;
+ len = oldl - 2;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ if ((len > 0) && (text[offset2] == '\''))
+ return ++offset2;
+ }
+
+ return 0;
+}
+//END
+
+//BEGIN KateHl2CharDetect
+KateHl2CharDetect::KateHl2CharDetect(int attribute, int context, signed char regionId,signed char regionId2, const QChar *s)
+ : KateHlItem(attribute,context,regionId,regionId2) {
+ sChar1 = s[0];
+ sChar2 = s[1];
+ }
+//END KateHl2CharDetect
+
+KateHlItemData::KateHlItemData(const QString name, int defStyleNum)
+ : name(name), defStyleNum(defStyleNum) {
+}
+
+KateHlData::KateHlData(const QString &wildcards, const QString &mimetypes, const QString &identifier, int priority)
+ : wildcards(wildcards), mimetypes(mimetypes), identifier(identifier), priority(priority)
+{
+}
+
+//BEGIN KateHlContext
+KateHlContext::KateHlContext (const QString &_hlId, int attribute, int lineEndContext, int _lineBeginContext, bool _fallthrough,
+ int _fallthroughContext, bool _dynamic, bool _noIndentationBasedFolding)
+{
+ hlId = _hlId;
+ attr = attribute;
+ ctx = lineEndContext;
+ lineBeginContext = _lineBeginContext;
+ fallthrough = _fallthrough;
+ ftctx = _fallthroughContext;
+ dynamic = _dynamic;
+ dynamicChild = false;
+ noIndentationBasedFolding=_noIndentationBasedFolding;
+ if (_noIndentationBasedFolding) kdDebug(13010)<<QString("**********************_noIndentationBasedFolding is TRUE*****************")<<endl;
+
+}
+
+KateHlContext *KateHlContext::clone(const QStringList *args)
+{
+ KateHlContext *ret = new KateHlContext(hlId, attr, ctx, lineBeginContext, fallthrough, ftctx, false,noIndentationBasedFolding);
+
+ for (uint n=0; n < items.size(); ++n)
+ {
+ KateHlItem *item = items[n];
+ KateHlItem *i = (item->dynamic ? item->clone(args) : item);
+ ret->items.append(i);
+ }
+
+ ret->dynamicChild = true;
+
+ return ret;
+}
+
+KateHlContext::~KateHlContext()
+{
+ if (dynamicChild)
+ {
+ for (uint n=0; n < items.size(); ++n)
+ {
+ if (items[n]->dynamicChild)
+ delete items[n];
+ }
+ }
+}
+//END
+
+//BEGIN KateHighlighting
+KateHighlighting::KateHighlighting(const KateSyntaxModeListItem *def) : refCount(0)
+{
+ m_attributeArrays.setAutoDelete (true);
+
+ errorsAndWarnings = "";
+ building=false;
+ noHl = false;
+ m_foldingIndentationSensitive = false;
+ folding=false;
+ internalIDList.setAutoDelete(true);
+
+ if (def == 0)
+ {
+ noHl = true;
+ iName = "None"; // not translated internal name (for config and more)
+ iNameTranslated = i18n("None"); // user visible name
+ iSection = "";
+ m_priority = 0;
+ iHidden = false;
+ m_additionalData.insert( "none", new HighlightPropertyBag );
+ m_additionalData["none"]->deliminator = stdDeliminator;
+ m_additionalData["none"]->wordWrapDeliminator = stdDeliminator;
+ m_hlIndex[0] = "none";
+ }
+ else
+ {
+ iName = def->name;
+ iNameTranslated = def->nameTranslated;
+ iSection = def->section;
+ iHidden = def->hidden;
+ iWildcards = def->extension;
+ iMimetypes = def->mimetype;
+ identifier = def->identifier;
+ iVersion=def->version;
+ iAuthor=def->author;
+ iLicense=def->license;
+ m_priority=def->priority.toInt();
+ }
+
+ deliminator = stdDeliminator;
+}
+
+KateHighlighting::~KateHighlighting()
+{
+ // cu contexts
+ for (uint i=0; i < m_contexts.size(); ++i)
+ delete m_contexts[i];
+ m_contexts.clear ();
+}
+
+void KateHighlighting::generateContextStack(int *ctxNum, int ctx, QMemArray<short>* ctxs, int *prevLine)
+{
+ //kdDebug(13010)<<QString("Entering generateContextStack with %1").arg(ctx)<<endl;
+ while (true)
+ {
+ if (ctx >= 0)
+ {
+ (*ctxNum) = ctx;
+
+ ctxs->resize (ctxs->size()+1, QGArray::SpeedOptim);
+ (*ctxs)[ctxs->size()-1]=(*ctxNum);
+
+ return;
+ }
+ else
+ {
+ if (ctx == -1)
+ {
+ (*ctxNum)=( (ctxs->isEmpty() ) ? 0 : (*ctxs)[ctxs->size()-1]);
+ }
+ else
+ {
+ int size = ctxs->size() + ctx + 1;
+
+ if (size > 0)
+ {
+ ctxs->resize (size, QGArray::SpeedOptim);
+ (*ctxNum)=(*ctxs)[size-1];
+ }
+ else
+ {
+ ctxs->resize (0, QGArray::SpeedOptim);
+ (*ctxNum)=0;
+ }
+
+ ctx = 0;
+
+ if ((*prevLine) >= (int)(ctxs->size()-1))
+ {
+ *prevLine=ctxs->size()-1;
+
+ if ( ctxs->isEmpty() )
+ return;
+
+ KateHlContext *c = contextNum((*ctxs)[ctxs->size()-1]);
+ if (c && (c->ctx != -1))
+ {
+ //kdDebug(13010)<<"PrevLine > size()-1 and ctx!=-1)"<<endl;
+ ctx = c->ctx;
+
+ continue;
+ }
+ }
+ }
+
+ return;
+ }
+ }
+}
+
+/**
+ * Creates a new dynamic context or reuse an old one if it has already been created.
+ */
+int KateHighlighting::makeDynamicContext(KateHlContext *model, const QStringList *args)
+{
+ QPair<KateHlContext *, QString> key(model, args->front());
+ short value;
+
+ if (dynamicCtxs.contains(key))
+ value = dynamicCtxs[key];
+ else
+ {
+ kdDebug(13010) << "new stuff: " << startctx << endl;
+
+ KateHlContext *newctx = model->clone(args);
+
+ m_contexts.push_back (newctx);
+
+ value = startctx++;
+ dynamicCtxs[key] = value;
+ KateHlManager::self()->incDynamicCtxs();
+ }
+
+ // kdDebug(13010) << "Dynamic context: using context #" << value << " (for model " << model << " with args " << *args << ")" << endl;
+
+ return value;
+}
+
+/**
+ * Drop all dynamic contexts. Shall be called with extreme care, and shall be immediatly
+ * followed by a full HL invalidation.
+ */
+void KateHighlighting::dropDynamicContexts()
+{
+ for (uint i=base_startctx; i < m_contexts.size(); ++i)
+ delete m_contexts[i];
+
+ m_contexts.resize (base_startctx);
+
+ dynamicCtxs.clear();
+ startctx = base_startctx;
+}
+
+/**
+ * Parse the text and fill in the context array and folding list array
+ *
+ * @param prevLine The previous line, the context array is picked up from that if present.
+ * @param textLine The text line to parse
+ * @param foldingList will be filled
+ * @param ctxChanged will be set to reflect if the context changed
+ */
+void KateHighlighting::doHighlight ( KateTextLine *prevLine,
+ KateTextLine *textLine,
+ QMemArray<uint>* foldingList,
+ bool *ctxChanged )
+{
+ if (!textLine)
+ return;
+
+ if (noHl)
+ {
+ if (textLine->length() > 0)
+ memset (textLine->attributes(), 0, textLine->length());
+
+ return;
+ }
+
+ // duplicate the ctx stack, only once !
+ QMemArray<short> ctx;
+ ctx.duplicate (prevLine->ctxArray());
+
+ int ctxNum = 0;
+ int previousLine = -1;
+ KateHlContext *context;
+
+ if (ctx.isEmpty())
+ {
+ // If the stack is empty, we assume to be in Context 0 (Normal)
+ context = contextNum(ctxNum);
+ }
+ else
+ {
+ // There does an old context stack exist -> find the context at the line start
+ ctxNum = ctx[ctx.size()-1]; //context ID of the last character in the previous line
+
+ //kdDebug(13010) << "\t\tctxNum = " << ctxNum << " contextList[ctxNum] = " << contextList[ctxNum] << endl; // ellis
+
+ //if (lineContinue) kdDebug(13010)<<QString("The old context should be %1").arg((int)ctxNum)<<endl;
+
+ if (!(context = contextNum(ctxNum)))
+ context = contextNum(0);
+
+ //kdDebug(13010)<<"test1-2-1-text2"<<endl;
+
+ previousLine=ctx.size()-1; //position of the last context ID of th previous line within the stack
+
+ // hl continue set or not ???
+ if (prevLine->hlLineContinue())
+ {
+ prevLine--;
+ }
+ else
+ {
+ generateContextStack(&ctxNum, context->ctx, &ctx, &previousLine); //get stack ID to use
+
+ if (!(context = contextNum(ctxNum)))
+ context = contextNum(0);
+ }
+
+ //kdDebug(13010)<<"test1-2-1-text4"<<endl;
+
+ //if (lineContinue) kdDebug(13010)<<QString("The new context is %1").arg((int)ctxNum)<<endl;
+ }
+
+ // text, for programming convenience :)
+ QChar lastChar = ' ';
+ const QString& text = textLine->string();
+ const int len = textLine->length();
+
+ // calc at which char the first char occurs, set it to lenght of line if never
+ const int firstChar = textLine->firstChar();
+ const int startNonSpace = (firstChar == -1) ? len : firstChar;
+
+ // last found item
+ KateHlItem *item = 0;
+
+ // loop over the line, offset gives current offset
+ int offset = 0;
+ while (offset < len)
+ {
+ bool anItemMatched = false;
+ bool standardStartEnableDetermined = false;
+ bool customStartEnableDetermined = false;
+
+ uint index = 0;
+ for (item = context->items.empty() ? 0 : context->items[0]; item; item = (++index < context->items.size()) ? context->items[index] : 0 )
+ {
+ // does we only match if we are firstNonSpace?
+ if (item->firstNonSpace && (offset > startNonSpace))
+ continue;
+
+ // have we a column specified? if yes, only match at this column
+ if ((item->column != -1) && (item->column != offset))
+ continue;
+
+ if (!item->alwaysStartEnable)
+ {
+ if (item->customStartEnable)
+ {
+ if (customStartEnableDetermined || kateInsideString (m_additionalData[context->hlId]->deliminator, lastChar))
+ customStartEnableDetermined = true;
+ else
+ continue;
+ }
+ else
+ {
+ if (standardStartEnableDetermined || kateInsideString (stdDeliminator, lastChar))
+ standardStartEnableDetermined = true;
+ else
+ continue;
+ }
+ }
+
+ int offset2 = item->checkHgl(text, offset, len-offset);
+
+ if (offset2 <= offset)
+ continue;
+ // BUG 144599: Ignore a context change that would push the same context
+ // without eating anything... this would be an infinite loop!
+ if ( item->lookAhead && item->ctx == ctxNum )
+ continue;
+
+ if (item->region2)
+ {
+ // kdDebug(13010)<<QString("Region mark 2 detected: %1").arg(item->region2)<<endl;
+ if ( !foldingList->isEmpty() && ((item->region2 < 0) && (*foldingList)[foldingList->size()-2] == -item->region2 ) )
+ {
+ foldingList->resize (foldingList->size()-2, QGArray::SpeedOptim);
+ }
+ else
+ {
+ foldingList->resize (foldingList->size()+2, QGArray::SpeedOptim);
+ (*foldingList)[foldingList->size()-2] = (uint)item->region2;
+ if (item->region2<0) //check not really needed yet
+ (*foldingList)[foldingList->size()-1] = offset2;
+ else
+ (*foldingList)[foldingList->size()-1] = offset;
+ }
+
+ }
+
+ if (item->region)
+ {
+ // kdDebug(13010)<<QString("Region mark detected: %1").arg(item->region)<<endl;
+
+ /* if ( !foldingList->isEmpty() && ((item->region < 0) && (*foldingList)[foldingList->size()-1] == -item->region ) )
+ {
+ foldingList->resize (foldingList->size()-1, QGArray::SpeedOptim);
+ }
+ else*/
+ {
+ foldingList->resize (foldingList->size()+2, QGArray::SpeedOptim);
+ (*foldingList)[foldingList->size()-2] = item->region;
+ if (item->region<0) //check not really needed yet
+ (*foldingList)[foldingList->size()-1] = offset2;
+ else
+ (*foldingList)[foldingList->size()-1] = offset;
+ }
+
+ }
+
+ // regenerate context stack if needed
+ if (item->ctx != -1)
+ {
+ generateContextStack (&ctxNum, item->ctx, &ctx, &previousLine);
+ context = contextNum(ctxNum);
+ }
+
+ // dynamic context: substitute the model with an 'instance'
+ if (context->dynamic)
+ {
+ QStringList *lst = item->capturedTexts();
+ if (lst != 0)
+ {
+ // Replace the top of the stack and the current context
+ int newctx = makeDynamicContext(context, lst);
+ if (ctx.size() > 0)
+ ctx[ctx.size() - 1] = newctx;
+ ctxNum = newctx;
+ context = contextNum(ctxNum);
+ }
+ delete lst;
+ }
+
+ // dominik: look ahead w/o changing offset?
+ if (!item->lookAhead)
+ {
+ if (offset2 > len)
+ offset2 = len;
+
+ // even set attributes ;)
+ memset ( textLine->attributes()+offset
+ , item->onlyConsume ? context->attr : item->attr
+ , offset2-offset);
+
+ offset = offset2;
+ lastChar = text[offset-1];
+ }
+
+ anItemMatched = true;
+ break;
+ }
+
+ // something matched, continue loop
+ if (anItemMatched)
+ continue;
+
+ // nothing found: set attribute of one char
+ // anders: unless this context does not want that!
+ if ( context->fallthrough )
+ {
+ // set context to context->ftctx.
+ generateContextStack(&ctxNum, context->ftctx, &ctx, &previousLine); //regenerate context stack
+ context=contextNum(ctxNum);
+ //kdDebug(13010)<<"context num after fallthrough at col "<<z<<": "<<ctxNum<<endl;
+ // the next is nessecary, as otherwise keyword (or anything using the std delimitor check)
+ // immediately after fallthrough fails. Is it bad?
+ // jowenn, can you come up with a nicer way to do this?
+ /* if (offset)
+ lastChar = text[offset - 1];
+ else
+ lastChar = '\\';*/
+ continue;
+ }
+ else
+ {
+ *(textLine->attributes() + offset) = context->attr;
+ lastChar = text[offset];
+ offset++;
+ }
+ }
+
+ // has the context stack changed ?
+ if (ctx == textLine->ctxArray())
+ {
+ if (ctxChanged)
+ (*ctxChanged) = false;
+ }
+ else
+ {
+ if (ctxChanged)
+ (*ctxChanged) = true;
+
+ // assign ctx stack !
+ textLine->setContext(ctx);
+ }
+
+ // write hl continue flag
+ textLine->setHlLineContinue (item && item->lineContinue());
+
+ if (m_foldingIndentationSensitive) {
+ bool noindent=false;
+ for(int i=ctx.size()-1; i>=0; --i) {
+ if (contextNum(ctx[i])->noIndentationBasedFolding) {
+ noindent=true;
+ break;
+ }
+ }
+ textLine->setNoIndentBasedFolding(noindent);
+ }
+}
+
+void KateHighlighting::loadWildcards()
+{
+ KConfig *config = KateHlManager::self()->getKConfig();
+ config->setGroup("Highlighting " + iName);
+
+ QString extensionString = config->readEntry("Wildcards", iWildcards);
+
+ if (extensionSource != extensionString) {
+ regexpExtensions.clear();
+ plainExtensions.clear();
+
+ extensionSource = extensionString;
+
+ static QRegExp sep("\\s*;\\s*");
+
+ QStringList l = QStringList::split( sep, extensionSource );
+
+ static QRegExp boringExpression("\\*\\.[\\d\\w]+");
+
+ for( QStringList::Iterator it = l.begin(); it != l.end(); ++it )
+ if (boringExpression.exactMatch(*it))
+ plainExtensions.append((*it).mid(1));
+ else
+ regexpExtensions.append(QRegExp((*it), true, true));
+ }
+}
+
+QValueList<QRegExp>& KateHighlighting::getRegexpExtensions()
+{
+ return regexpExtensions;
+}
+
+QStringList& KateHighlighting::getPlainExtensions()
+{
+ return plainExtensions;
+}
+
+QString KateHighlighting::getMimetypes()
+{
+ KConfig *config = KateHlManager::self()->getKConfig();
+ config->setGroup("Highlighting " + iName);
+
+ return config->readEntry("Mimetypes", iMimetypes);
+}
+
+int KateHighlighting::priority()
+{
+ KConfig *config = KateHlManager::self()->getKConfig();
+ config->setGroup("Highlighting " + iName);
+
+ return config->readNumEntry("Priority", m_priority);
+}
+
+KateHlData *KateHighlighting::getData()
+{
+ KConfig *config = KateHlManager::self()->getKConfig();
+ config->setGroup("Highlighting " + iName);
+
+ KateHlData *hlData = new KateHlData(
+ config->readEntry("Wildcards", iWildcards),
+ config->readEntry("Mimetypes", iMimetypes),
+ config->readEntry("Identifier", identifier),
+ config->readNumEntry("Priority", m_priority));
+
+ return hlData;
+}
+
+void KateHighlighting::setData(KateHlData *hlData)
+{
+ KConfig *config = KateHlManager::self()->getKConfig();
+ config->setGroup("Highlighting " + iName);
+
+ config->writeEntry("Wildcards",hlData->wildcards);
+ config->writeEntry("Mimetypes",hlData->mimetypes);
+ config->writeEntry("Priority",hlData->priority);
+}
+
+void KateHighlighting::getKateHlItemDataList (uint schema, KateHlItemDataList &list)
+{
+ KConfig *config = KateHlManager::self()->getKConfig();
+ config->setGroup("Highlighting " + iName + " - Schema " + KateFactory::self()->schemaManager()->name(schema));
+
+ list.clear();
+ createKateHlItemData(list);
+
+ for (KateHlItemData *p = list.first(); p != 0L; p = list.next())
+ {
+ QStringList s = config->readListEntry(p->name);
+
+// kdDebug(13010)<<p->name<<s.count()<<endl;
+ if (s.count()>0)
+ {
+
+ while(s.count()<9) s<<"";
+ p->clear();
+
+ QString tmp=s[0]; if (!tmp.isEmpty()) p->defStyleNum=tmp.toInt();
+
+ QRgb col;
+
+ tmp=s[1]; if (!tmp.isEmpty()) {
+ col=tmp.toUInt(0,16); p->setTextColor(col); }
+
+ tmp=s[2]; if (!tmp.isEmpty()) {
+ col=tmp.toUInt(0,16); p->setSelectedTextColor(col); }
+
+ tmp=s[3]; if (!tmp.isEmpty()) p->setBold(tmp!="0");
+
+ tmp=s[4]; if (!tmp.isEmpty()) p->setItalic(tmp!="0");
+
+ tmp=s[5]; if (!tmp.isEmpty()) p->setStrikeOut(tmp!="0");
+
+ tmp=s[6]; if (!tmp.isEmpty()) p->setUnderline(tmp!="0");
+
+ tmp=s[7]; if (!tmp.isEmpty()) {
+ col=tmp.toUInt(0,16); p->setBGColor(col); }
+
+ tmp=s[8]; if (!tmp.isEmpty()) {
+ col=tmp.toUInt(0,16); p->setSelectedBGColor(col); }
+
+ }
+ }
+}
+
+/**
+ * Saves the KateHlData attribute definitions to the config file.
+ *
+ * @param schema The id of the schema group to save
+ * @param list KateHlItemDataList containing the data to be used
+ */
+void KateHighlighting::setKateHlItemDataList(uint schema, KateHlItemDataList &list)
+{
+ KConfig *config = KateHlManager::self()->getKConfig();
+ config->setGroup("Highlighting " + iName + " - Schema "
+ + KateFactory::self()->schemaManager()->name(schema));
+
+ QStringList settings;
+
+ for (KateHlItemData *p = list.first(); p != 0L; p = list.next())
+ {
+ settings.clear();
+ settings<<QString::number(p->defStyleNum,10);
+ settings<<(p->itemSet(KateAttribute::TextColor)?QString::number(p->textColor().rgb(),16):"");
+ settings<<(p->itemSet(KateAttribute::SelectedTextColor)?QString::number(p->selectedTextColor().rgb(),16):"");
+ settings<<(p->itemSet(KateAttribute::Weight)?(p->bold()?"1":"0"):"");
+ settings<<(p->itemSet(KateAttribute::Italic)?(p->italic()?"1":"0"):"");
+ settings<<(p->itemSet(KateAttribute::StrikeOut)?(p->strikeOut()?"1":"0"):"");
+ settings<<(p->itemSet(KateAttribute::Underline)?(p->underline()?"1":"0"):"");
+ settings<<(p->itemSet(KateAttribute::BGColor)?QString::number(p->bgColor().rgb(),16):"");
+ settings<<(p->itemSet(KateAttribute::SelectedBGColor)?QString::number(p->selectedBGColor().rgb(),16):"");
+ settings<<"---";
+ config->writeEntry(p->name,settings);
+ }
+}
+
+/**
+ * Increase the usage count, and trigger initialization if needed.
+ */
+void KateHighlighting::use()
+{
+ if (refCount == 0)
+ init();
+
+ refCount++;
+}
+
+/**
+ * Decrease the usage count, and trigger cleanup if needed.
+ */
+void KateHighlighting::release()
+{
+ refCount--;
+
+ if (refCount == 0)
+ done();
+}
+
+/**
+ * Initialize a context for the first time.
+ */
+
+void KateHighlighting::init()
+{
+ if (noHl)
+ return;
+
+ // cu contexts
+ for (uint i=0; i < m_contexts.size(); ++i)
+ delete m_contexts[i];
+ m_contexts.clear ();
+
+ makeContextList();
+}
+
+
+/**
+ * If the there is no document using the highlighting style free the complete
+ * context structure.
+ */
+void KateHighlighting::done()
+{
+ if (noHl)
+ return;
+
+ // cu contexts
+ for (uint i=0; i < m_contexts.size(); ++i)
+ delete m_contexts[i];
+ m_contexts.clear ();
+
+ internalIDList.clear();
+}
+
+/**
+ * KateHighlighting - createKateHlItemData
+ * This function reads the itemData entries from the config file, which specifies the
+ * default attribute styles for matched items/contexts.
+ *
+ * @param list A reference to the internal list containing the parsed default config
+ */
+void KateHighlighting::createKateHlItemData(KateHlItemDataList &list)
+{
+ // If no highlighting is selected we need only one default.
+ if (noHl)
+ {
+ list.append(new KateHlItemData(i18n("Normal Text"), KateHlItemData::dsNormal));
+ return;
+ }
+
+ // If the internal list isn't already available read the config file
+ if (internalIDList.isEmpty())
+ makeContextList();
+
+ list=internalIDList;
+}
+
+/**
+ * Adds the styles of the currently parsed highlight to the itemdata list
+ */
+void KateHighlighting::addToKateHlItemDataList()
+{
+ //Tell the syntax document class which file we want to parse and which data group
+ KateHlManager::self()->syntax->setIdentifier(buildIdentifier);
+ KateSyntaxContextData *data = KateHlManager::self()->syntax->getGroupInfo("highlighting","itemData");
+
+ //begin with the real parsing
+ while (KateHlManager::self()->syntax->nextGroup(data))
+ {
+ // read all attributes
+ QString color = KateHlManager::self()->syntax->groupData(data,QString("color"));
+ QString selColor = KateHlManager::self()->syntax->groupData(data,QString("selColor"));
+ QString bold = KateHlManager::self()->syntax->groupData(data,QString("bold"));
+ QString italic = KateHlManager::self()->syntax->groupData(data,QString("italic"));
+ QString underline = KateHlManager::self()->syntax->groupData(data,QString("underline"));
+ QString strikeOut = KateHlManager::self()->syntax->groupData(data,QString("strikeOut"));
+ QString bgColor = KateHlManager::self()->syntax->groupData(data,QString("backgroundColor"));
+ QString selBgColor = KateHlManager::self()->syntax->groupData(data,QString("selBackgroundColor"));
+
+ KateHlItemData* newData = new KateHlItemData(
+ buildPrefix+KateHlManager::self()->syntax->groupData(data,QString("name")).simplifyWhiteSpace(),
+ getDefStyleNum(KateHlManager::self()->syntax->groupData(data,QString("defStyleNum"))));
+
+ /* here the custom style overrides are specified, if needed */
+ if (!color.isEmpty()) newData->setTextColor(QColor(color));
+ if (!selColor.isEmpty()) newData->setSelectedTextColor(QColor(selColor));
+ if (!bold.isEmpty()) newData->setBold( IS_TRUE(bold) );
+ if (!italic.isEmpty()) newData->setItalic( IS_TRUE(italic) );
+ // new attributes for the new rendering view
+ if (!underline.isEmpty()) newData->setUnderline( IS_TRUE(underline) );
+ if (!strikeOut.isEmpty()) newData->setStrikeOut( IS_TRUE(strikeOut) );
+ if (!bgColor.isEmpty()) newData->setBGColor(QColor(bgColor));
+ if (!selBgColor.isEmpty()) newData->setSelectedBGColor(QColor(selBgColor));
+
+ internalIDList.append(newData);
+ }
+
+ //clean up
+ if (data)
+ KateHlManager::self()->syntax->freeGroupInfo(data);
+}
+
+/**
+ * KateHighlighting - lookupAttrName
+ * This function is a helper for makeContextList and createKateHlItem. It looks the given
+ * attribute name in the itemData list up and returns it's index
+ *
+ * @param name the attribute name to lookup
+ * @param iDl the list containing all available attributes
+ *
+ * @return The index of the attribute, or 0 if the attribute isn't found
+ */
+int KateHighlighting::lookupAttrName(const QString& name, KateHlItemDataList &iDl)
+{
+ for (uint i = 0; i < iDl.count(); i++)
+ if (iDl.at(i)->name == buildPrefix+name)
+ return i;
+
+ kdDebug(13010)<<"Couldn't resolve itemDataName:"<<name<<endl;
+ return 0;
+}
+
+/**
+ * KateHighlighting - createKateHlItem
+ * This function is a helper for makeContextList. It parses the xml file for
+ * information.
+ *
+ * @param data Data about the item read from the xml file
+ * @param iDl List of all available itemData entries.
+ * Needed for attribute name->index translation
+ * @param RegionList list of code folding region names
+ * @param ContextNameList list of context names
+ *
+ * @return A pointer to the newly created item object
+ */
+KateHlItem *KateHighlighting::createKateHlItem(KateSyntaxContextData *data,
+ KateHlItemDataList &iDl,
+ QStringList *RegionList,
+ QStringList *ContextNameList)
+{
+ // No highlighting -> exit
+ if (noHl)
+ return 0;
+
+ // get the (tagname) itemd type
+ QString dataname=KateHlManager::self()->syntax->groupItemData(data,QString(""));
+
+ // code folding region handling:
+ QString beginRegionStr=KateHlManager::self()->syntax->groupItemData(data,QString("beginRegion"));
+ QString endRegionStr=KateHlManager::self()->syntax->groupItemData(data,QString("endRegion"));
+
+ signed char regionId=0;
+ signed char regionId2=0;
+
+ if (!beginRegionStr.isEmpty())
+ {
+ regionId = RegionList->findIndex(beginRegionStr);
+
+ if (regionId==-1) // if the region name doesn't already exist, add it to the list
+ {
+ (*RegionList)<<beginRegionStr;
+ regionId = RegionList->findIndex(beginRegionStr);
+ }
+
+ regionId++;
+
+ kdDebug(13010) << "########### BEG REG: " << beginRegionStr << " NUM: " << regionId << endl;
+ }
+
+ if (!endRegionStr.isEmpty())
+ {
+ regionId2 = RegionList->findIndex(endRegionStr);
+
+ if (regionId2==-1) // if the region name doesn't already exist, add it to the list
+ {
+ (*RegionList)<<endRegionStr;
+ regionId2 = RegionList->findIndex(endRegionStr);
+ }
+
+ regionId2 = -regionId2 - 1;
+
+ kdDebug(13010) << "########### END REG: " << endRegionStr << " NUM: " << regionId2 << endl;
+ }
+
+ int attr = 0;
+ QString tmpAttr=KateHlManager::self()->syntax->groupItemData(data,QString("attribute")).simplifyWhiteSpace();
+ bool onlyConsume = tmpAttr.isEmpty();
+
+ // only relevant for non consumer
+ if (!onlyConsume)
+ {
+ if (QString("%1").arg(tmpAttr.toInt())==tmpAttr)
+ {
+ errorsAndWarnings+=i18n(
+ "<B>%1</B>: Deprecated syntax. Attribute (%2) not addressed by symbolic name<BR>").
+ arg(buildIdentifier).arg(tmpAttr);
+ attr=tmpAttr.toInt();
+ }
+ else
+ attr=lookupAttrName(tmpAttr,iDl);
+ }
+
+ // Info about context switch
+ int context = -1;
+ QString unresolvedContext;
+ QString tmpcontext=KateHlManager::self()->syntax->groupItemData(data,QString("context"));
+ if (!tmpcontext.isEmpty())
+ context=getIdFromString(ContextNameList, tmpcontext,unresolvedContext);
+
+ // Get the char parameter (eg DetectChar)
+ char chr;
+ if (! KateHlManager::self()->syntax->groupItemData(data,QString("char")).isEmpty())
+ chr= (KateHlManager::self()->syntax->groupItemData(data,QString("char")).latin1())[0];
+ else
+ chr=0;
+
+ // Get the String parameter (eg. StringDetect)
+ QString stringdata=KateHlManager::self()->syntax->groupItemData(data,QString("String"));
+
+ // Get a second char parameter (char1) (eg Detect2Chars)
+ char chr1;
+ if (! KateHlManager::self()->syntax->groupItemData(data,QString("char1")).isEmpty())
+ chr1= (KateHlManager::self()->syntax->groupItemData(data,QString("char1")).latin1())[0];
+ else
+ chr1=0;
+
+ // Will be removed eventually. Atm used for StringDetect, keyword and RegExp
+ const QString & insensitive_str = KateHlManager::self()->syntax->groupItemData(data,QString("insensitive"));
+ bool insensitive = IS_TRUE( insensitive_str );
+
+ // for regexp only
+ bool minimal = IS_TRUE( KateHlManager::self()->syntax->groupItemData(data,QString("minimal")) );
+
+ // dominik: look ahead and do not change offset. so we can change contexts w/o changing offset1.
+ bool lookAhead = IS_TRUE( KateHlManager::self()->syntax->groupItemData(data,QString("lookAhead")) );
+
+ bool dynamic= IS_TRUE(KateHlManager::self()->syntax->groupItemData(data,QString("dynamic")) );
+
+ bool firstNonSpace = IS_TRUE(KateHlManager::self()->syntax->groupItemData(data,QString("firstNonSpace")) );
+
+ int column = -1;
+ QString colStr = KateHlManager::self()->syntax->groupItemData(data,QString("column"));
+ if (!colStr.isEmpty())
+ column = colStr.toInt();
+
+ //Create the item corresponding to it's type and set it's parameters
+ KateHlItem *tmpItem;
+
+ if (dataname=="keyword")
+ {
+ bool keywordInsensitive = insensitive_str.isEmpty() ? !casesensitive : insensitive;
+ KateHlKeyword *keyword=new KateHlKeyword(attr,context,regionId,regionId2,keywordInsensitive,
+ m_additionalData[ buildIdentifier ]->deliminator);
+
+ //Get the entries for the keyword lookup list
+ keyword->addList(KateHlManager::self()->syntax->finddata("highlighting",stringdata));
+ tmpItem=keyword;
+ }
+ else if (dataname=="Float") tmpItem= (new KateHlFloat(attr,context,regionId,regionId2));
+ else if (dataname=="Int") tmpItem=(new KateHlInt(attr,context,regionId,regionId2));
+ else if (dataname=="DetectChar") tmpItem=(new KateHlCharDetect(attr,context,regionId,regionId2,chr));
+ else if (dataname=="Detect2Chars") tmpItem=(new KateHl2CharDetect(attr,context,regionId,regionId2,chr,chr1));
+ else if (dataname=="RangeDetect") tmpItem=(new KateHlRangeDetect(attr,context,regionId,regionId2, chr, chr1));
+ else if (dataname=="LineContinue") tmpItem=(new KateHlLineContinue(attr,context,regionId,regionId2));
+ else if (dataname=="StringDetect") tmpItem=(new KateHlStringDetect(attr,context,regionId,regionId2,stringdata,insensitive));
+ else if (dataname=="AnyChar") tmpItem=(new KateHlAnyChar(attr,context,regionId,regionId2,stringdata));
+ else if (dataname=="RegExpr") tmpItem=(new KateHlRegExpr(attr,context,regionId,regionId2,stringdata, insensitive, minimal));
+ else if (dataname=="HlCChar") tmpItem= ( new KateHlCChar(attr,context,regionId,regionId2));
+ else if (dataname=="HlCHex") tmpItem= (new KateHlCHex(attr,context,regionId,regionId2));
+ else if (dataname=="HlCOct") tmpItem= (new KateHlCOct(attr,context,regionId,regionId2));
+ else if (dataname=="HlCFloat") tmpItem= (new KateHlCFloat(attr,context,regionId,regionId2));
+ else if (dataname=="HlCStringChar") tmpItem= (new KateHlCStringChar(attr,context,regionId,regionId2));
+ else if (dataname=="DetectSpaces") tmpItem= (new KateHlDetectSpaces(attr,context,regionId,regionId2));
+ else if (dataname=="DetectIdentifier") tmpItem= (new KateHlDetectIdentifier(attr,context,regionId,regionId2));
+ else
+ {
+ // oops, unknown type. Perhaps a spelling error in the xml file
+ return 0;
+ }
+
+ // set lookAhead & dynamic properties
+ tmpItem->lookAhead = lookAhead;
+ tmpItem->dynamic = dynamic;
+ tmpItem->firstNonSpace = firstNonSpace;
+ tmpItem->column = column;
+ tmpItem->onlyConsume = onlyConsume;
+
+ if (!unresolvedContext.isEmpty())
+ {
+ unresolvedContextReferences.insert(&(tmpItem->ctx),unresolvedContext);
+ }
+
+ return tmpItem;
+}
+
+QString KateHighlighting::hlKeyForAttrib( int i ) const
+{
+ // find entry. This is faster than QMap::find. m_hlIndex always has an entry
+ // for key '0' (it is "none"), so the result is always valid.
+ int k = 0;
+ QMap<int,QString>::const_iterator it = m_hlIndex.constEnd();
+ while ( it != m_hlIndex.constBegin() )
+ {
+ --it;
+ k = it.key();
+ if ( i >= k )
+ break;
+ }
+ return it.data();
+}
+
+bool KateHighlighting::isInWord( QChar c, int attrib ) const
+{
+ return m_additionalData[ hlKeyForAttrib( attrib ) ]->deliminator.find(c) < 0
+ && !c.isSpace() && c != '"' && c != '\'';
+}
+
+bool KateHighlighting::canBreakAt( QChar c, int attrib ) const
+{
+ static const QString& sq = KGlobal::staticQString("\"'");
+ return (m_additionalData[ hlKeyForAttrib( attrib ) ]->wordWrapDeliminator.find(c) != -1) && (sq.find(c) == -1);
+}
+
+signed char KateHighlighting::commentRegion(int attr) const {
+ QString commentRegion=m_additionalData[ hlKeyForAttrib( attr ) ]->multiLineRegion;
+ return (commentRegion.isEmpty()?0:(commentRegion.toShort()));
+}
+
+bool KateHighlighting::canComment( int startAttrib, int endAttrib ) const
+{
+ QString k = hlKeyForAttrib( startAttrib );
+ return ( k == hlKeyForAttrib( endAttrib ) &&
+ ( ( !m_additionalData[k]->multiLineCommentStart.isEmpty() && !m_additionalData[k]->multiLineCommentEnd.isEmpty() ) ||
+ ! m_additionalData[k]->singleLineCommentMarker.isEmpty() ) );
+}
+
+QString KateHighlighting::getCommentStart( int attrib ) const
+{
+ return m_additionalData[ hlKeyForAttrib( attrib) ]->multiLineCommentStart;
+}
+
+QString KateHighlighting::getCommentEnd( int attrib ) const
+{
+ return m_additionalData[ hlKeyForAttrib( attrib ) ]->multiLineCommentEnd;
+}
+
+QString KateHighlighting::getCommentSingleLineStart( int attrib ) const
+{
+ return m_additionalData[ hlKeyForAttrib( attrib) ]->singleLineCommentMarker;
+}
+
+KateHighlighting::CSLPos KateHighlighting::getCommentSingleLinePosition( int attrib ) const
+{
+ return m_additionalData[ hlKeyForAttrib( attrib) ]->singleLineCommentPosition;
+}
+
+
+/**
+ * Helper for makeContextList. It parses the xml file for
+ * information, how single or multi line comments are marked
+ */
+void KateHighlighting::readCommentConfig()
+{
+ KateHlManager::self()->syntax->setIdentifier(buildIdentifier);
+ KateSyntaxContextData *data=KateHlManager::self()->syntax->getGroupInfo("general","comment");
+
+ QString cmlStart="", cmlEnd="", cmlRegion="", cslStart="";
+ CSLPos cslPosition=CSLPosColumn0;
+
+ if (data)
+ {
+ while (KateHlManager::self()->syntax->nextGroup(data))
+ {
+ if (KateHlManager::self()->syntax->groupData(data,"name")=="singleLine")
+ {
+ cslStart=KateHlManager::self()->syntax->groupData(data,"start");
+ QString cslpos=KateHlManager::self()->syntax->groupData(data,"position");
+ if (cslpos=="afterwhitespace")
+ cslPosition=CSLPosAfterWhitespace;
+ else
+ cslPosition=CSLPosColumn0;
+ }
+ else if (KateHlManager::self()->syntax->groupData(data,"name")=="multiLine")
+ {
+ cmlStart=KateHlManager::self()->syntax->groupData(data,"start");
+ cmlEnd=KateHlManager::self()->syntax->groupData(data,"end");
+ cmlRegion=KateHlManager::self()->syntax->groupData(data,"region");
+ }
+ }
+
+ KateHlManager::self()->syntax->freeGroupInfo(data);
+ }
+
+ m_additionalData[buildIdentifier]->singleLineCommentMarker = cslStart;
+ m_additionalData[buildIdentifier]->singleLineCommentPosition = cslPosition;
+ m_additionalData[buildIdentifier]->multiLineCommentStart = cmlStart;
+ m_additionalData[buildIdentifier]->multiLineCommentEnd = cmlEnd;
+ m_additionalData[buildIdentifier]->multiLineRegion = cmlRegion;
+}
+
+/**
+ * Helper for makeContextList. It parses the xml file for information,
+ * if keywords should be treated case(in)sensitive and creates the keyword
+ * delimiter list. Which is the default list, without any given weak deliminiators
+ */
+void KateHighlighting::readGlobalKeywordConfig()
+{
+ deliminator = stdDeliminator;
+ // Tell the syntax document class which file we want to parse
+ kdDebug(13010)<<"readGlobalKeywordConfig:BEGIN"<<endl;
+
+ KateHlManager::self()->syntax->setIdentifier(buildIdentifier);
+ KateSyntaxContextData *data = KateHlManager::self()->syntax->getConfig("general","keywords");
+
+ if (data)
+ {
+ kdDebug(13010)<<"Found global keyword config"<<endl;
+
+ if ( IS_TRUE( KateHlManager::self()->syntax->groupItemData(data,QString("casesensitive")) ) )
+ casesensitive=true;
+ else
+ casesensitive=false;
+
+ //get the weak deliminators
+ weakDeliminator=(KateHlManager::self()->syntax->groupItemData(data,QString("weakDeliminator")));
+
+ kdDebug(13010)<<"weak delimiters are: "<<weakDeliminator<<endl;
+
+ // remove any weakDelimitars (if any) from the default list and store this list.
+ for (uint s=0; s < weakDeliminator.length(); s++)
+ {
+ int f = deliminator.find (weakDeliminator[s]);
+
+ if (f > -1)
+ deliminator.remove (f, 1);
+ }
+
+ QString addDelim = (KateHlManager::self()->syntax->groupItemData(data,QString("additionalDeliminator")));
+
+ if (!addDelim.isEmpty())
+ deliminator=deliminator+addDelim;
+
+ KateHlManager::self()->syntax->freeGroupInfo(data);
+ }
+ else
+ {
+ //Default values
+ casesensitive=true;
+ weakDeliminator=QString("");
+ }
+
+ kdDebug(13010)<<"readGlobalKeywordConfig:END"<<endl;
+
+ kdDebug(13010)<<"delimiterCharacters are: "<<deliminator<<endl;
+
+ m_additionalData[buildIdentifier]->deliminator = deliminator;
+}
+
+/**
+ * Helper for makeContextList. It parses the xml file for any wordwrap
+ * deliminators, characters * at which line can be broken. In case no keyword
+ * tag is found in the xml file, the wordwrap deliminators list defaults to the
+ * standard denominators. In case a keyword tag is defined, but no
+ * wordWrapDeliminator attribute is specified, the deliminator list as computed
+ * in readGlobalKeywordConfig is used.
+ *
+ * @return the computed delimiter string.
+ */
+void KateHighlighting::readWordWrapConfig()
+{
+ // Tell the syntax document class which file we want to parse
+ kdDebug(13010)<<"readWordWrapConfig:BEGIN"<<endl;
+
+ KateHlManager::self()->syntax->setIdentifier(buildIdentifier);
+ KateSyntaxContextData *data = KateHlManager::self()->syntax->getConfig("general","keywords");
+
+ QString wordWrapDeliminator = stdDeliminator;
+ if (data)
+ {
+ kdDebug(13010)<<"Found global keyword config"<<endl;
+
+ wordWrapDeliminator = (KateHlManager::self()->syntax->groupItemData(data,QString("wordWrapDeliminator")));
+ //when no wordWrapDeliminator is defined use the deliminator list
+ if ( wordWrapDeliminator.length() == 0 ) wordWrapDeliminator = deliminator;
+
+ kdDebug(13010) << "word wrap deliminators are " << wordWrapDeliminator << endl;
+
+ KateHlManager::self()->syntax->freeGroupInfo(data);
+ }
+
+ kdDebug(13010)<<"readWordWrapConfig:END"<<endl;
+
+ m_additionalData[buildIdentifier]->wordWrapDeliminator = wordWrapDeliminator;
+}
+
+void KateHighlighting::readIndentationConfig()
+{
+ m_indentation = "";
+
+ KateHlManager::self()->syntax->setIdentifier(buildIdentifier);
+ KateSyntaxContextData *data = KateHlManager::self()->syntax->getConfig("general","indentation");
+
+ if (data)
+ {
+ m_indentation = (KateHlManager::self()->syntax->groupItemData(data,QString("mode")));
+
+ KateHlManager::self()->syntax->freeGroupInfo(data);
+ }
+}
+
+void KateHighlighting::readFoldingConfig()
+{
+ // Tell the syntax document class which file we want to parse
+ kdDebug(13010)<<"readfoldignConfig:BEGIN"<<endl;
+
+ KateHlManager::self()->syntax->setIdentifier(buildIdentifier);
+ KateSyntaxContextData *data = KateHlManager::self()->syntax->getConfig("general","folding");
+
+ if (data)
+ {
+ kdDebug(13010)<<"Found global keyword config"<<endl;
+
+ if ( IS_TRUE( KateHlManager::self()->syntax->groupItemData(data,QString("indentationsensitive")) ) )
+ m_foldingIndentationSensitive=true;
+ else
+ m_foldingIndentationSensitive=false;
+
+ KateHlManager::self()->syntax->freeGroupInfo(data);
+ }
+ else
+ {
+ //Default values
+ m_foldingIndentationSensitive = false;
+ }
+
+ kdDebug(13010)<<"readfoldingConfig:END"<<endl;
+
+ kdDebug(13010)<<"############################ use indent for fold are: "<<m_foldingIndentationSensitive<<endl;
+}
+
+void KateHighlighting::createContextNameList(QStringList *ContextNameList,int ctx0)
+{
+ kdDebug(13010)<<"creatingContextNameList:BEGIN"<<endl;
+
+ if (ctx0 == 0)
+ ContextNameList->clear();
+
+ KateHlManager::self()->syntax->setIdentifier(buildIdentifier);
+
+ KateSyntaxContextData *data=KateHlManager::self()->syntax->getGroupInfo("highlighting","context");
+
+ int id=ctx0;
+
+ if (data)
+ {
+ while (KateHlManager::self()->syntax->nextGroup(data))
+ {
+ QString tmpAttr=KateHlManager::self()->syntax->groupData(data,QString("name")).simplifyWhiteSpace();
+ if (tmpAttr.isEmpty())
+ {
+ tmpAttr=QString("!KATE_INTERNAL_DUMMY! %1").arg(id);
+ errorsAndWarnings +=i18n("<B>%1</B>: Deprecated syntax. Context %2 has no symbolic name<BR>").arg(buildIdentifier).arg(id-ctx0);
+ }
+ else tmpAttr=buildPrefix+tmpAttr;
+ (*ContextNameList)<<tmpAttr;
+ id++;
+ }
+ KateHlManager::self()->syntax->freeGroupInfo(data);
+ }
+ kdDebug(13010)<<"creatingContextNameList:END"<<endl;
+
+}
+
+int KateHighlighting::getIdFromString(QStringList *ContextNameList, QString tmpLineEndContext, /*NO CONST*/ QString &unres)
+{
+ unres="";
+ int context;
+ if ((tmpLineEndContext=="#stay") || (tmpLineEndContext.simplifyWhiteSpace().isEmpty()))
+ context=-1;
+
+ else if (tmpLineEndContext.startsWith("#pop"))
+ {
+ context=-1;
+ for(;tmpLineEndContext.startsWith("#pop");context--)
+ {
+ tmpLineEndContext.remove(0,4);
+ kdDebug(13010)<<"#pop found"<<endl;
+ }
+ }
+
+ else if ( tmpLineEndContext.contains("##"))
+ {
+ int o = tmpLineEndContext.find("##");
+ // FIXME at least with 'foo##bar'-style contexts the rules are picked up
+ // but the default attribute is not
+ QString tmp=tmpLineEndContext.mid(o+2);
+ if (!embeddedHls.contains(tmp)) embeddedHls.insert(tmp,KateEmbeddedHlInfo());
+ unres=tmp+':'+tmpLineEndContext.left(o);
+ context=0;
+ }
+
+ else
+ {
+ context=ContextNameList->findIndex(buildPrefix+tmpLineEndContext);
+ if (context==-1)
+ {
+ context=tmpLineEndContext.toInt();
+ errorsAndWarnings+=i18n(
+ "<B>%1</B>:Deprecated syntax. Context %2 not addressed by a symbolic name"
+ ).arg(buildIdentifier).arg(tmpLineEndContext);
+ }
+//#warning restructure this the name list storage.
+// context=context+buildContext0Offset;
+ }
+ return context;
+}
+
+/**
+ * The most important initialization function for each highlighting. It's called
+ * each time a document gets a highlighting style assigned. parses the xml file
+ * and creates a corresponding internal structure
+ */
+void KateHighlighting::makeContextList()
+{
+ if (noHl) // if this a highlighting for "normal texts" only, tere is no need for a context list creation
+ return;
+
+ embeddedHls.clear();
+ unresolvedContextReferences.clear();
+ RegionList.clear();
+ ContextNameList.clear();
+
+ // prepare list creation. To reuse as much code as possible handle this
+ // highlighting the same way as embedded onces
+ embeddedHls.insert(iName,KateEmbeddedHlInfo());
+
+ bool something_changed;
+ // the context "0" id is 0 for this hl, all embedded context "0"s have offsets
+ startctx=base_startctx=0;
+ // inform everybody that we are building the highlighting contexts and itemlists
+ building=true;
+
+ do
+ {
+ kdDebug(13010)<<"**************** Outer loop in make ContextList"<<endl;
+ kdDebug(13010)<<"**************** Hl List count:"<<embeddedHls.count()<<endl;
+ something_changed=false; //assume all "embedded" hls have already been loaded
+ for (KateEmbeddedHlInfos::const_iterator it=embeddedHls.begin(); it!=embeddedHls.end();++it)
+ {
+ if (!it.data().loaded) // we found one, we still have to load
+ {
+ kdDebug(13010)<<"**************** Inner loop in make ContextList"<<endl;
+ QString identifierToUse;
+ kdDebug(13010)<<"Trying to open highlighting definition file: "<< it.key()<<endl;
+ if (iName==it.key()) // the own identifier is known
+ identifierToUse=identifier;
+ else // all others have to be looked up
+ identifierToUse=KateHlManager::self()->identifierForName(it.key());
+
+ kdDebug(13010)<<"Location is:"<< identifierToUse<<endl;
+
+ buildPrefix=it.key()+':'; // attribute names get prefixed by the names
+ // of the highlighting definitions they belong to
+
+ if (identifierToUse.isEmpty() )
+ kdDebug(13010)<<"OHOH, unknown highlighting description referenced"<<endl;
+
+ kdDebug(13010)<<"setting ("<<it.key()<<") to loaded"<<endl;
+
+ //mark hl as loaded
+ it=embeddedHls.insert(it.key(),KateEmbeddedHlInfo(true,startctx));
+ //set class member for context 0 offset, so we don't need to pass it around
+ buildContext0Offset=startctx;
+ //parse one hl definition file
+ startctx=addToContextList(identifierToUse,startctx);
+
+ if (noHl) return; // an error occurred
+
+ base_startctx = startctx;
+ something_changed=true; // something has been loaded
+ }
+ }
+ } while (something_changed); // as long as there has been another file parsed
+ // repeat everything, there could be newly added embedded hls.
+
+
+ // at this point all needed highlighing (sub)definitions are loaded. It's time
+ // to resolve cross file references (if there are any)
+ kdDebug(13010)<<"Unresolved contexts, which need attention: "<<unresolvedContextReferences.count()<<endl;
+
+ //optimize this a littlebit
+ for (KateHlUnresolvedCtxRefs::iterator unresIt=unresolvedContextReferences.begin();
+ unresIt!=unresolvedContextReferences.end();++unresIt)
+ {
+ QString incCtx = unresIt.data();
+ kdDebug(13010)<<"Context "<<incCtx<<" is unresolved"<<endl;
+ // only resolve '##Name' contexts here; handleKateHlIncludeRules() can figure
+ // out 'Name##Name'-style inclusions, but we screw it up
+ if (incCtx.endsWith(":")) {
+ kdDebug(13010)<<"Looking up context0 for ruleset "<<incCtx<<endl;
+ incCtx = incCtx.left(incCtx.length()-1);
+ //try to find the context0 id for a given unresolvedReference
+ KateEmbeddedHlInfos::const_iterator hlIt=embeddedHls.find(incCtx);
+ if (hlIt!=embeddedHls.end())
+ *(unresIt.key())=hlIt.data().context0;
+ }
+ }
+
+ // eventually handle KateHlIncludeRules items, if they exist.
+ // This has to be done after the cross file references, because it is allowed
+ // to include the context0 from a different definition, than the one the rule
+ // belongs to
+ handleKateHlIncludeRules();
+
+ embeddedHls.clear(); //save some memory.
+ unresolvedContextReferences.clear(); //save some memory
+ RegionList.clear(); // I think you get the idea ;)
+ ContextNameList.clear();
+
+
+ // if there have been errors show them
+ if (!errorsAndWarnings.isEmpty())
+ KMessageBox::detailedSorry(0L,i18n(
+ "There were warning(s) and/or error(s) while parsing the syntax "
+ "highlighting configuration."),
+ errorsAndWarnings, i18n("Kate Syntax Highlighting Parser"));
+
+ // we have finished
+ building=false;
+}
+
+void KateHighlighting::handleKateHlIncludeRules()
+{
+ // if there are noe include rules to take care of, just return
+ kdDebug(13010)<<"KateHlIncludeRules, which need attention: " <<includeRules.count()<<endl;
+ if (includeRules.isEmpty()) return;
+
+ buildPrefix="";
+ QString dummy;
+
+ // By now the context0 references are resolved, now more or less only inner
+ // file references are resolved. If we decide that arbitrary inclusion is
+ // needed, this doesn't need to be changed, only the addToContextList
+ // method.
+
+ //resolove context names
+ for (KateHlIncludeRules::iterator it=includeRules.begin();it!=includeRules.end();)
+ {
+ if ((*it)->incCtx==-1) // context unresolved ?
+ {
+
+ if ((*it)->incCtxN.isEmpty())
+ {
+ // no context name given, and no valid context id set, so this item is
+ // going to be removed
+ KateHlIncludeRules::iterator it1=it;
+ ++it1;
+ delete (*it);
+ includeRules.remove(it);
+ it=it1;
+ }
+ else
+ {
+ // resolve name to id
+ (*it)->incCtx=getIdFromString(&ContextNameList,(*it)->incCtxN,dummy);
+ kdDebug(13010)<<"Resolved "<<(*it)->incCtxN<< " to "<<(*it)->incCtx<<" for include rule"<<endl;
+ // It would be good to look here somehow, if the result is valid
+ }
+ }
+ else ++it; //nothing to do, already resolved (by the cross defintion reference resolver)
+ }
+
+ // now that all KateHlIncludeRule items should be valid and completely resolved,
+ // do the real inclusion of the rules.
+ // recursiveness is needed, because context 0 could include context 1, which
+ // itself includes context 2 and so on.
+ // In that case we have to handle context 2 first, then 1, 0
+ //TODO: catch circular references: eg 0->1->2->3->1
+ while (!includeRules.isEmpty())
+ handleKateHlIncludeRulesRecursive(includeRules.begin(),&includeRules);
+}
+
+void KateHighlighting::handleKateHlIncludeRulesRecursive(KateHlIncludeRules::iterator it, KateHlIncludeRules *list)
+{
+ if (it==list->end()) return; //invalid iterator, shouldn't happen, but better have a rule prepared ;)
+
+ KateHlIncludeRules::iterator it1=it;
+ int ctx=(*it1)->ctx;
+
+ // find the last entry for the given context in the KateHlIncludeRules list
+ // this is need if one context includes more than one. This saves us from
+ // updating all insert positions:
+ // eg: context 0:
+ // pos 3 - include context 2
+ // pos 5 - include context 3
+ // During the building of the includeRules list the items are inserted in
+ // ascending order, now we need it descending to make our life easier.
+ while ((it!=list->end()) && ((*it)->ctx==ctx))
+ {
+ it1=it;
+ ++it;
+ }
+
+ // iterate over each include rule for the context the function has been called for.
+ while ((it1!=list->end()) && ((*it1)->ctx==ctx))
+ {
+ int ctx1=(*it1)->incCtx;
+
+ //let's see, if the the included context includes other contexts
+ for (KateHlIncludeRules::iterator it2=list->begin();it2!=list->end();++it2)
+ {
+ if ((*it2)->ctx==ctx1)
+ {
+ //yes it does, so first handle that include rules, since we want to
+ // include those subincludes too
+ handleKateHlIncludeRulesRecursive(it2,list);
+ break;
+ }
+ }
+
+ // if the context we want to include had sub includes, they are already inserted there.
+ KateHlContext *dest=m_contexts[ctx];
+ KateHlContext *src=m_contexts[ctx1];
+// kdDebug(3010)<<"linking included rules from "<<ctx<<" to "<<ctx1<<endl;
+
+ // If so desired, change the dest attribute to the one of the src.
+ // Required to make commenting work, if text matched by the included context
+ // is a different highlight than the host context.
+ if ( (*it1)->includeAttrib )
+ dest->attr = src->attr;
+
+ // insert the included context's rules starting at position p
+ int p=(*it1)->pos;
+
+ // remember some stuff
+ int oldLen = dest->items.size();
+ uint itemsToInsert = src->items.size();
+
+ // resize target
+ dest->items.resize (oldLen + itemsToInsert);
+
+ // move old elements
+ for (int i=oldLen-1; i >= p; --i)
+ dest->items[i+itemsToInsert] = dest->items[i];
+
+ // insert new stuff
+ for (uint i=0; i < itemsToInsert; ++i )
+ dest->items[p+i] = src->items[i];
+
+ it=it1; //backup the iterator
+ --it1; //move to the next entry, which has to be take care of
+ delete (*it); //free the already handled data structure
+ list->remove(it); // remove it from the list
+ }
+}
+
+/**
+ * Add one highlight to the contextlist.
+ *
+ * @return the number of contexts after this is added.
+ */
+int KateHighlighting::addToContextList(const QString &ident, int ctx0)
+{
+ kdDebug(13010)<<"=== Adding hl with ident '"<<ident<<"'"<<endl;
+
+ buildIdentifier=ident;
+ KateSyntaxContextData *data, *datasub;
+ KateHlItem *c;
+
+ QString dummy;
+
+ // Let the syntax document class know, which file we'd like to parse
+ if (!KateHlManager::self()->syntax->setIdentifier(ident))
+ {
+ noHl=true;
+ KMessageBox::information(0L,i18n(
+ "Since there has been an error parsing the highlighting description, "
+ "this highlighting will be disabled"));
+ return 0;
+ }
+
+ // only read for the own stuff
+ if (identifier == ident)
+ {
+ readIndentationConfig ();
+ }
+
+ RegionList<<"!KateInternal_TopLevel!";
+
+ m_hlIndex[internalIDList.count()] = ident;
+ m_additionalData.insert( ident, new HighlightPropertyBag );
+
+ // fill out the propertybag
+ readCommentConfig();
+ readGlobalKeywordConfig();
+ readWordWrapConfig();
+
+ readFoldingConfig ();
+
+ QString ctxName;
+
+ // This list is needed for the translation of the attribute parameter,
+ // if the itemData name is given instead of the index
+ addToKateHlItemDataList();
+ KateHlItemDataList iDl = internalIDList;
+
+ createContextNameList(&ContextNameList,ctx0);
+
+
+ kdDebug(13010)<<"Parsing Context structure"<<endl;
+ //start the real work
+ data=KateHlManager::self()->syntax->getGroupInfo("highlighting","context");
+ uint i=buildContext0Offset;
+ if (data)
+ {
+ while (KateHlManager::self()->syntax->nextGroup(data))
+ {
+ kdDebug(13010)<<"Found a context in file, building structure now"<<endl;
+ //BEGIN - Translation of the attribute parameter
+ QString tmpAttr=KateHlManager::self()->syntax->groupData(data,QString("attribute")).simplifyWhiteSpace();
+ int attr;
+ if (QString("%1").arg(tmpAttr.toInt())==tmpAttr)
+ attr=tmpAttr.toInt();
+ else
+ attr=lookupAttrName(tmpAttr,iDl);
+ //END - Translation of the attribute parameter
+
+ ctxName=buildPrefix+KateHlManager::self()->syntax->groupData(data,QString("lineEndContext")).simplifyWhiteSpace();
+
+ QString tmpLineEndContext=KateHlManager::self()->syntax->groupData(data,QString("lineEndContext")).simplifyWhiteSpace();
+ int context;
+
+ context=getIdFromString(&ContextNameList, tmpLineEndContext,dummy);
+
+ QString tmpNIBF = KateHlManager::self()->syntax->groupData(data, QString("noIndentationBasedFolding") );
+ bool noIndentationBasedFolding=IS_TRUE(tmpNIBF);
+
+ //BEGIN get fallthrough props
+ bool ft = false;
+ int ftc = 0; // fallthrough context
+ if ( i > 0 ) // fallthrough is not smart in context 0
+ {
+ QString tmpFt = KateHlManager::self()->syntax->groupData(data, QString("fallthrough") );
+ if ( IS_TRUE(tmpFt) )
+ ft = true;
+ if ( ft )
+ {
+ QString tmpFtc = KateHlManager::self()->syntax->groupData( data, QString("fallthroughContext") );
+
+ ftc=getIdFromString(&ContextNameList, tmpFtc,dummy);
+ if (ftc == -1) ftc =0;
+
+ kdDebug(13010)<<"Setting fall through context (context "<<i<<"): "<<ftc<<endl;
+ }
+ }
+ //END falltrhough props
+
+ bool dynamic = false;
+ QString tmpDynamic = KateHlManager::self()->syntax->groupData(data, QString("dynamic") );
+ if ( tmpDynamic.lower() == "true" || tmpDynamic.toInt() == 1 )
+ dynamic = true;
+
+ KateHlContext *ctxNew = new KateHlContext (
+ ident,
+ attr,
+ context,
+ (KateHlManager::self()->syntax->groupData(data,QString("lineBeginContext"))).isEmpty()?-1:
+ (KateHlManager::self()->syntax->groupData(data,QString("lineBeginContext"))).toInt(),
+ ft, ftc, dynamic,noIndentationBasedFolding);
+
+ m_contexts.push_back (ctxNew);
+
+ kdDebug(13010) << "INDEX: " << i << " LENGTH " << m_contexts.size()-1 << endl;
+
+ //Let's create all items for the context
+ while (KateHlManager::self()->syntax->nextItem(data))
+ {
+// kdDebug(13010)<< "In make Contextlist: Item:"<<endl;
+
+ // KateHlIncludeRules : add a pointer to each item in that context
+ // TODO add a attrib includeAttrib
+ QString tag = KateHlManager::self()->syntax->groupItemData(data,QString(""));
+ if ( tag == "IncludeRules" ) //if the new item is an Include rule, we have to take special care
+ {
+ QString incCtx = KateHlManager::self()->syntax->groupItemData( data, QString("context"));
+ QString incAttrib = KateHlManager::self()->syntax->groupItemData( data, QString("includeAttrib"));
+ bool includeAttrib = IS_TRUE( incAttrib );
+ // only context refernces of type Name, ##Name, and Subname##Name are allowed
+ if (incCtx.startsWith("##") || (!incCtx.startsWith("#")))
+ {
+ int incCtxi = incCtx.find("##");
+ //#stay, #pop is not interesting here
+ if (incCtxi >= 0)
+ {
+ QString incSet = incCtx.mid(incCtxi + 2);
+ QString incCtxN = incSet + ":" + incCtx.left(incCtxi);
+
+ //a cross highlighting reference
+ kdDebug(13010)<<"Cross highlight reference <IncludeRules>, context "<<incCtxN<<endl;
+ KateHlIncludeRule *ir=new KateHlIncludeRule(i,m_contexts[i]->items.count(),incCtxN,includeAttrib);
+
+ //use the same way to determine cross hl file references as other items do
+ if (!embeddedHls.contains(incSet))
+ embeddedHls.insert(incSet,KateEmbeddedHlInfo());
+ else
+ kdDebug(13010)<<"Skipping embeddedHls.insert for "<<incCtxN<<endl;
+
+ unresolvedContextReferences.insert(&(ir->incCtx), incCtxN);
+
+ includeRules.append(ir);
+ }
+ else
+ {
+ // a local reference -> just initialize the include rule structure
+ incCtx=buildPrefix+incCtx.simplifyWhiteSpace();
+ includeRules.append(new KateHlIncludeRule(i,m_contexts[i]->items.count(),incCtx, includeAttrib));
+ }
+ }
+
+ continue;
+ }
+ // TODO -- can we remove the block below??
+#if 0
+ QString tag = KateHlManager::self()->syntax->groupKateHlItemData(data,QString(""));
+ if ( tag == "IncludeRules" ) {
+ // attrib context: the index (jowenn, i think using names here
+ // would be a cool feat, goes for mentioning the context in
+ // any item. a map or dict?)
+ int ctxId = getIdFromString(&ContextNameList,
+ KateHlManager::self()->syntax->groupKateHlItemData( data, QString("context")),dummy); // the index is *required*
+ if ( ctxId > -1) { // we can even reuse rules of 0 if we want to:)
+ kdDebug(13010)<<"makeContextList["<<i<<"]: including all items of context "<<ctxId<<endl;
+ if ( ctxId < (int) i ) { // must be defined
+ for ( c = m_contexts[ctxId]->items.first(); c; c = m_contexts[ctxId]->items.next() )
+ m_contexts[i]->items.append(c);
+ }
+ else
+ kdDebug(13010)<<"Context "<<ctxId<<"not defined. You can not include the rules of an undefined context"<<endl;
+ }
+ continue; // while nextItem
+ }
+#endif
+ c=createKateHlItem(data,iDl,&RegionList,&ContextNameList);
+ if (c)
+ {
+ m_contexts[i]->items.append(c);
+
+ // Not supported completely atm and only one level. Subitems.(all have
+ // to be matched to at once)
+ datasub=KateHlManager::self()->syntax->getSubItems(data);
+ bool tmpbool;
+ if (tmpbool=KateHlManager::self()->syntax->nextItem(datasub))
+ {
+ for (;tmpbool;tmpbool=KateHlManager::self()->syntax->nextItem(datasub))
+ {
+ c->subItems.resize (c->subItems.size()+1);
+ c->subItems[c->subItems.size()-1] = createKateHlItem(datasub,iDl,&RegionList,&ContextNameList);
+ } }
+ KateHlManager::self()->syntax->freeGroupInfo(datasub);
+ // end of sublevel
+ }
+ }
+ i++;
+ }
+ }
+
+ KateHlManager::self()->syntax->freeGroupInfo(data);
+
+ if (RegionList.count()!=1)
+ folding=true;
+
+ folding = folding || m_foldingIndentationSensitive;
+
+ //BEGIN Resolve multiline region if possible
+ if (!m_additionalData[ ident ]->multiLineRegion.isEmpty()) {
+ long commentregionid=RegionList.findIndex( m_additionalData[ ident ]->multiLineRegion );
+ if (-1==commentregionid) {
+ errorsAndWarnings+=i18n(
+ "<B>%1</B>: Specified multiline comment region (%2) could not be resolved<BR>"
+ ).arg(buildIdentifier).arg( m_additionalData[ ident ]->multiLineRegion );
+ m_additionalData[ ident ]->multiLineRegion = QString();
+ kdDebug(13010)<<"ERROR comment region attribute could not be resolved"<<endl;
+
+ } else {
+ m_additionalData[ ident ]->multiLineRegion=QString::number(commentregionid+1);
+ kdDebug(13010)<<"comment region resolved to:"<<m_additionalData[ ident ]->multiLineRegion<<endl;
+ }
+ }
+ //END Resolve multiline region if possible
+ return i;
+}
+
+void KateHighlighting::clearAttributeArrays ()
+{
+ for ( QIntDictIterator< QMemArray<KateAttribute> > it( m_attributeArrays ); it.current(); ++it )
+ {
+ // k, schema correct, let create the data
+ KateAttributeList defaultStyleList;
+ defaultStyleList.setAutoDelete(true);
+ KateHlManager::self()->getDefaults(it.currentKey(), defaultStyleList);
+
+ KateHlItemDataList itemDataList;
+ getKateHlItemDataList(it.currentKey(), itemDataList);
+
+ uint nAttribs = itemDataList.count();
+ QMemArray<KateAttribute> *array = it.current();
+ array->resize (nAttribs);
+
+ for (uint z = 0; z < nAttribs; z++)
+ {
+ KateHlItemData *itemData = itemDataList.at(z);
+ KateAttribute n = *defaultStyleList.at(itemData->defStyleNum);
+
+ if (itemData && itemData->isSomethingSet())
+ n += *itemData;
+
+ array->at(z) = n;
+ }
+ }
+}
+
+QMemArray<KateAttribute> *KateHighlighting::attributes (uint schema)
+{
+ QMemArray<KateAttribute> *array;
+
+ // found it, allready floating around
+ if ((array = m_attributeArrays[schema]))
+ return array;
+
+ // ohh, not found, check if valid schema number
+ if (!KateFactory::self()->schemaManager()->validSchema(schema))
+ {
+ // uhh, not valid :/, stick with normal default schema, it's always there !
+ return attributes (0);
+ }
+
+ // k, schema correct, let create the data
+ KateAttributeList defaultStyleList;
+ defaultStyleList.setAutoDelete(true);
+ KateHlManager::self()->getDefaults(schema, defaultStyleList);
+
+ KateHlItemDataList itemDataList;
+ getKateHlItemDataList(schema, itemDataList);
+
+ uint nAttribs = itemDataList.count();
+ array = new QMemArray<KateAttribute> (nAttribs);
+
+ for (uint z = 0; z < nAttribs; z++)
+ {
+ KateHlItemData *itemData = itemDataList.at(z);
+ KateAttribute n = *defaultStyleList.at(itemData->defStyleNum);
+
+ if (itemData && itemData->isSomethingSet())
+ n += *itemData;
+
+ array->at(z) = n;
+ }
+
+ m_attributeArrays.insert(schema, array);
+
+ return array;
+}
+
+void KateHighlighting::getKateHlItemDataListCopy (uint schema, KateHlItemDataList &outlist)
+{
+ KateHlItemDataList itemDataList;
+ getKateHlItemDataList(schema, itemDataList);
+
+ outlist.clear ();
+ outlist.setAutoDelete (true);
+ for (uint z=0; z < itemDataList.count(); z++)
+ outlist.append (new KateHlItemData (*itemDataList.at(z)));
+}
+
+//END
+
+//BEGIN KateHlManager
+KateHlManager::KateHlManager()
+ : QObject()
+ , m_config ("katesyntaxhighlightingrc", false, false)
+ , commonSuffixes (QStringList::split(";", ".orig;.new;~;.bak;.BAK"))
+ , syntax (new KateSyntaxDocument())
+ , dynamicCtxsCount(0)
+ , forceNoDCReset(false)
+{
+ hlList.setAutoDelete(true);
+ hlDict.setAutoDelete(false);
+
+ KateSyntaxModeList modeList = syntax->modeList();
+ for (uint i=0; i < modeList.count(); i++)
+ {
+ KateHighlighting *hl = new KateHighlighting(modeList[i]);
+
+ uint insert = 0;
+ for (; insert <= hlList.count(); insert++)
+ {
+ if (insert == hlList.count())
+ break;
+
+ if ( QString(hlList.at(insert)->section() + hlList.at(insert)->nameTranslated()).lower()
+ > QString(hl->section() + hl->nameTranslated()).lower() )
+ break;
+ }
+
+ hlList.insert (insert, hl);
+ hlDict.insert (hl->name(), hl);
+ }
+
+ // Normal HL
+ KateHighlighting *hl = new KateHighlighting(0);
+ hlList.prepend (hl);
+ hlDict.insert (hl->name(), hl);
+
+ lastCtxsReset.start();
+}
+
+KateHlManager::~KateHlManager()
+{
+ delete syntax;
+}
+
+static KStaticDeleter<KateHlManager> sdHlMan;
+
+KateHlManager *KateHlManager::self()
+{
+ if ( !s_self )
+ sdHlMan.setObject(s_self, new KateHlManager ());
+
+ return s_self;
+}
+
+KateHighlighting *KateHlManager::getHl(int n)
+{
+ if (n < 0 || n >= (int) hlList.count())
+ n = 0;
+
+ return hlList.at(n);
+}
+
+int KateHlManager::nameFind(const QString &name)
+{
+ int z (hlList.count() - 1);
+ for (; z > 0; z--)
+ if (hlList.at(z)->name() == name)
+ return z;
+
+ return z;
+}
+
+int KateHlManager::detectHighlighting (KateDocument *doc)
+{
+ int hl = wildcardFind( doc->url().filename() );
+ if ( hl < 0 )
+ hl = mimeFind ( doc );
+
+ return hl;
+}
+
+int KateHlManager::wildcardFind(const QString &fileName)
+{
+ int result = -1;
+ if ((result = realWildcardFind(fileName)) != -1)
+ return result;
+
+ int length = fileName.length();
+ QString backupSuffix = KateDocumentConfig::global()->backupSuffix();
+ if (fileName.endsWith(backupSuffix)) {
+ if ((result = realWildcardFind(fileName.left(length - backupSuffix.length()))) != -1)
+ return result;
+ }
+
+ for (QStringList::Iterator it = commonSuffixes.begin(); it != commonSuffixes.end(); ++it) {
+ if (*it != backupSuffix && fileName.endsWith(*it)) {
+ if ((result = realWildcardFind(fileName.left(length - (*it).length()))) != -1)
+ return result;
+ }
+ }
+
+ return -1;
+}
+
+int KateHlManager::realWildcardFind(const QString &fileName)
+{
+ static QRegExp sep("\\s*;\\s*");
+
+ QPtrList<KateHighlighting> highlights;
+
+ for (KateHighlighting *highlight = hlList.first(); highlight != 0L; highlight = hlList.next()) {
+ highlight->loadWildcards();
+
+ for (QStringList::Iterator it = highlight->getPlainExtensions().begin(); it != highlight->getPlainExtensions().end(); ++it)
+ if (fileName.endsWith((*it)))
+ highlights.append(highlight);
+
+ for (int i = 0; i < (int)highlight->getRegexpExtensions().count(); i++) {
+ QRegExp re = highlight->getRegexpExtensions()[i];
+ if (re.exactMatch(fileName))
+ highlights.append(highlight);
+ }
+ }
+
+ if ( !highlights.isEmpty() )
+ {
+ int pri = -1;
+ int hl = -1;
+
+ for (KateHighlighting *highlight = highlights.first(); highlight != 0L; highlight = highlights.next())
+ {
+ if (highlight->priority() > pri)
+ {
+ pri = highlight->priority();
+ hl = hlList.findRef (highlight);
+ }
+ }
+ return hl;
+ }
+
+ return -1;
+}
+
+int KateHlManager::mimeFind( KateDocument *doc )
+{
+ static QRegExp sep("\\s*;\\s*");
+
+ KMimeType::Ptr mt = doc->mimeTypeForContent();
+
+ QPtrList<KateHighlighting> highlights;
+
+ for (KateHighlighting *highlight = hlList.first(); highlight != 0L; highlight = hlList.next())
+ {
+ QStringList l = QStringList::split( sep, highlight->getMimetypes() );
+
+ for( QStringList::Iterator it = l.begin(); it != l.end(); ++it )
+ {
+ if ( *it == mt->name() ) // faster than a regexp i guess?
+ highlights.append (highlight);
+ }
+ }
+
+ if ( !highlights.isEmpty() )
+ {
+ int pri = -1;
+ int hl = -1;
+
+ for (KateHighlighting *highlight = highlights.first(); highlight != 0L; highlight = highlights.next())
+ {
+ if (highlight->priority() > pri)
+ {
+ pri = highlight->priority();
+ hl = hlList.findRef (highlight);
+ }
+ }
+
+ return hl;
+ }
+
+ return -1;
+}
+
+uint KateHlManager::defaultStyles()
+{
+ return 14;
+}
+
+QString KateHlManager::defaultStyleName(int n, bool translateNames)
+{
+ static QStringList names;
+ static QStringList translatedNames;
+
+ if (names.isEmpty())
+ {
+ names << "Normal";
+ names << "Keyword";
+ names << "Data Type";
+ names << "Decimal/Value";
+ names << "Base-N Integer";
+ names << "Floating Point";
+ names << "Character";
+ names << "String";
+ names << "Comment";
+ names << "Others";
+ names << "Alert";
+ names << "Function";
+ // this next one is for denoting the beginning/end of a user defined folding region
+ names << "Region Marker";
+ // this one is for marking invalid input
+ names << "Error";
+
+ translatedNames << i18n("Normal");
+ translatedNames << i18n("Keyword");
+ translatedNames << i18n("Data Type");
+ translatedNames << i18n("Decimal/Value");
+ translatedNames << i18n("Base-N Integer");
+ translatedNames << i18n("Floating Point");
+ translatedNames << i18n("Character");
+ translatedNames << i18n("String");
+ translatedNames << i18n("Comment");
+ translatedNames << i18n("Others");
+ translatedNames << i18n("Alert");
+ translatedNames << i18n("Function");
+ // this next one is for denoting the beginning/end of a user defined folding region
+ translatedNames << i18n("Region Marker");
+ // this one is for marking invalid input
+ translatedNames << i18n("Error");
+ }
+
+ return translateNames ? translatedNames[n] : names[n];
+}
+
+void KateHlManager::getDefaults(uint schema, KateAttributeList &list)
+{
+ list.setAutoDelete(true);
+
+ KateAttribute* normal = new KateAttribute();
+ normal->setTextColor(Qt::black);
+ normal->setSelectedTextColor(Qt::white);
+ list.append(normal);
+
+ KateAttribute* keyword = new KateAttribute();
+ keyword->setTextColor(Qt::black);
+ keyword->setSelectedTextColor(Qt::white);
+ keyword->setBold(true);
+ list.append(keyword);
+
+ KateAttribute* dataType = new KateAttribute();
+ dataType->setTextColor(Qt::darkRed);
+ dataType->setSelectedTextColor(Qt::white);
+ list.append(dataType);
+
+ KateAttribute* decimal = new KateAttribute();
+ decimal->setTextColor(Qt::blue);
+ decimal->setSelectedTextColor(Qt::cyan);
+ list.append(decimal);
+
+ KateAttribute* basen = new KateAttribute();
+ basen->setTextColor(Qt::darkCyan);
+ basen->setSelectedTextColor(Qt::cyan);
+ list.append(basen);
+
+ KateAttribute* floatAttribute = new KateAttribute();
+ floatAttribute->setTextColor(Qt::darkMagenta);
+ floatAttribute->setSelectedTextColor(Qt::cyan);
+ list.append(floatAttribute);
+
+ KateAttribute* charAttribute = new KateAttribute();
+ charAttribute->setTextColor(Qt::magenta);
+ charAttribute->setSelectedTextColor(Qt::magenta);
+ list.append(charAttribute);
+
+ KateAttribute* string = new KateAttribute();
+ string->setTextColor(QColor::QColor("#D00"));
+ string->setSelectedTextColor(Qt::red);
+ list.append(string);
+
+ KateAttribute* comment = new KateAttribute();
+ comment->setTextColor(Qt::darkGray);
+ comment->setSelectedTextColor(Qt::gray);
+ comment->setItalic(true);
+ list.append(comment);
+
+ KateAttribute* others = new KateAttribute();
+ others->setTextColor(Qt::darkGreen);
+ others->setSelectedTextColor(Qt::green);
+ list.append(others);
+
+ KateAttribute* alert = new KateAttribute();
+ alert->setTextColor(Qt::black);
+ alert->setSelectedTextColor( QColor::QColor("#FCC") );
+ alert->setBold(true);
+ alert->setBGColor( QColor::QColor("#FCC") );
+ list.append(alert);
+
+ KateAttribute* functionAttribute = new KateAttribute();
+ functionAttribute->setTextColor(Qt::darkBlue);
+ functionAttribute->setSelectedTextColor(Qt::white);
+ list.append(functionAttribute);
+
+ KateAttribute* regionmarker = new KateAttribute();
+ regionmarker->setTextColor(Qt::white);
+ regionmarker->setBGColor(Qt::gray);
+ regionmarker->setSelectedTextColor(Qt::gray);
+ list.append(regionmarker);
+
+ KateAttribute* error = new KateAttribute();
+ error->setTextColor(Qt::red);
+ error->setUnderline(true);
+ error->setSelectedTextColor(Qt::red);
+ list.append(error);
+
+ KConfig *config = KateHlManager::self()->self()->getKConfig();
+ config->setGroup("Default Item Styles - Schema " + KateFactory::self()->schemaManager()->name(schema));
+
+ for (uint z = 0; z < defaultStyles(); z++)
+ {
+ KateAttribute *i = list.at(z);
+ QStringList s = config->readListEntry(defaultStyleName(z));
+ if (!s.isEmpty())
+ {
+ while( s.count()<8)
+ s << "";
+
+ QString tmp;
+ QRgb col;
+
+ tmp=s[0]; if (!tmp.isEmpty()) {
+ col=tmp.toUInt(0,16); i->setTextColor(col); }
+
+ tmp=s[1]; if (!tmp.isEmpty()) {
+ col=tmp.toUInt(0,16); i->setSelectedTextColor(col); }
+
+ tmp=s[2]; if (!tmp.isEmpty()) i->setBold(tmp!="0");
+
+ tmp=s[3]; if (!tmp.isEmpty()) i->setItalic(tmp!="0");
+
+ tmp=s[4]; if (!tmp.isEmpty()) i->setStrikeOut(tmp!="0");
+
+ tmp=s[5]; if (!tmp.isEmpty()) i->setUnderline(tmp!="0");
+
+ tmp=s[6]; if (!tmp.isEmpty()) {
+ if ( tmp != "-" )
+ {
+ col=tmp.toUInt(0,16);
+ i->setBGColor(col);
+ }
+ else
+ i->clearAttribute(KateAttribute::BGColor);
+ }
+ tmp=s[7]; if (!tmp.isEmpty()) {
+ if ( tmp != "-" )
+ {
+ col=tmp.toUInt(0,16);
+ i->setSelectedBGColor(col);
+ }
+ else
+ i->clearAttribute(KateAttribute::SelectedBGColor);
+ }
+ }
+ }
+}
+
+void KateHlManager::setDefaults(uint schema, KateAttributeList &list)
+{
+ KConfig *config = KateHlManager::self()->self()->getKConfig();
+ config->setGroup("Default Item Styles - Schema " + KateFactory::self()->schemaManager()->name(schema));
+
+ for (uint z = 0; z < defaultStyles(); z++)
+ {
+ QStringList settings;
+ KateAttribute *i = list.at(z);
+
+ settings<<(i->itemSet(KateAttribute::TextColor)?QString::number(i->textColor().rgb(),16):"");
+ settings<<(i->itemSet(KateAttribute::SelectedTextColor)?QString::number(i->selectedTextColor().rgb(),16):"");
+ settings<<(i->itemSet(KateAttribute::Weight)?(i->bold()?"1":"0"):"");
+ settings<<(i->itemSet(KateAttribute::Italic)?(i->italic()?"1":"0"):"");
+ settings<<(i->itemSet(KateAttribute::StrikeOut)?(i->strikeOut()?"1":"0"):"");
+ settings<<(i->itemSet(KateAttribute::Underline)?(i->underline()?"1":"0"):"");
+ settings<<(i->itemSet(KateAttribute::BGColor)?QString::number(i->bgColor().rgb(),16):"-");
+ settings<<(i->itemSet(KateAttribute::SelectedBGColor)?QString::number(i->selectedBGColor().rgb(),16):"-");
+ settings<<"---";
+
+ config->writeEntry(defaultStyleName(z),settings);
+ }
+
+ emit changed();
+}
+
+int KateHlManager::highlights()
+{
+ return (int) hlList.count();
+}
+
+QString KateHlManager::hlName(int n)
+{
+ return hlList.at(n)->name();
+}
+
+QString KateHlManager::hlNameTranslated(int n)
+{
+ return hlList.at(n)->nameTranslated();
+}
+
+QString KateHlManager::hlSection(int n)
+{
+ return hlList.at(n)->section();
+}
+
+bool KateHlManager::hlHidden(int n)
+{
+ return hlList.at(n)->hidden();
+}
+
+QString KateHlManager::identifierForName(const QString& name)
+{
+ KateHighlighting *hl = 0;
+
+ if ((hl = hlDict[name]))
+ return hl->getIdentifier ();
+
+ return QString();
+}
+
+bool KateHlManager::resetDynamicCtxs()
+{
+ if (forceNoDCReset)
+ return false;
+
+ if (lastCtxsReset.elapsed() < KATE_DYNAMIC_CONTEXTS_RESET_DELAY)
+ return false;
+
+ KateHighlighting *hl;
+ for (hl = hlList.first(); hl; hl = hlList.next())
+ hl->dropDynamicContexts();
+
+ dynamicCtxsCount = 0;
+ lastCtxsReset.start();
+
+ return true;
+}
+//END
+
+//BEGIN KateHighlightAction
+void KateViewHighlightAction::init()
+{
+ m_doc = 0;
+ subMenus.setAutoDelete( true );
+
+ connect(popupMenu(),SIGNAL(aboutToShow()),this,SLOT(slotAboutToShow()));
+}
+
+void KateViewHighlightAction::updateMenu (Kate::Document *doc)
+{
+ m_doc = doc;
+}
+
+void KateViewHighlightAction::slotAboutToShow()
+{
+ Kate::Document *doc=m_doc;
+ int count = KateHlManager::self()->highlights();
+
+ for (int z=0; z<count; z++)
+ {
+ QString hlName = KateHlManager::self()->hlNameTranslated (z);
+ QString hlSection = KateHlManager::self()->hlSection (z);
+
+ if (!KateHlManager::self()->hlHidden(z))
+ {
+ if ( !hlSection.isEmpty() && (names.contains(hlName) < 1) )
+ {
+ if (subMenusName.contains(hlSection) < 1)
+ {
+ subMenusName << hlSection;
+ QPopupMenu *menu = new QPopupMenu ();
+ subMenus.append(menu);
+ popupMenu()->insertItem ( '&' + hlSection, menu);
+ }
+
+ int m = subMenusName.findIndex (hlSection);
+ names << hlName;
+ subMenus.at(m)->insertItem ( '&' + hlName, this, SLOT(setHl(int)), 0, z);
+ }
+ else if (names.contains(hlName) < 1)
+ {
+ names << hlName;
+ popupMenu()->insertItem ( '&' + hlName, this, SLOT(setHl(int)), 0, z);
+ }
+ }
+ }
+
+ if (!doc) return;
+
+ for (uint i=0;i<subMenus.count();i++)
+ {
+ for (uint i2=0;i2<subMenus.at(i)->count();i2++)
+ {
+ subMenus.at(i)->setItemChecked(subMenus.at(i)->idAt(i2),false);
+ }
+ }
+ popupMenu()->setItemChecked (0, false);
+
+ int i = subMenusName.findIndex (KateHlManager::self()->hlSection(doc->hlMode()));
+ if (i >= 0 && subMenus.at(i))
+ subMenus.at(i)->setItemChecked (doc->hlMode(), true);
+ else
+ popupMenu()->setItemChecked (0, true);
+}
+
+void KateViewHighlightAction::setHl (int mode)
+{
+ Kate::Document *doc=m_doc;
+
+ if (doc)
+ doc->setHlMode((uint)mode);
+}
+//END KateViewHighlightAction
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katehighlight.h b/kate/part/katehighlight.h
new file mode 100644
index 000000000..70b7016a3
--- /dev/null
+++ b/kate/part/katehighlight.h
@@ -0,0 +1,438 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2001,2002 Joseph Wenninger <jowenn@kde.org>
+ Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KATE_HIGHLIGHT_H__
+#define __KATE_HIGHLIGHT_H__
+
+#include "katetextline.h"
+#include "kateattribute.h"
+
+#include "../interfaces/document.h"
+
+#include <kconfig.h>
+
+#include <qptrlist.h>
+#include <qvaluelist.h>
+#include <qvaluevector.h>
+#include <qregexp.h>
+#include <qdict.h>
+#include <qintdict.h>
+#include <qmap.h>
+#include <qobject.h>
+#include <qstringlist.h>
+#include <qguardedptr.h>
+#include <qdatetime.h>
+#include <qpopupmenu.h>
+
+class KateHlContext;
+class KateHlItem;
+class KateHlItemData;
+class KateHlData;
+class KateEmbeddedHlInfo;
+class KateHlIncludeRule;
+class KateSyntaxDocument;
+class KateTextLine;
+class KateSyntaxModeListItem;
+class KateSyntaxContextData;
+
+// some typedefs
+typedef QPtrList<KateAttribute> KateAttributeList;
+typedef QValueList<KateHlIncludeRule*> KateHlIncludeRules;
+typedef QPtrList<KateHlItemData> KateHlItemDataList;
+typedef QPtrList<KateHlData> KateHlDataList;
+typedef QMap<QString,KateEmbeddedHlInfo> KateEmbeddedHlInfos;
+typedef QMap<int*,QString> KateHlUnresolvedCtxRefs;
+typedef QValueList<int> IntList;
+
+//Item Properties: name, Item Style, Item Font
+class KateHlItemData : public KateAttribute
+{
+ public:
+ KateHlItemData(const QString name, int defStyleNum);
+
+ enum ItemStyles {
+ dsNormal,
+ dsKeyword,
+ dsDataType,
+ dsDecVal,
+ dsBaseN,
+ dsFloat,
+ dsChar,
+ dsString,
+ dsComment,
+ dsOthers,
+ dsAlert,
+ dsFunction,
+ dsRegionMarker,
+ dsError };
+
+ public:
+ const QString name;
+ int defStyleNum;
+};
+
+class KateHlData
+{
+ public:
+ KateHlData(const QString &wildcards, const QString &mimetypes,const QString &identifier, int priority);
+
+ public:
+ QString wildcards;
+ QString mimetypes;
+ QString identifier;
+ int priority;
+};
+
+class KateHighlighting
+{
+ public:
+ KateHighlighting(const KateSyntaxModeListItem *def);
+ ~KateHighlighting();
+
+ public:
+ void doHighlight ( KateTextLine *prevLine,
+ KateTextLine *textLine,
+ QMemArray<uint> *foldingList,
+ bool *ctxChanged );
+
+ void loadWildcards();
+ QValueList<QRegExp>& getRegexpExtensions();
+ QStringList& getPlainExtensions();
+
+ QString getMimetypes();
+
+ // this pointer needs to be deleted !!!!!!!!!!
+ KateHlData *getData();
+ void setData(KateHlData *);
+
+ void setKateHlItemDataList(uint schema, KateHlItemDataList &);
+
+ // both methodes return hard copies of the internal lists
+ // the lists are cleared first + autodelete is set !
+ // keep track that you delete them, or mem will be lost
+ void getKateHlItemDataListCopy (uint schema, KateHlItemDataList &);
+
+ const QString &name() const {return iName;}
+ const QString &nameTranslated() const {return iNameTranslated;}
+ const QString &section() const {return iSection;}
+ bool hidden() const {return iHidden;}
+ const QString &version() const {return iVersion;}
+ const QString &author () const { return iAuthor; }
+ const QString &license () const { return iLicense; }
+ int priority();
+ const QString &getIdentifier() const {return identifier;}
+ void use();
+ void release();
+
+ /**
+ * @return true if the character @p c is not a deliminator character
+ * for the corresponding highlight.
+ */
+ bool isInWord( QChar c, int attrib=0 ) const;
+
+ /**
+ * @return true if the character @p c is a wordwrap deliminator as specified
+ * in the general keyword section of the xml file.
+ */
+ bool canBreakAt( QChar c, int attrib=0 ) const;
+
+ /**
+ * @return true if @p beginAttr and @p endAttr are members of the same
+ * highlight, and there are comment markers of either type in that.
+ */
+ bool canComment( int startAttr, int endAttr ) const;
+
+ /**
+ * @return 0 if highlighting which attr is a member of does not
+ * define a comment region, otherwise the region is returned
+ */
+ signed char commentRegion(int attr) const;
+
+ /**
+ * @return the mulitiline comment start marker for the highlight
+ * corresponding to @p attrib.
+ */
+ QString getCommentStart( int attrib=0 ) const;
+
+ /**
+ * @return the muiltiline comment end marker for the highlight corresponding
+ * to @p attrib.
+ */
+ QString getCommentEnd( int attrib=0 ) const;
+
+ /**
+ * @return the single comment marker for the highlight corresponding
+ * to @p attrib.
+ */
+ QString getCommentSingleLineStart( int attrib=0 ) const;
+
+
+ /**
+ * This enum is used for storing the information where a single line comment marker should be inserted
+ */
+ enum CSLPos { CSLPosColumn0=0,CSLPosAfterWhitespace=1};
+
+ /**
+ * @return the single comment marker position for the highlight corresponding
+ * to @p attrib.
+ */
+ CSLPos getCommentSingleLinePosition( int attrib=0 ) const;
+
+ /**
+ * @return the attribute for @p context.
+ */
+ int attribute( int context ) const;
+
+ /**
+ * map attribute to its highlighting file.
+ * the returned string is used as key for m_additionalData.
+ */
+ QString hlKeyForAttrib( int attrib ) const;
+
+
+ void clearAttributeArrays ();
+
+ QMemArray<KateAttribute> *attributes (uint schema);
+
+ inline bool noHighlighting () const { return noHl; };
+
+ // be carefull: all documents hl should be invalidated after calling this method!
+ void dropDynamicContexts();
+
+ QString indentation () { return m_indentation; }
+
+ private:
+ // make this private, nobody should play with the internal data pointers
+ void getKateHlItemDataList(uint schema, KateHlItemDataList &);
+
+ void init();
+ void done();
+ void makeContextList ();
+ int makeDynamicContext(KateHlContext *model, const QStringList *args);
+ void handleKateHlIncludeRules ();
+ void handleKateHlIncludeRulesRecursive(KateHlIncludeRules::iterator it, KateHlIncludeRules *list);
+ int addToContextList(const QString &ident, int ctx0);
+ void addToKateHlItemDataList();
+ void createKateHlItemData (KateHlItemDataList &list);
+ void readGlobalKeywordConfig();
+ void readWordWrapConfig();
+ void readCommentConfig();
+ void readIndentationConfig ();
+ void readFoldingConfig ();
+
+ // manipulates the ctxs array directly ;)
+ void generateContextStack(int *ctxNum, int ctx, QMemArray<short> *ctxs, int *posPrevLine);
+
+ KateHlItem *createKateHlItem(KateSyntaxContextData *data, KateHlItemDataList &iDl, QStringList *RegionList, QStringList *ContextList);
+ int lookupAttrName(const QString& name, KateHlItemDataList &iDl);
+
+ void createContextNameList(QStringList *ContextNameList, int ctx0);
+ int getIdFromString(QStringList *ContextNameList, QString tmpLineEndContext,/*NO CONST*/ QString &unres);
+
+ KateHlItemDataList internalIDList;
+
+ QValueVector<KateHlContext*> m_contexts;
+ inline KateHlContext *contextNum (uint n) { if (n < m_contexts.size()) return m_contexts[n]; return 0; }
+
+ QMap< QPair<KateHlContext *, QString>, short> dynamicCtxs;
+
+ // make them pointers perhaps
+ KateEmbeddedHlInfos embeddedHls;
+ KateHlUnresolvedCtxRefs unresolvedContextReferences;
+ QStringList RegionList;
+ QStringList ContextNameList;
+
+ bool noHl;
+ bool folding;
+ bool casesensitive;
+ QString weakDeliminator;
+ QString deliminator;
+
+ QString iName;
+ QString iNameTranslated;
+ QString iSection;
+ bool iHidden;
+ QString iWildcards;
+ QString iMimetypes;
+ QString identifier;
+ QString iVersion;
+ QString iAuthor;
+ QString iLicense;
+ QString m_indentation;
+ int m_priority;
+ int refCount;
+ int startctx, base_startctx;
+
+ QString errorsAndWarnings;
+ QString buildIdentifier;
+ QString buildPrefix;
+ bool building;
+ uint itemData0;
+ uint buildContext0Offset;
+ KateHlIncludeRules includeRules;
+ bool m_foldingIndentationSensitive;
+
+ QIntDict< QMemArray<KateAttribute> > m_attributeArrays;
+
+
+ /**
+ * This class holds the additional properties for one highlight
+ * definition, such as comment strings, deliminators etc.
+ *
+ * When a highlight is added, a instance of this class is appended to
+ * m_additionalData, and the current position in the attrib and context
+ * arrays are stored in the indexes for look up. You can then use
+ * hlKeyForAttrib or hlKeyForContext to find the relevant instance of this
+ * class from m_additionalData.
+ *
+ * If you need to add a property to a highlight, add it here.
+ */
+ class HighlightPropertyBag {
+ public:
+ QString singleLineCommentMarker;
+ QString multiLineCommentStart;
+ QString multiLineCommentEnd;
+ QString multiLineRegion;
+ CSLPos singleLineCommentPosition;
+ QString deliminator;
+ QString wordWrapDeliminator;
+ };
+
+ /**
+ * Highlight properties for each included highlight definition.
+ * The key is the identifier
+ */
+ QDict<HighlightPropertyBag> m_additionalData;
+
+ /**
+ * Fast lookup of hl properties, based on attribute index
+ * The key is the starting index in the attribute array for each file.
+ * @see hlKeyForAttrib
+ */
+ QMap<int, QString> m_hlIndex;
+
+
+ QString extensionSource;
+ QValueList<QRegExp> regexpExtensions;
+ QStringList plainExtensions;
+
+ public:
+ inline bool foldingIndentationSensitive () { return m_foldingIndentationSensitive; }
+ inline bool allowsFolding(){return folding;}
+};
+
+class KateHlManager : public QObject
+{
+ Q_OBJECT
+
+ private:
+ KateHlManager();
+
+ public:
+ ~KateHlManager();
+
+ static KateHlManager *self();
+
+ inline KConfig *getKConfig() { return &m_config; };
+
+ KateHighlighting *getHl(int n);
+ int nameFind(const QString &name);
+
+ int detectHighlighting (class KateDocument *doc);
+
+ int findHl(KateHighlighting *h) {return hlList.find(h);}
+ QString identifierForName(const QString&);
+
+ // methodes to get the default style count + names
+ static uint defaultStyles();
+ static QString defaultStyleName(int n, bool translateNames = false);
+
+ void getDefaults(uint schema, KateAttributeList &);
+ void setDefaults(uint schema, KateAttributeList &);
+
+ int highlights();
+ QString hlName(int n);
+ QString hlNameTranslated (int n);
+ QString hlSection(int n);
+ bool hlHidden(int n);
+
+ void incDynamicCtxs() { ++dynamicCtxsCount; };
+ uint countDynamicCtxs() { return dynamicCtxsCount; };
+ void setForceNoDCReset(bool b) { forceNoDCReset = b; };
+
+ // be carefull: all documents hl should be invalidated after having successfully called this method!
+ bool resetDynamicCtxs();
+
+ signals:
+ void changed();
+
+ private:
+ int wildcardFind(const QString &fileName);
+ int mimeFind(KateDocument *);
+ int realWildcardFind(const QString &fileName);
+
+ private:
+ friend class KateHighlighting;
+
+ QPtrList<KateHighlighting> hlList;
+ QDict<KateHighlighting> hlDict;
+
+ static KateHlManager *s_self;
+
+ KConfig m_config;
+ QStringList commonSuffixes;
+
+ KateSyntaxDocument *syntax;
+
+ uint dynamicCtxsCount;
+ QTime lastCtxsReset;
+ bool forceNoDCReset;
+};
+
+class KateViewHighlightAction: public Kate::ActionMenu
+{
+ Q_OBJECT
+
+ public:
+ KateViewHighlightAction(const QString& text, QObject* parent = 0, const char* name = 0)
+ : Kate::ActionMenu(text, parent, name) { init(); };
+
+ ~KateViewHighlightAction(){;};
+
+ void updateMenu (Kate::Document *doc);
+
+ private:
+ void init();
+
+ QGuardedPtr<Kate::Document> m_doc;
+ QStringList subMenusName;
+ QStringList names;
+ QPtrList<QPopupMenu> subMenus;
+
+ public slots:
+ void slotAboutToShow();
+
+ private slots:
+ void setHl (int mode);
+};
+
+#endif
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/kateindentscriptabstracts.cpp b/kate/part/kateindentscriptabstracts.cpp
new file mode 100644
index 000000000..f30dd9ebd
--- /dev/null
+++ b/kate/part/kateindentscriptabstracts.cpp
@@ -0,0 +1,49 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2005 Joseph Wenninger <jowenn@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kateindentscriptabstracts.h"
+
+#include <kdebug.h>
+#include <qstring.h>
+
+//BEGIN KateIndentScriptImplAbstractImpl
+
+KateIndentScriptImplAbstract::KateIndentScriptImplAbstract(const QString& internalName,
+ const QString &filePath, const QString &niceName,
+ const QString &copyright, double version):m_refcount(0),m_filePath(filePath),m_niceName(niceName),
+ m_copyright(copyright),m_version(version)
+{
+}
+
+KateIndentScriptImplAbstract::~KateIndentScriptImplAbstract()
+{
+}
+
+void KateIndentScriptImplAbstract::incRef()
+{
+ kdDebug(13050)<<"KateIndentScriptImplAbstract::incRef()"<<endl;
+ m_refcount++;
+}
+
+void KateIndentScriptImplAbstract::decRef()
+{
+ kdDebug(13050)<<"KateIndentScriptImplAbstract::decRef()"<<endl;
+ m_refcount--;
+}
+
+//END
diff --git a/kate/part/kateindentscriptabstracts.h b/kate/part/kateindentscriptabstracts.h
new file mode 100644
index 000000000..a47a8431a
--- /dev/null
+++ b/kate/part/kateindentscriptabstracts.h
@@ -0,0 +1,99 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2005 Joseph Wenninger <jowenn@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef _KATEINDENTSCRIPTABSTRACTS_H_
+#define _KATEINDENTSCRIPTABSTRACTS_H_
+
+#include <qstring.h>
+#include <kdebug.h>
+
+namespace Kate {
+ class View;
+}
+
+class KateDocCursor;
+
+
+class KateIndentScriptImplAbstract {
+ public:
+ friend class KateIndentScript;
+ KateIndentScriptImplAbstract(const QString& internalName,
+ const QString &filePath, const QString &niceName,
+ const QString &copyright, double version);
+ virtual ~KateIndentScriptImplAbstract();
+
+ virtual bool processChar( class Kate::View *view, QChar c, QString &errorMsg )=0;
+ virtual bool processLine( class Kate::View *view, const KateDocCursor &line, QString &errorMsg )=0;
+ virtual bool processNewline( class Kate::View *view, const KateDocCursor &begin, bool needcontinue, QString &errorMsg )=0;
+ protected:
+ virtual void decRef();
+ long refCount() {return m_refcount;}
+ QString filePath() const {return m_filePath;}
+ private:
+ void incRef();
+ long m_refcount;
+ QString m_internalName;
+ QString m_filePath;
+ QString m_niceName;
+ QString m_copyright;
+ double m_version;
+};
+
+
+class KateIndentScript {
+ public:
+ KateIndentScript(KateIndentScriptImplAbstract *scr):m_scr(scr) { if (scr) scr->incRef(); }
+ ~KateIndentScript() {if (m_scr) m_scr->decRef();}
+ KateIndentScript():m_scr(0) {}
+ KateIndentScript(const KateIndentScript &p):m_scr(p.m_scr){if (m_scr) m_scr->incRef();}
+ KateIndentScript &operator=(const KateIndentScript &p) {
+ if (m_scr==p.m_scr) return *this;
+ if (m_scr) m_scr->decRef();
+ m_scr=p.m_scr;
+ if (m_scr) m_scr->incRef();
+ return *this;
+ }
+ /*operator KateIndentJScript*() const { return m_scr; }*/
+ bool processChar( class Kate::View *view, QChar c, QString &errorMsg ) {
+ kdDebug(13050)<<"KateIndentScript::processChar: m_scr:"<<m_scr<<endl;
+ if (m_scr) return m_scr->processChar(view,c,errorMsg); else return true;
+ }
+ bool processLine( class Kate::View *view, const KateDocCursor& line, QString &errorMsg ) {
+ kdDebug(13050)<<"KateIndentScript::processLine: m_scr:"<<m_scr<<endl;
+ if (m_scr) return m_scr->processLine(view,line,errorMsg); else return true;
+ }
+ bool processNewline( class Kate::View *view, const KateDocCursor& begin, bool needcontinue, QString &errorMsg ) {
+ kdDebug(13050)<<"KateIndentScript::processNewLine: m_scr:"<<m_scr<<endl;
+ if (m_scr) return m_scr->processNewline(view,begin,needcontinue,errorMsg); else return true;
+ }
+
+ bool isNull () const {return (m_scr==0);}
+ private:
+ KateIndentScriptImplAbstract *m_scr;
+};
+
+class KateIndentScriptManagerAbstract
+{
+
+ public:
+ KateIndentScriptManagerAbstract () {};
+ virtual ~KateIndentScriptManagerAbstract () {};
+ virtual KateIndentScript script(const QString &scriptname)=0;
+};
+
+#endif
diff --git a/kate/part/katejscript.cpp b/kate/part/katejscript.cpp
new file mode 100644
index 000000000..024f36500
--- /dev/null
+++ b/kate/part/katejscript.cpp
@@ -0,0 +1,1169 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2005 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2005 Joseph Wenninger <jowenn@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "katejscript.h"
+
+#include "katedocument.h"
+#include "kateview.h"
+#include "katefactory.h"
+#include "kateconfig.h"
+#include "kateautoindent.h"
+#include "katehighlight.h"
+#include "katetextline.h"
+
+#include "kateindentscriptabstracts.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <kdebug.h>
+#include <kstandarddirs.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kconfig.h>
+
+#include <kjs/function_object.h>
+#include <kjs/interpreter.h>
+#include <kjs/lookup.h>
+
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qpopupmenu.h>
+#include <qregexp.h>
+#include <qtextstream.h>
+
+
+namespace KJS {
+
+// taken from khtml
+// therefor thx to:
+// Copyright (C) 1999-2003 Harri Porten (porten@kde.org)
+// Copyright (C) 2001-2003 David Faure (faure@kde.org)
+// Copyright (C) 2003 Apple Computer, Inc.
+
+UString::UString(const QString &d)
+{
+ unsigned int len = d.length();
+ UChar *dat = new UChar[len];
+ memcpy(dat, d.unicode(), len * sizeof(UChar));
+ rep = UString::Rep::create(dat, len);
+}
+
+QString UString::qstring() const
+{
+ return QString((QChar*) data(), size());
+}
+
+QConstString UString::qconststring() const
+{
+ return QConstString((QChar*) data(), size());
+}
+
+//BEGIN global methods
+class KateJSGlobalFunctions : public ObjectImp
+{
+ public:
+ KateJSGlobalFunctions(int i, int length);
+ virtual bool implementsCall() const { return true; }
+ virtual Value call(ExecState *exec, Object &thisObj, const List &args);
+
+ enum {
+ Debug
+ };
+
+ private:
+ int id;
+};
+KateJSGlobalFunctions::KateJSGlobalFunctions(int i, int length) : ObjectImp(), id(i)
+{
+ putDirect(lengthPropertyName,length,DontDelete|ReadOnly|DontEnum);
+}
+Value KateJSGlobalFunctions::call(ExecState *exec, Object &/*thisObj*/, const List &args)
+{
+ switch (id) {
+ case Debug:
+ qDebug("Kate (KJS Scripting): %s", args[0].toString(exec).ascii());
+ return Undefined();
+ default:
+ break;
+ }
+
+ return Undefined();
+}
+//END global methods
+
+} // namespace KJS
+
+//BEGIN JS API STUFF
+
+class KateJSGlobal : public KJS::ObjectImp {
+public:
+ virtual KJS::UString className() const { return "global"; }
+};
+
+class KateJSDocument : public KJS::ObjectImp
+{
+ public:
+ KateJSDocument (KJS::ExecState *exec, KateDocument *_doc);
+
+ KJS::Value get( KJS::ExecState *exec, const KJS::Identifier &propertyName) const;
+
+ KJS::Value getValueProperty(KJS::ExecState *exec, int token) const;
+
+ void put(KJS::ExecState *exec, const KJS::Identifier &propertyName, const KJS::Value& value, int attr = KJS::None);
+
+ void putValueProperty(KJS::ExecState *exec, int token, const KJS::Value& value, int attr);
+
+ const KJS::ClassInfo* classInfo() const { return &info; }
+
+ enum { FullText,
+ Text,
+ TextLine,
+ Lines,
+ Length,
+ LineLength,
+ SetText,
+ Clear,
+ InsertText,
+ RemoveText,
+ InsertLine,
+ RemoveLine,
+ EditBegin,
+ EditEnd,
+ IndentWidth,
+ IndentMode,
+ SpaceIndent,
+ MixedIndent,
+ HighlightMode,
+ IsInWord,
+ CanBreakAt,
+ CanComment,
+ CommentMarker,
+ CommentStart,
+ CommentEnd,
+ Attribute
+ };
+
+ public:
+ KateDocument *doc;
+
+ static const KJS::ClassInfo info;
+};
+
+class KateJSView : public KJS::ObjectImp
+{
+ public:
+ KateJSView (KJS::ExecState *exec, KateView *_view);
+
+ KJS::Value get( KJS::ExecState *exec, const KJS::Identifier &propertyName) const;
+
+ KJS::Value getValueProperty(KJS::ExecState *exec, int token) const;
+
+ void put(KJS::ExecState *exec, const KJS::Identifier &propertyName, const KJS::Value& value, int attr = KJS::None);
+
+ void putValueProperty(KJS::ExecState *exec, int token, const KJS::Value& value, int attr);
+
+ const KJS::ClassInfo* classInfo() const { return &info; }
+
+ enum { CursorLine,
+ CursorColumn,
+ CursorColumnReal,
+ SetCursorPosition,
+ SetCursorPositionReal,
+ Selection,
+ HasSelection,
+ SetSelection,
+ RemoveSelectedText,
+ SelectAll,
+ ClearSelection,
+ SelStartLine,
+ SelStartCol,
+ SelEndLine,
+ SelEndCol
+ };
+
+ public:
+ KateView *view;
+
+ static const KJS::ClassInfo info;
+};
+
+class KateJSIndenter : public KJS::ObjectImp
+{
+ public:
+ KateJSIndenter (KJS::ExecState *exec);
+ /*
+ KJS::Value get( KJS::ExecState *exec, const KJS::Identifier &propertyName) const;
+
+ KJS::Value getValueProperty(KJS::ExecState *exec, int token) const;
+
+ void put(KJS::ExecState *exec, const KJS::Identifier &propertyName, const KJS::Value& value, int attr = KJS::None);
+
+ void putValueProperty(KJS::ExecState *exec, int token, const KJS::Value& value, int attr);
+ */
+ const KJS::ClassInfo* classInfo() const { return &info; }
+
+ enum { OnChar,
+ OnLine,
+ OnNewline,
+ Dummy
+ };
+
+ public:
+
+ static const KJS::ClassInfo info;
+};
+
+#include "katejscript.lut.h"
+
+//END
+
+KateJScript::KateJScript ()
+ : m_global (new KJS::Object (new KateJSGlobal ()))
+ , m_interpreter (new KJS::Interpreter (*m_global))
+ , m_document (new KJS::Object(wrapDocument(m_interpreter->globalExec(), 0)))
+ , m_view (new KJS::Object (wrapView(m_interpreter->globalExec(), 0)))
+{
+ // put some stuff into env., this should stay for all executions, as we keep external
+ // references to the inserted KJS::Objects, this should avoid any garbage collection
+ m_interpreter->globalObject().put(m_interpreter->globalExec(), "document", *m_document);
+ m_interpreter->globalObject().put(m_interpreter->globalExec(), "view", *m_view);
+ m_interpreter->globalObject().put(m_interpreter->globalExec(), "debug",
+ KJS::Object(new KateJSGlobalFunctions(KateJSGlobalFunctions::Debug,1)));
+}
+
+KateJScript::~KateJScript ()
+{
+ delete m_view;
+ delete m_document;
+ delete m_interpreter;
+ delete m_global;
+}
+
+KJS::ObjectImp *KateJScript::wrapDocument (KJS::ExecState *exec, KateDocument *doc)
+{
+ return new KateJSDocument(exec, doc);
+}
+
+KJS::ObjectImp *KateJScript::wrapView (KJS::ExecState *exec, KateView *view)
+{
+ return new KateJSView(exec, view);
+}
+
+bool KateJScript::execute (KateView *view, const QString &script, QString &errorMsg)
+{
+ // no view, no fun
+ if (!view)
+ {
+ errorMsg = i18n("Could not access view");
+ return false;
+ }
+
+ // init doc & view with new pointers!
+ static_cast<KateJSDocument *>( m_document->imp() )->doc = view->doc();
+ static_cast<KateJSView *>( m_view->imp() )->view = view;
+
+ // run the script for real
+ KJS::Completion comp (m_interpreter->evaluate(script));
+
+ if (comp.complType() == KJS::Throw)
+ {
+ KJS::ExecState *exec = m_interpreter->globalExec();
+
+ KJS::Value exVal = comp.value();
+
+ char *msg = exVal.toString(exec).ascii();
+
+ int lineno = -1;
+
+ if (exVal.type() == KJS::ObjectType)
+ {
+ KJS::Value lineVal = KJS::Object::dynamicCast(exVal).get(exec,"line");
+
+ if (lineVal.type() == KJS::NumberType)
+ lineno = int(lineVal.toNumber(exec));
+ }
+
+ errorMsg = i18n("Exception, line %1: %2").arg(lineno).arg(msg);
+ return false;
+ }
+
+ return true;
+}
+
+//BEGIN KateJSDocument
+
+// -------------------------------------------------------------------------
+/* Source for KateJSDocumentProtoTable.
+@begin KateJSDocumentProtoTable 21
+#
+# edit interface stuff + editBegin/End, this is nice start
+#
+ textFull KateJSDocument::FullText DontDelete|Function 0
+ textRange KateJSDocument::Text DontDelete|Function 4
+ textLine KateJSDocument::TextLine DontDelete|Function 1
+ lines KateJSDocument::Lines DontDelete|Function 0
+ length KateJSDocument::Length DontDelete|Function 0
+ lineLength KateJSDocument::LineLength DontDelete|Function 1
+ setText KateJSDocument::SetText DontDelete|Function 1
+ clear KateJSDocument::Clear DontDelete|Function 0
+ insertText KateJSDocument::InsertText DontDelete|Function 3
+ removeText KateJSDocument::RemoveText DontDelete|Function 4
+ insertLine KateJSDocument::InsertLine DontDelete|Function 2
+ removeLine KateJSDocument::RemoveLine DontDelete|Function 1
+ editBegin KateJSDocument::EditBegin DontDelete|Function 0
+ editEnd KateJSDocument::EditEnd DontDelete|Function 0
+#
+# methods from highlight (and around)
+#
+ isInWord KateJSDocument::IsInWord DontDelete|Function 2
+ canBreakAt KateJSDocument::CanBreakAt DontDelete|Function 2
+ canComment KateJSDocument::CanComment DontDelete|Function 2
+ commentMarker KateJSDocument::CommentMarker DontDelete|Function 1
+ commentStart KateJSDocument::CommentStart DontDelete|Function 1
+ commentEnd KateJSDocument::CommentEnd DontDelete|Function 1
+ attribute KateJSDocument::Attribute DontDelete|Function 2
+@end
+
+@begin KateJSDocumentTable 6
+#
+# Configuration properties
+#
+ indentWidth KateJSDocument::IndentWidth DontDelete|ReadOnly
+ indentMode KateJSDocument::IndentMode DontDelete|ReadOnly
+ spaceIndent KateJSDocument::SpaceIndent DontDelete|ReadOnly
+ mixedIndent KateJSDocument::MixedIndent DontDelete|ReadOnly
+ highlightMode KateJSDocument::HighlightMode DontDelete|ReadOnly
+@end
+*/
+
+DEFINE_PROTOTYPE("KateJSDocument",KateJSDocumentProto)
+IMPLEMENT_PROTOFUNC(KateJSDocumentProtoFunc)
+IMPLEMENT_PROTOTYPE(KateJSDocumentProto,KateJSDocumentProtoFunc)
+
+const KJS::ClassInfo KateJSDocument::info = { "KateJSDocument", 0, 0, 0 };
+
+KJS::Value KJS::KateJSDocumentProtoFunc::call(KJS::ExecState *exec, KJS::Object &thisObj, const KJS::List &args)
+{
+ KJS_CHECK_THIS( KateJSDocument, thisObj );
+
+ KateDocument *doc = static_cast<KateJSDocument *>( thisObj.imp() )->doc;
+
+ if (!doc)
+ return KJS::Undefined();
+
+ switch (id)
+ {
+ case KateJSDocument::FullText:
+ return KJS::String (doc->text());
+
+ case KateJSDocument::Text:
+ return KJS::String (doc->text(args[0].toUInt32(exec), args[1].toUInt32(exec), args[2].toUInt32(exec), args[3].toUInt32(exec)));
+
+ case KateJSDocument::TextLine:
+ return KJS::String (doc->textLine (args[0].toUInt32(exec)));
+
+ case KateJSDocument::Lines:
+ return KJS::Number (doc->numLines());
+
+ case KateJSDocument::Length:
+ return KJS::Number (doc->length());
+
+ case KateJSDocument::LineLength:
+ return KJS::Number (doc->lineLength(args[0].toUInt32(exec)));
+
+ case KateJSDocument::SetText:
+ return KJS::Boolean (doc->setText(args[0].toString(exec).qstring()));
+
+ case KateJSDocument::Clear:
+ return KJS::Boolean (doc->clear());
+
+ case KateJSDocument::InsertText:
+ return KJS::Boolean (doc->insertText (args[0].toUInt32(exec), args[1].toUInt32(exec), args[2].toString(exec).qstring()));
+
+ case KateJSDocument::RemoveText:
+ return KJS::Boolean (doc->removeText(args[0].toUInt32(exec), args[1].toUInt32(exec), args[2].toUInt32(exec), args[3].toUInt32(exec)));
+
+ case KateJSDocument::InsertLine:
+ return KJS::Boolean (doc->insertLine (args[0].toUInt32(exec), args[1].toString(exec).qstring()));
+
+ case KateJSDocument::RemoveLine:
+ return KJS::Boolean (doc->removeLine (args[0].toUInt32(exec)));
+
+ case KateJSDocument::EditBegin:
+ doc->editBegin();
+ return KJS::Null ();
+
+ case KateJSDocument::EditEnd:
+ doc->editEnd ();
+ return KJS::Null ();
+
+ case KateJSDocument::IsInWord:
+ return KJS::Boolean( doc->highlight()->isInWord( args[0].toString(exec).qstring().at(0), args[1].toUInt32(exec) ) );
+
+ case KateJSDocument::CanBreakAt:
+ return KJS::Boolean( doc->highlight()->canBreakAt( args[0].toString(exec).qstring().at(0), args[1].toUInt32(exec) ) );
+
+ case KateJSDocument::CanComment:
+ return KJS::Boolean( doc->highlight()->canComment( args[0].toUInt32(exec), args[1].toUInt32(exec) ) );
+
+ case KateJSDocument::CommentMarker:
+ return KJS::String( doc->highlight()->getCommentSingleLineStart( args[0].toUInt32(exec) ) );
+
+ case KateJSDocument::CommentStart:
+ return KJS::String( doc->highlight()->getCommentStart( args[0].toUInt32(exec) ) );
+
+ case KateJSDocument::CommentEnd:
+ return KJS::String( doc->highlight()->getCommentEnd( args[0].toUInt32(exec) ) );
+
+ case KateJSDocument::Attribute:
+ return KJS::Number( doc->kateTextLine(args[0].toUInt32(exec))->attribute(args[1].toUInt32(exec)) );
+ }
+
+ return KJS::Undefined();
+}
+
+KJS::Value KateJSDocument::get( KJS::ExecState *exec, const KJS::Identifier &propertyName) const
+{
+ return KJS::lookupGetValue<KateJSDocument,KJS::ObjectImp>(exec, propertyName, &KateJSDocumentTable, this );
+}
+
+KJS::Value KateJSDocument::getValueProperty(KJS::ExecState *exec, int token) const
+{
+ if (!doc)
+ return KJS::Undefined ();
+
+ switch (token) {
+ case KateJSDocument::IndentWidth:
+ return KJS::Number( doc->config()->indentationWidth() );
+
+ case KateJSDocument::IndentMode:
+ return KJS::String( KateAutoIndent::modeName( doc->config()->indentationMode() ) );
+
+ case KateJSDocument::SpaceIndent:
+ return KJS::Boolean( doc->config()->configFlags() & KateDocumentConfig::cfSpaceIndent );
+
+ case KateJSDocument::MixedIndent:
+ return KJS::Boolean( doc->config()->configFlags() & KateDocumentConfig::cfMixedIndent );
+
+ case KateJSDocument::HighlightMode:
+ return KJS::String( doc->hlModeName( doc->hlMode() ) );
+ }
+
+ return KJS::Undefined ();
+}
+
+void KateJSDocument::put(KJS::ExecState *exec, const KJS::Identifier &propertyName, const KJS::Value& value, int attr)
+{
+ KJS::lookupPut<KateJSDocument,KJS::ObjectImp>(exec, propertyName, value, attr, &KateJSDocumentTable, this );
+}
+
+void KateJSDocument::putValueProperty(KJS::ExecState *exec, int token, const KJS::Value& value, int attr)
+{
+ if (!doc)
+ return;
+}
+
+KateJSDocument::KateJSDocument (KJS::ExecState *exec, KateDocument *_doc)
+ : KJS::ObjectImp (KateJSDocumentProto::self(exec))
+ , doc (_doc)
+{
+}
+
+//END
+
+//BEGIN KateJSView
+
+// -------------------------------------------------------------------------
+/* Source for KateJSViewProtoTable.
+@begin KateJSViewProtoTable 14
+ cursorLine KateJSView::CursorLine DontDelete|Function 0
+ cursorColumn KateJSView::CursorColumn DontDelete|Function 0
+ cursorColumnReal KateJSView::CursorColumnReal DontDelete|Function 0
+ setCursorPosition KateJSView::SetCursorPosition DontDelete|Function 2
+ setCursorPositionReal KateJSView::SetCursorPositionReal DontDelete|Function 2
+ selection KateJSView::Selection DontDelete|Function 0
+ hasSelection KateJSView::HasSelection DontDelete|Function 0
+ setSelection KateJSView::SetSelection DontDelete|Function 4
+ removeSelectedText KateJSView::RemoveSelectedText DontDelete|Function 0
+ selectAll KateJSView::SelectAll DontDelete|Function 0
+ clearSelection KateJSView::ClearSelection DontDelete|Function 0
+@end
+*/
+
+/* Source for KateJSViewTable.
+@begin KateJSViewTable 5
+ selectionStartLine KateJSView::SelStartLine DontDelete|ReadOnly
+ selectionStartColumn KateJSView::SelStartCol DontDelete|ReadOnly
+ selectionEndLine KateJSView::SelEndLine DontDelete|ReadOnly
+ selectionEndColumn KateJSView::SelEndCol DontDelete|ReadOnly
+@end
+*/
+
+DEFINE_PROTOTYPE("KateJSView",KateJSViewProto)
+IMPLEMENT_PROTOFUNC(KateJSViewProtoFunc)
+IMPLEMENT_PROTOTYPE(KateJSViewProto,KateJSViewProtoFunc)
+
+const KJS::ClassInfo KateJSView::info = { "KateJSView", 0, &KateJSViewTable, 0 };
+
+KJS::Value KJS::KateJSViewProtoFunc::call(KJS::ExecState *exec, KJS::Object &thisObj, const KJS::List &args)
+{
+ KJS_CHECK_THIS( KateJSView, thisObj );
+
+ KateView *view = static_cast<KateJSView *>( thisObj.imp() )->view;
+
+ if (!view)
+ return KJS::Undefined();
+
+ switch (id)
+ {
+ case KateJSView::CursorLine:
+ return KJS::Number (view->cursorLine());
+
+ case KateJSView::CursorColumn:
+ return KJS::Number (view->cursorColumn());
+
+ case KateJSView::CursorColumnReal:
+ return KJS::Number (view->cursorColumnReal());
+
+ case KateJSView::SetCursorPosition:
+ return KJS::Boolean( view->setCursorPosition( args[0].toUInt32(exec), args[1].toUInt32(exec) ) );
+
+ case KateJSView::SetCursorPositionReal:
+ return KJS::Boolean( view->setCursorPositionReal( args[0].toUInt32(exec), args[1].toUInt32(exec) ) );
+
+ // SelectionInterface goes in the view, in anticipation of the future
+ case KateJSView::Selection:
+ return KJS::String( view->selection() );
+
+ case KateJSView::HasSelection:
+ return KJS::Boolean( view->hasSelection() );
+
+ case KateJSView::SetSelection:
+ return KJS::Boolean( view->setSelection(args[0].toUInt32(exec),
+ args[1].toUInt32(exec),
+ args[2].toUInt32(exec),
+ args[3].toUInt32(exec)) );
+
+ case KateJSView::RemoveSelectedText:
+ return KJS::Boolean( view->removeSelectedText() );
+
+ case KateJSView::SelectAll:
+ return KJS::Boolean( view->selectAll() );
+
+ case KateJSView::ClearSelection:
+ return KJS::Boolean( view->clearSelection() );
+ }
+
+ return KJS::Undefined();
+}
+
+KateJSView::KateJSView (KJS::ExecState *exec, KateView *_view)
+ : KJS::ObjectImp (KateJSViewProto::self(exec))
+ , view (_view)
+{
+}
+
+KJS::Value KateJSView::get( KJS::ExecState *exec, const KJS::Identifier &propertyName) const
+{
+ return KJS::lookupGetValue<KateJSView,KJS::ObjectImp>(exec, propertyName, &KateJSViewTable, this );
+}
+
+KJS::Value KateJSView::getValueProperty(KJS::ExecState *exec, int token) const
+{
+ if (!view)
+ return KJS::Undefined ();
+
+ switch (token) {
+ case KateJSView::SelStartLine:
+ return KJS::Number( view->selStartLine() );
+
+ case KateJSView::SelStartCol:
+ return KJS::Number( view->selStartCol() );
+
+ case KateJSView::SelEndLine:
+ return KJS::Number( view->selEndLine() );
+
+ case KateJSView::SelEndCol:
+ return KJS::Number( view->selEndCol() );
+ }
+
+ return KJS::Undefined ();
+}
+
+void KateJSView::put(KJS::ExecState *exec, const KJS::Identifier &propertyName, const KJS::Value& value, int attr)
+{
+ KJS::lookupPut<KateJSView,KJS::ObjectImp>(exec, propertyName, value, attr, &KateJSViewTable, this );
+}
+
+void KateJSView::putValueProperty(KJS::ExecState *exec, int token, const KJS::Value& value, int attr)
+{
+ if (!view)
+ return;
+
+
+}
+
+//END
+
+//BEGIN KateJScriptManager
+
+KateJScriptManager::KateJScriptManager ()
+{
+ m_scripts.setAutoDelete (true);
+ collectScripts ();
+}
+
+KateJScriptManager::~KateJScriptManager ()
+{
+}
+
+void KateJScriptManager::collectScripts (bool force)
+{
+// If there's something in myModeList the Mode List was already built so, don't do it again
+ if (!m_scripts.isEmpty())
+ return;
+
+ // We'll store the scripts list in this config
+ KConfig config("katepartjscriptrc", false, false);
+
+ // figure out if the kate install is too new
+ config.setGroup ("General");
+ if (config.readNumEntry ("Version") > config.readNumEntry ("CachedVersion"))
+ {
+ config.writeEntry ("CachedVersion", config.readNumEntry ("Version"));
+ force = true;
+ }
+
+ // Let's get a list of all the .js files
+ QStringList list = KGlobal::dirs()->findAllResources("data","katepart/scripts/*.js",false,true);
+
+ // Let's iterate through the list and build the Mode List
+ for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it )
+ {
+ // Each file has a group called:
+ QString Group="Cache "+ *it;
+
+ // Let's go to this group
+ config.setGroup(Group);
+
+ // stat the file
+ struct stat sbuf;
+ memset (&sbuf, 0, sizeof(sbuf));
+ stat(QFile::encodeName(*it), &sbuf);
+
+ // If the group exist and we're not forced to read the .js file, let's build myModeList for katepartjscriptrc
+ if (!force && config.hasGroup(Group) && (sbuf.st_mtime == config.readNumEntry("lastModified")))
+ {
+ }
+ else
+ {
+ kdDebug (13050) << "add script: " << *it << endl;
+
+ QString desktopFile = (*it).left((*it).length()-2).append ("desktop");
+
+ kdDebug (13050) << "add script (desktop file): " << desktopFile << endl;
+
+ QFileInfo dfi (desktopFile);
+
+ if (dfi.exists())
+ {
+ KConfig df (desktopFile, true, false);
+ df.setDesktopGroup ();
+
+ // get cmdname, fallback to baseName, if it is empty, therefor not use the kconfig fallback
+ QString cmdname = df.readEntry ("X-Kate-Command");
+ if (cmdname.isEmpty())
+ {
+ QFileInfo fi (*it);
+ cmdname = fi.baseName();
+ }
+
+ if (m_scripts[cmdname])
+ continue;
+
+ KateJScriptManager::Script *s = new KateJScriptManager::Script ();
+
+ s->name = cmdname;
+ s->filename = *it;
+ s->desktopFileExists = true;
+
+ m_scripts.insert (s->name, s);
+ }
+ else // no desktop file around, fall back to scriptfilename == commandname
+ {
+ kdDebug (13050) << "add script: fallback, no desktop file around!" << endl;
+
+ QFileInfo fi (*it);
+
+ if (m_scripts[fi.baseName()])
+ continue;
+
+ KateJScriptManager::Script *s = new KateJScriptManager::Script ();
+
+ s->name = fi.baseName();
+ s->filename = *it;
+ s->desktopFileExists = false;
+
+ m_scripts.insert (s->name, s);
+ }
+ }
+ }
+
+ // Syncronize with the file katepartjscriptrc
+ config.sync();
+}
+
+bool KateJScriptManager::exec( Kate::View *view, const QString &_cmd, QString &errorMsg )
+{
+ // cast it hardcore, we know that it is really a kateview :)
+ KateView *v = (KateView*) view;
+
+ if ( !v )
+ {
+ errorMsg = i18n("Could not access view");
+ return false;
+ }
+
+ //create a list of args
+ QStringList args( QStringList::split( QRegExp("\\s+"), _cmd ) );
+ QString cmd ( args.first() );
+ args.remove( args.first() );
+
+ kdDebug(13050) << "try to exec: " << cmd << endl;
+
+ if (!m_scripts[cmd])
+ {
+ errorMsg = i18n("Command not found");
+ return false;
+ }
+
+ QFile file (m_scripts[cmd]->filename);
+
+ if ( !file.open( IO_ReadOnly ) )
+ {
+ errorMsg = i18n("JavaScript file not found");
+ return false;
+ }
+
+ QTextStream stream( &file );
+ stream.setEncoding (QTextStream::UnicodeUTF8);
+
+ QString source = stream.read ();
+
+ file.close();
+
+ return KateFactory::self()->jscript()->execute(v, source, errorMsg);
+}
+
+bool KateJScriptManager::help( Kate::View *, const QString &cmd, QString &msg )
+{
+ if (!m_scripts[cmd] || !m_scripts[cmd]->desktopFileExists)
+ return false;
+
+ KConfig df (m_scripts[cmd]->desktopFilename(), true, false);
+ df.setDesktopGroup ();
+
+ msg = df.readEntry ("X-Kate-Help");
+
+ if (msg.isEmpty())
+ return false;
+
+ return true;
+}
+
+QStringList KateJScriptManager::cmds()
+{
+ QStringList l;
+
+ QDictIterator<KateJScriptManager::Script> it( m_scripts );
+ for( ; it.current(); ++it )
+ l << it.current()->name;
+
+ return l;
+}
+
+//END
+
+
+
+
+//BEGIN KateJSIndenter
+
+// -------------------------------------------------------------------------
+/* Source for KateJSIndenterProtoTable.
+@begin KateJSIndenterProtoTable 1
+ Dummy KateJSIndenter::Dummy DontDelete
+@end
+*/
+
+/* Source for KateJSIndenterTable.
+@begin KateJSIndenterTable 3
+ onchar KateJSIndenter::OnChar DontDelete
+ onnewline KateJSIndenter::OnNewline DontDelete
+ online KateJSIndenter::OnLine DontDelete
+
+@end
+*/
+
+KateJSIndenter::KateJSIndenter (KJS::ExecState *exec)
+ : KJS::ObjectImp (KateJSViewProto::self(exec))
+{
+}
+
+DEFINE_PROTOTYPE("KateJSIndenter",KateJSIndenterProto)
+IMPLEMENT_PROTOFUNC(KateJSIndenterProtoFunc)
+IMPLEMENT_PROTOTYPE(KateJSIndenterProto,KateJSIndenterProtoFunc)
+
+const KJS::ClassInfo KateJSIndenter::info = { "KateJSIndenter", 0, &KateJSIndenterTable, 0 };
+
+KJS::Value KJS::KateJSIndenterProtoFunc::call(KJS::ExecState *exec, KJS::Object &thisObj, const KJS::List &args)
+{
+ KJS_CHECK_THIS( KateJSIndenter, thisObj );
+
+ return KJS::Undefined();
+}
+
+//END
+
+//BEGIN KateIndentJScriptImpl
+KateIndentJScriptImpl::KateIndentJScriptImpl(const QString& internalName,
+ const QString &filePath, const QString &niceName,
+ const QString &copyright, double version):
+ KateIndentScriptImplAbstract(internalName,filePath,niceName,copyright,version),m_interpreter(0),m_indenter(0)
+{
+}
+
+
+KateIndentJScriptImpl::~KateIndentJScriptImpl()
+{
+ deleteInterpreter();
+}
+
+void KateIndentJScriptImpl::decRef()
+{
+ KateIndentScriptImplAbstract::decRef();
+ if (refCount()==0)
+ {
+ deleteInterpreter();
+ }
+}
+
+void KateIndentJScriptImpl::deleteInterpreter()
+{
+ m_docWrapper=0;
+ m_viewWrapper=0;
+ delete m_indenter;
+ m_indenter=0;
+ delete m_interpreter;
+ m_interpreter=0;
+}
+
+bool KateIndentJScriptImpl::setupInterpreter(QString &errorMsg)
+{
+ if (!m_interpreter)
+ {
+ kdDebug(13050)<<"Setting up interpreter"<<endl;
+ m_interpreter=new KJS::Interpreter(KJS::Object(new KateJSGlobal()));
+ m_docWrapper=new KateJSDocument(m_interpreter->globalExec(),0);
+ m_viewWrapper=new KateJSView(m_interpreter->globalExec(),0);
+ m_indenter=new KJS::Object(new KateJSIndenter(m_interpreter->globalExec()));
+ m_interpreter->globalObject().put(m_interpreter->globalExec(),"document",KJS::Object(m_docWrapper),KJS::DontDelete | KJS::ReadOnly);
+ m_interpreter->globalObject().put(m_interpreter->globalExec(),"view",KJS::Object(m_viewWrapper),KJS::DontDelete | KJS::ReadOnly);
+ m_interpreter->globalObject().put(m_interpreter->globalExec(),"debug", KJS::Object(new
+ KateJSGlobalFunctions(KateJSGlobalFunctions::Debug,1)));
+ m_interpreter->globalObject().put(m_interpreter->globalExec(),"indenter",*m_indenter,KJS::DontDelete | KJS::ReadOnly);
+ QFile file (filePath());
+
+ if ( !file.open( IO_ReadOnly ) )
+ {
+ errorMsg = i18n("JavaScript file not found");
+ deleteInterpreter();
+ return false;
+ }
+
+ QTextStream stream( &file );
+ stream.setEncoding (QTextStream::UnicodeUTF8);
+
+ QString source = stream.read ();
+
+ file.close();
+
+ KJS::Completion comp (m_interpreter->evaluate(source));
+ if (comp.complType() == KJS::Throw)
+ {
+ KJS::ExecState *exec = m_interpreter->globalExec();
+
+ KJS::Value exVal = comp.value();
+
+ char *msg = exVal.toString(exec).ascii();
+
+ int lineno = -1;
+
+ if (exVal.type() == KJS::ObjectType)
+ {
+ KJS::Value lineVal = KJS::Object::dynamicCast(exVal).get(exec,"line");
+
+ if (lineVal.type() == KJS::NumberType)
+ lineno = int(lineVal.toNumber(exec));
+ }
+
+ errorMsg = i18n("Exception, line %1: %2").arg(lineno).arg(msg);
+ deleteInterpreter();
+ return false;
+ } else {
+ return true;
+ }
+ } else return true;
+}
+
+
+inline static bool KateIndentJScriptCall(Kate::View *view, QString &errorMsg, KateJSDocument *docWrapper, KateJSView *viewWrapper,
+ KJS::Interpreter *interpreter, KJS::Object lookupobj,const KJS::Identifier& func,KJS::List params)
+{
+ // no view, no fun
+ if (!view)
+ {
+ errorMsg = i18n("Could not access view");
+ return false;
+ }
+
+ KateView *v=(KateView*)view;
+
+ KJS::Object o=lookupobj.get(interpreter->globalExec(),func).toObject(interpreter->globalExec());
+ if (interpreter->globalExec()->hadException())
+ {
+ errorMsg=interpreter->globalExec()->exception().toString(interpreter->globalExec()).qstring();
+ kdDebug(13050)<<"Exception(1):"<<errorMsg<<endl;
+ interpreter->globalExec()->clearException();
+ return false;
+ }
+
+ // init doc & view with new pointers!
+ docWrapper->doc = v->doc();
+ viewWrapper->view = v;
+
+ /*kdDebug(13050)<<"Call Object:"<<o.toString(interpreter->globalExec()).ascii()<<endl;*/
+ o.call(interpreter->globalExec(),interpreter->globalObject(),params);
+ if (interpreter->globalExec()->hadException())
+ {
+ errorMsg=interpreter->globalExec()->exception().toString(interpreter->globalExec()).ascii();
+ kdDebug(13050)<<"Exception(2):"<<errorMsg<<endl;
+ interpreter->globalExec()->clearException();
+ return false;
+ }
+ return true;
+}
+
+bool KateIndentJScriptImpl::processChar(Kate::View *view, QChar c, QString &errorMsg )
+{
+
+ kdDebug(13050)<<"KateIndentJScriptImpl::processChar"<<endl;
+ if (!setupInterpreter(errorMsg)) return false;
+ KJS::List params;
+ params.append(KJS::String(QString(c)));
+ return KateIndentJScriptCall(view,errorMsg,m_docWrapper,m_viewWrapper,m_interpreter,*m_indenter,KJS::Identifier("onchar"),params);
+}
+
+bool KateIndentJScriptImpl::processLine(Kate::View *view, const KateDocCursor &line, QString &errorMsg )
+{
+ kdDebug(13050)<<"KateIndentJScriptImpl::processLine"<<endl;
+ if (!setupInterpreter(errorMsg)) return false;
+ return KateIndentJScriptCall(view,errorMsg,m_docWrapper,m_viewWrapper,m_interpreter,*m_indenter,KJS::Identifier("online"),KJS::List());
+}
+
+bool KateIndentJScriptImpl::processNewline( class Kate::View *view, const KateDocCursor &begin, bool needcontinue, QString &errorMsg )
+{
+ kdDebug(13050)<<"KateIndentJScriptImpl::processNewline"<<endl;
+ if (!setupInterpreter(errorMsg)) return false;
+ return KateIndentJScriptCall(view,errorMsg,m_docWrapper,m_viewWrapper,m_interpreter,*m_indenter,KJS::Identifier("onnewline"),KJS::List());
+}
+//END
+
+//BEGIN KateIndentJScriptManager
+KateIndentJScriptManager::KateIndentJScriptManager():KateIndentScriptManagerAbstract()
+{
+ m_scripts.setAutoDelete (true);
+ collectScripts ();
+}
+
+KateIndentJScriptManager::~KateIndentJScriptManager ()
+{
+}
+
+void KateIndentJScriptManager::collectScripts (bool force)
+{
+// If there's something in myModeList the Mode List was already built so, don't do it again
+ if (!m_scripts.isEmpty())
+ return;
+
+
+ // We'll store the scripts list in this config
+ KConfig config("katepartindentjscriptrc", false, false);
+#if 0
+ // figure out if the kate install is too new
+ config.setGroup ("General");
+ if (config.readNumEntry ("Version") > config.readNumEntry ("CachedVersion"))
+ {
+ config.writeEntry ("CachedVersion", config.readNumEntry ("Version"));
+ force = true;
+ }
+#endif
+
+ // Let's get a list of all the .js files
+ QStringList list = KGlobal::dirs()->findAllResources("data","katepart/scripts/indent/*.js",false,true);
+
+ // Let's iterate through the list and build the Mode List
+ for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it )
+ {
+ // Each file has a group ed:
+ QString Group="Cache "+ *it;
+
+ // Let's go to this group
+ config.setGroup(Group);
+
+ // stat the file
+ struct stat sbuf;
+ memset (&sbuf, 0, sizeof(sbuf));
+ stat(QFile::encodeName(*it), &sbuf);
+
+ // If the group exist and we're not forced to read the .js file, let's build myModeList for katepartjscriptrc
+ bool readnew=false;
+ if (!force && config.hasGroup(Group) && (sbuf.st_mtime == config.readNumEntry("lastModified")))
+ {
+ config.setGroup(Group);
+ QString filePath=*it;
+ QString internalName=config.readEntry("internlName","KATE-ERROR");
+ if (internalName=="KATE-ERROR") readnew=true;
+ else
+ {
+ QString niceName=config.readEntry("niceName",internalName);
+ QString copyright=config.readEntry("copyright",i18n("(Unknown)"));
+ double version=config.readDoubleNumEntry("version",0.0);
+ KateIndentJScriptImpl *s=new KateIndentJScriptImpl(
+ internalName,filePath,niceName,copyright,version);
+ m_scripts.insert (internalName, s);
+ }
+ }
+ else readnew=true;
+ if (readnew)
+ {
+ QFileInfo fi (*it);
+
+ if (m_scripts[fi.baseName()])
+ continue;
+
+ QString internalName=fi.baseName();
+ QString filePath=*it;
+ QString niceName=internalName;
+ QString copyright=i18n("(Unknown)");
+ double version=0.0;
+ parseScriptHeader(filePath,&niceName,&copyright,&version);
+ /*save the information for retrieval*/
+ config.setGroup(Group);
+ config.writeEntry("lastModified",sbuf.st_mtime);
+ config.writeEntry("internalName",internalName);
+ config.writeEntry("niceName",niceName);
+ config.writeEntry("copyright",copyright);
+ config.writeEntry("version",version);
+ KateIndentJScriptImpl *s=new KateIndentJScriptImpl(
+ internalName,filePath,niceName,copyright,version);
+ m_scripts.insert (internalName, s);
+ }
+ }
+
+ // Syncronize with the file katepartjscriptrc
+ config.sync();
+}
+
+KateIndentScript KateIndentJScriptManager::script(const QString &scriptname) {
+ KateIndentJScriptImpl *s=m_scripts[scriptname];
+ kdDebug(13050)<<scriptname<<"=="<<s<<endl;
+ return KateIndentScript(s);
+}
+
+void KateIndentJScriptManager::parseScriptHeader(const QString &filePath,
+ QString *niceName,QString *copyright,double *version)
+{
+ QFile f(QFile::encodeName(filePath));
+ if (!f.open(IO_ReadOnly) ) {
+ kdDebug(13050)<<"Header could not be parsed, because file could not be opened"<<endl;
+ return;
+ }
+ QTextStream st(&f);
+ st.setEncoding (QTextStream::UnicodeUTF8);
+ if (!st.readLine().upper().startsWith("/**KATE")) {
+ kdDebug(13050)<<"No header found"<<endl;
+ f.close();
+ return;
+ }
+ // here the real parsing begins
+ kdDebug(13050)<<"Parsing indent script header"<<endl;
+ enum {NOTHING=0,COPYRIGHT=1} currentState=NOTHING;
+ QString line;
+ QString tmpblockdata="";
+ QRegExp endExpr("[\\s\\t]*\\*\\*\\/[\\s\\t]*$");
+ QRegExp keyValue("[\\s\\t]*\\*\\s*(.+):(.*)$");
+ QRegExp blockContent("[\\s\\t]*\\*(.*)$");
+ while ((line=st.readLine())!=QString::null) {
+ if (endExpr.exactMatch(line)) {
+ kdDebug(13050)<<"end of config block"<<endl;
+ if (currentState==NOTHING) break;
+ if (currentState==COPYRIGHT) {
+ *copyright=tmpblockdata;
+ break;
+ }
+ Q_ASSERT(0);
+ }
+ if (currentState==NOTHING)
+ {
+ if (keyValue.exactMatch(line)) {
+ QStringList sl=keyValue.capturedTexts();
+ kdDebug(13050)<<"key:"<<sl[1]<<endl<<"value:"<<sl[2]<<endl;
+ kdDebug(13050)<<"key-length:"<<sl[1].length()<<endl<<"value-length:"<<sl[2].length()<<endl;
+ QString key=sl[1];
+ QString value=sl[2];
+ if (key=="NAME") (*niceName)=value.stripWhiteSpace();
+ else if (key=="VERSION") (*version)=value.stripWhiteSpace().toDouble(0);
+ else if (key=="COPYRIGHT")
+ {
+ tmpblockdata="";
+ if (value.stripWhiteSpace().length()>0) tmpblockdata=value;
+ currentState=COPYRIGHT;
+ } else kdDebug(13050)<<"ignoring key"<<endl;
+ }
+ } else {
+ if (blockContent.exactMatch(line))
+ {
+ QString bl=blockContent.capturedTexts()[1];
+ //kdDebug(13050)<<"block content line:"<<bl<<endl<<bl.length()<<" "<<bl.isEmpty()<<endl;
+ if (bl.isEmpty())
+ {
+ (*copyright)=tmpblockdata;
+ kdDebug(13050)<<"Copyright block:"<<endl<<(*copyright)<<endl;
+ currentState=NOTHING;
+ } else tmpblockdata=tmpblockdata+"\n"+bl;
+ }
+ }
+ }
+ f.close();
+}
+//END
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katejscript.h b/kate/part/katejscript.h
new file mode 100644
index 000000000..ac9e20246
--- /dev/null
+++ b/kate/part/katejscript.h
@@ -0,0 +1,233 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2005 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2005 Joseph Wenninger <jowenn@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __kate_jscript_h__
+#define __kate_jscript_h__
+
+#include "../interfaces/document.h"
+#include "kateindentscriptabstracts.h"
+#include <qdict.h>
+#include <kdebug.h>
+/**
+ * Some common stuff
+ */
+class KateDocument;
+class KateView;
+class QString;
+class KateJSDocument;
+class KateJSView;
+class KateJSIndenter;
+class KateDocCursor;
+
+/**
+ * Cool, this is all we need here
+ */
+namespace KJS {
+ class Object;
+ class ObjectImp;
+ class Interpreter;
+ class ExecState;
+}
+
+/**
+ * Whole Kate Part scripting in one classs
+ * Allow subclassing to allow specialized scripting engine for indenters
+ */
+class KateJScript
+{
+ public:
+ /**
+ * generate new global interpreter for part scripting
+ */
+ KateJScript ();
+
+ /**
+ * be destructive
+ */
+ virtual ~KateJScript ();
+
+ /**
+ * creates a JS wrapper object for given KateDocument
+ * @param exec execution state, to find out interpreter to use
+ * @param doc document object to wrap
+ * @return new js wrapper object
+ */
+ KJS::ObjectImp *wrapDocument (KJS::ExecState *exec, KateDocument *doc);
+
+ /**
+ * creates a JS wrapper object for given KateView
+ * @param exec execution state, to find out interpreter to use
+ * @param view view object to wrap
+ * @return new js wrapper object
+ */
+ KJS::ObjectImp *wrapView (KJS::ExecState *exec, KateView *view);
+
+ /**
+ * execute given script
+ * the script will get the doc and view exposed via document and view object
+ * in global scope
+ * @param view view to expose
+ * @param script source code of script to execute
+ * @param errorMsg error to return if no success
+ * @return success or not?
+ */
+ bool execute (KateView *view, const QString &script, QString &errorMsg);
+
+ protected:
+ /**
+ * global object of interpreter
+ */
+ KJS::Object *m_global;
+
+ /**
+ * js interpreter
+ */
+ KJS::Interpreter *m_interpreter;
+
+ /**
+ * object for document
+ */
+ KJS::Object *m_document;
+
+ /**
+ * object for view
+ */
+ KJS::Object *m_view;
+};
+
+class KateJScriptManager : public Kate::Command
+{
+ private:
+ /**
+ * Internal used Script Representation
+ */
+ class Script
+ {
+ public:
+ /**
+ * get desktop filename
+ * @return desktop filename
+ */
+ inline QString desktopFilename () { return filename.left(filename.length()-2).append ("desktop"); }
+
+ public:
+ /**
+ * command name, as used for command line and more
+ */
+ QString name;
+
+ /**
+ * filename of the script
+ */
+ QString filename;
+
+ /**
+ * has it a desktop file?
+ */
+ bool desktopFileExists;
+ };
+
+ public:
+ KateJScriptManager ();
+ ~KateJScriptManager ();
+
+ private:
+ /**
+ * go, search our scripts
+ * @param force force cache updating?
+ */
+ void collectScripts (bool force = false);
+
+ //
+ // Here we deal with the Kate::Command stuff
+ //
+ public:
+ /**
+ * execute command
+ * @param view view to use for execution
+ * @param cmd cmd string
+ * @param errorMsg error to return if no success
+ * @return success
+ */
+ bool exec( class Kate::View *view, const QString &cmd, QString &errorMsg );
+
+ /**
+ * get help for a command
+ * @param view view to use
+ * @param cmd cmd name
+ * @param msg help message
+ * @return help available or not
+ */
+ bool help( class Kate::View *view, const QString &cmd, QString &msg );
+
+ /**
+ * supported commands as prefixes
+ * @return prefix list
+ */
+ QStringList cmds();
+
+ private:
+ /**
+ * we need to know somewhere which scripts are around
+ */
+ QDict<KateJScriptManager::Script> m_scripts;
+};
+
+class KateIndentJScriptImpl: public KateIndentScriptImplAbstract {
+ public:
+ KateIndentJScriptImpl(const QString& internalName,
+ const QString &filePath, const QString &niceName,
+ const QString &copyright, double version);
+ ~KateIndentJScriptImpl();
+
+ virtual bool processChar( class Kate::View *view, QChar c, QString &errorMsg );
+ virtual bool processLine( class Kate::View *view, const KateDocCursor &line, QString &errorMsg );
+ virtual bool processNewline( class Kate::View *view, const KateDocCursor &begin, bool needcontinue, QString &errorMsg );
+ protected:
+ virtual void decRef();
+ private:
+ KateJSView *m_viewWrapper;
+ KateJSDocument *m_docWrapper;
+ KJS::Object *m_indenter;
+ KJS::Interpreter *m_interpreter;
+ bool setupInterpreter(QString &errorMsg);
+ void deleteInterpreter();
+};
+
+class KateIndentJScriptManager: public KateIndentScriptManagerAbstract
+{
+
+ public:
+ KateIndentJScriptManager ();
+ virtual ~KateIndentJScriptManager ();
+ virtual KateIndentScript script(const QString &scriptname);
+ private:
+ /**
+ * go, search our scripts
+ * @param force force cache updating?
+ */
+ void collectScripts (bool force = false);
+ void parseScriptHeader(const QString &filePath,
+ QString *niceName,QString *copyright,double *version);
+ QDict<KateIndentJScriptImpl> m_scripts;
+};
+
+#endif
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katekeyinterceptorfunctor.h b/kate/part/katekeyinterceptorfunctor.h
new file mode 100644
index 000000000..0126ff814
--- /dev/null
+++ b/kate/part/katekeyinterceptorfunctor.h
@@ -0,0 +1,25 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2004 Joseph Wenninger <jowenn@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef _KATE_KEY_INTERCEPTOR_FUNCTOR_H
+#define _KATE_KEY_INTERCEPTOR_FUNCTOR_H
+#include <kshortcut.h>
+class KateKeyInterceptorFunctor {
+ public:
+ virtual bool operator()(KKey key)=0;
+};
+#endif
diff --git a/kate/part/katelinerange.cpp b/kate/part/katelinerange.cpp
new file mode 100644
index 000000000..7500b4e8a
--- /dev/null
+++ b/kate/part/katelinerange.cpp
@@ -0,0 +1,75 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2002,2003 Hamish Rodda <rodda@kde.org>
+ Copyright (C) 2003 Anakim Border <aborder@sources.sourceforge.net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "katelinerange.h"
+
+KateLineRange::KateLineRange()
+ : line(-1)
+ , virtualLine(-1)
+ , startCol(-1)
+ , endCol(-1)
+ , startX(-1)
+ , endX(-1)
+ , dirty(false)
+ , viewLine(-1)
+ , wrap(false)
+ , startsInvisibleBlock(false)
+ , shiftX(0)
+{
+}
+
+KateLineRange::~KateLineRange ()
+{
+}
+
+void KateLineRange::clear()
+{
+ line = -1;
+ virtualLine = -1;
+ startCol = -1;
+ endCol = -1;
+ startX = -1;
+ shiftX = 0;
+ endX = -1;
+ viewLine = -1;
+ wrap = false;
+ startsInvisibleBlock = false;
+}
+
+bool operator> (const KateLineRange& r, const KateTextCursor& c)
+{
+ return r.line > c.line() || r.endCol > c.col();
+}
+
+bool operator>= (const KateLineRange& r, const KateTextCursor& c)
+{
+ return r.line > c.line() || r.endCol >= c.col();
+}
+
+bool operator< (const KateLineRange& r, const KateTextCursor& c)
+{
+ return r.line < c.line() || r.startCol < c.col();
+}
+
+bool operator<= (const KateLineRange& r, const KateTextCursor& c)
+{
+ return r.line < c.line() || r.startCol <= c.col();
+}
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katelinerange.h b/kate/part/katelinerange.h
new file mode 100644
index 000000000..0557d565d
--- /dev/null
+++ b/kate/part/katelinerange.h
@@ -0,0 +1,70 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2002,2003 Hamish Rodda <rodda@kde.org>
+ Copyright (C) 2003 Anakim Border <aborder@sources.sourceforge.net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef _KATE_LINERANGE_H_
+#define _KATE_LINERANGE_H_
+
+#include "katecursor.h"
+
+class KateLineRange
+{
+ public:
+ KateLineRange();
+ virtual ~KateLineRange ();
+
+ void clear();
+
+ inline bool includesCursor (const KateTextCursor& realCursor) const
+ {
+ return realCursor.line() == line && realCursor.col() >= startCol && (!wrap || realCursor.col() < endCol);
+ }
+
+ inline int xOffset () const
+ {
+ return startX ? shiftX : 0;
+ }
+
+ friend bool operator> (const KateLineRange& r, const KateTextCursor& c);
+ friend bool operator>= (const KateLineRange& r, const KateTextCursor& c);
+ friend bool operator< (const KateLineRange& r, const KateTextCursor& c);
+ friend bool operator<= (const KateLineRange& r, const KateTextCursor& c);
+
+ int line;
+ int virtualLine;
+ int startCol;
+ int endCol;
+ int startX;
+ int endX;
+
+ bool dirty;
+ int viewLine;
+ bool wrap;
+ bool startsInvisibleBlock;
+
+ // This variable is used as follows:
+ // non-dynamic-wrapping mode: unused
+ // dynamic wrapping mode:
+ // first viewLine of a line: the X position of the first non-whitespace char
+ // subsequent viewLines: the X offset from the left of the display.
+ //
+ // this is used to provide a dynamic-wrapping-retains-indent feature.
+ int shiftX;
+};
+
+#endif
diff --git a/kate/part/kateluaindentscript.cpp b/kate/part/kateluaindentscript.cpp
new file mode 100644
index 000000000..a8872c0e8
--- /dev/null
+++ b/kate/part/kateluaindentscript.cpp
@@ -0,0 +1,528 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2005 Joseph Wenninger <jowenn@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "config.h"
+#ifdef HAVE_LUA
+
+#include "kateluaindentscript.h"
+#include "katedocument.h"
+#include "kateview.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <kstandarddirs.h>
+
+#include <kconfig.h>
+#include <kglobal.h>
+#include <klocale.h>
+
+extern "C" {
+#include <lua.h>
+#include <lualib.h>
+}
+
+#define ONCHAR 1
+#define ONNEWLINE 2
+#define ONCHARSTR "kateonchar"
+#define ONNEWLINESTR "kateonnewline"
+
+#define katelua_registerFunc(n,f,t) \
+ (lua_pushstring(m_interpreter, n), \
+ lua_pushcfunction(m_interpreter, f), \
+ lua_settable(m_interpreter, t))
+
+#define katelua_registerNumConst(n,v,t) \
+ (lua_pushstring(m_interpreter, n), \
+ lua_pushnumber(m_interpreter, v), \
+ lua_settable(m_interpreter, t))
+
+//BEGIN temporary, try to use registry later
+static KateDocument *katelua_doc;
+static Kate::View *katelua_view;
+//END
+
+
+
+//BEGIN STATIC BINDING FUNCTIONS
+typedef struct KATELUA_FUNCTIONS {
+ char *name;
+ lua_CFunction func;
+} KATELUA_FUNCTIONS;
+
+static int katelua_katedebug(lua_State *L) {
+ int n=lua_gettop(L);
+ for (int i=1;i<=n;i++) {
+ if (lua_isnil(L,i)) kdDebug()<<"NIL VALUE"<<endl;
+ else if (lua_isstring(L,i)) kdDebug()<<lua_tostring(L,i)<<endl;
+ else if (lua_isboolean(L,i)) kdDebug()<<(bool)lua_toboolean(L,i)<<endl;
+ else if (lua_isnumber(L,i)) kdDebug()<<lua_tonumber(L,i)<<endl;
+ else kdDebug()<<"Invalid type for katedebug:"<<lua_type(L,i)<<endl;
+ }
+ return 0;
+}
+
+static int katelua_indenter_register(lua_State *L) {
+ int n=lua_gettop(L);
+ if (n!=2) {
+ lua_pushstring(L,i18n("indenter.register requires 2 parameters (event id, function to call)").utf8().data());
+ lua_error(L);
+ }
+ if ( (!lua_isfunction(L,2)) || (!lua_isnumber(L,1)))
+ {
+ /*if (lua_isnumber(L,1)) kdDebug()<<"A"<<endl;
+ if (lua_isfunction(L,2)) kdDebug()<<"B"<<endl;
+ kdDebug()<<lua_type(L,2)<<endl;*/
+ lua_pushstring(L,i18n("indenter.register requires 2 parameters (event id (number), function to call (function))").utf8().data());
+ lua_error(L);
+ }
+ switch ((int)lua_tonumber(L,1))
+ {
+ case ONCHAR:
+ lua_pushstring(L,ONCHARSTR);
+ lua_pushstring(L,ONCHARSTR);
+ break;
+ case ONNEWLINE:
+ lua_pushstring(L,ONNEWLINESTR);
+ lua_pushstring(L,ONNEWLINESTR);
+ break;
+ default:
+ lua_pushstring(L,i18n("indenter.register:invalid event id").utf8().data());
+ lua_error(L);
+ }
+ lua_gettable(L,LUA_REGISTRYINDEX);
+ if (!lua_isnil(L,lua_gettop(L))) {
+ lua_pushstring(L,i18n("indenter.register:there is already a function set for given").utf8().data());
+ lua_error(L);
+ }
+ lua_pop(L,1);
+ lua_pushvalue(L,2);
+ lua_settable(L,LUA_REGISTRYINDEX);
+ kdDebug()<<"katelua_indenter_register: Success"<<endl;
+ return 0;
+}
+
+
+static int katelua_document_textline(lua_State *L) {
+ if (lua_gettop(L)!=1) {
+ lua_pushstring(L,i18n("document.textLine:One parameter (line number) required").utf8().data());
+ lua_error(L);
+ }
+ if (!lua_isnumber(L,1)) {
+ lua_pushstring(L,i18n("document.textLine:One parameter (line number) required (number)").utf8().data());
+ lua_error(L);
+ }
+ lua_pushstring(L,katelua_doc->textLine((uint)lua_tonumber(L,1)).utf8().data());
+ return 1;
+}
+
+static int katelua_document_removeText(lua_State *L) {
+ if (lua_gettop(L)!=4) {
+ lua_pushstring(L,i18n("document.removeText:Four parameters needed (start line, start col,end line, end col)").utf8().data());
+ lua_error(L);
+ }
+ if ((!lua_isnumber(L,1)) || (!lua_isnumber(L,2)) ||(!lua_isnumber(L,3)) || (!lua_isnumber(L,4))) {
+ lua_pushstring(L,i18n("document.removeText:Four parameters needed (start line, start col,end line, end col) (4x number)").utf8().data());
+ lua_error(L);
+ }
+ lua_pushboolean(L,katelua_doc->removeText((uint)lua_tonumber(L,1),(uint)lua_tonumber(L,2),(uint)lua_tonumber(L,3),(uint)lua_tonumber(L,4)));
+ return 1;
+}
+
+static int katelua_document_insertText(lua_State *L) {
+ if (lua_gettop(L)!=3) {
+ lua_pushstring(L,i18n("document.insertText:Three parameters needed (line,col,text)").utf8().data());
+ lua_error(L);
+ }
+ if ((!lua_isnumber(L,1)) || (!lua_isnumber(L,2)) ||(!lua_isstring(L,3)) ) {
+ lua_pushstring(L,i18n("document.removeText:Three parameters needed (line,col,text) (number,number,string)").utf8().data());
+ lua_error(L);
+ }
+ lua_pushboolean(L,katelua_doc->insertText((uint)lua_tonumber(L,1),(uint)lua_tonumber(L,2),QString::fromUtf8(lua_tostring(L,3))));
+ return 1;
+}
+
+static int katelua_view_cursorline(lua_State *L) {
+ lua_pushnumber(L,katelua_view->cursorLine());
+ return 1;
+}
+static int katelua_view_cursorcolumn(lua_State *L) {
+ lua_pushnumber(L,katelua_view->cursorColumn());
+ return 1;
+}
+static int katelua_view_cursorposition(lua_State *L) {
+ lua_pushnumber(L,katelua_view->cursorLine());
+ lua_pushnumber(L,katelua_view->cursorColumn());
+ return 2;
+
+}
+static int katelua_view_setcursorpositionreal(lua_State *L) {
+ return 0;
+}
+
+static const struct KATELUA_FUNCTIONS katelua_documenttable[4]= {
+{"textLine",katelua_document_textline},
+{"removeText",katelua_document_removeText},
+{"insertText",katelua_document_insertText},
+{0,0}
+};
+
+static const struct KATELUA_FUNCTIONS katelua_viewtable[5]= {
+{"cursorLine",katelua_view_cursorline},
+{"cursorColumn",katelua_view_cursorcolumn},
+{"cursorPosition",katelua_view_cursorposition},
+{"setCursorPositionReal",katelua_view_setcursorpositionreal},
+{0,0}
+};
+
+static void kateregistertable(lua_State* m_interpreter,const KATELUA_FUNCTIONS funcs[],char * tablename) {
+ lua_newtable(m_interpreter);
+ int table=lua_gettop(m_interpreter);
+ for (uint i=0;funcs[i].name!=0;i++)
+ {
+ katelua_registerFunc(funcs[i].name,funcs[i].func,table);
+ }
+
+ lua_pushstring(m_interpreter,tablename);
+ lua_pushvalue(m_interpreter,table);
+ lua_settable(m_interpreter,LUA_GLOBALSINDEX);
+ lua_pop(m_interpreter,1);
+
+}
+
+//END STATIC BINDING FUNCTIONS
+
+
+//BEGIN KateLUAIndentScriptImpl
+KateLUAIndentScriptImpl::KateLUAIndentScriptImpl(const QString& internalName,
+ const QString &filePath, const QString &niceName,
+ const QString &copyright, double version):
+ KateIndentScriptImplAbstract(internalName,filePath,niceName,copyright,version),m_interpreter(0)/*,m_indenter(0)*/
+{
+}
+
+
+KateLUAIndentScriptImpl::~KateLUAIndentScriptImpl()
+{
+ deleteInterpreter();
+}
+
+void KateLUAIndentScriptImpl::decRef()
+{
+ KateIndentScriptImplAbstract::decRef();
+ if (refCount()==0)
+ {
+ deleteInterpreter();
+ }
+}
+
+void KateLUAIndentScriptImpl::deleteInterpreter()
+{
+ if (m_interpreter)
+ {
+ lua_close(m_interpreter);
+ m_interpreter=0;
+ }
+}
+
+bool KateLUAIndentScriptImpl::setupInterpreter(QString &errorMsg)
+{
+ if (m_interpreter) return true;
+ m_interpreter=lua_open();
+
+ if (!m_interpreter)
+ {
+ errorMsg=i18n("LUA interpreter could not be initialized");
+ return false;
+ }
+ luaopen_base(m_interpreter);
+ luaopen_string( m_interpreter );
+ luaopen_table( m_interpreter );
+ luaopen_math( m_interpreter );
+ luaopen_io( m_interpreter );
+ luaopen_debug( m_interpreter );
+
+
+ /*indenter callback setup table*/
+ lua_newtable(m_interpreter);
+ int indentertable=lua_gettop(m_interpreter);
+ katelua_registerFunc("register",katelua_indenter_register,indentertable);
+ katelua_registerNumConst("OnChar",ONCHAR,indentertable);
+ katelua_registerNumConst("OnNewline",ONNEWLINE,indentertable);
+ lua_pushstring(m_interpreter,"indenter");
+ lua_pushvalue(m_interpreter,indentertable);
+ lua_settable(m_interpreter,LUA_GLOBALSINDEX);
+ lua_pop(m_interpreter,1);
+
+ /*debug*/
+ katelua_registerFunc("katedebug",katelua_katedebug,LUA_GLOBALSINDEX);
+
+ /*document interface*/
+ kateregistertable(m_interpreter,katelua_documenttable,"document");
+ /*view interface*/
+ kateregistertable(m_interpreter,katelua_viewtable,"view");
+
+ /*open script*/
+ lua_pushstring(m_interpreter,"dofile");
+ lua_gettable(m_interpreter,LUA_GLOBALSINDEX);
+ QCString fn=QFile::encodeName(filePath());
+ lua_pushstring(m_interpreter,fn.data());
+ int execresult=lua_pcall(m_interpreter,1,1,0);
+ if (execresult==0) {
+ kdDebug()<<"Lua script has been loaded successfully. Lua interpreter version:"<<lua_version()<<endl;
+ return true;
+ } else {
+ errorMsg=i18n("Lua indenting script had errors: %1").arg(lua_tostring(m_interpreter,lua_gettop(m_interpreter)));
+ kdDebug()<<errorMsg<<endl;
+ deleteInterpreter();
+
+ return false;
+ }
+}
+
+
+bool KateLUAIndentScriptImpl::processChar(Kate::View *view, QChar c, QString &errorMsg )
+{
+ if (!setupInterpreter(errorMsg)) return false;
+ katelua_doc=((KateView*)view)->doc();
+ katelua_view=view;
+ int oldtop=lua_gettop(m_interpreter);
+ lua_pushstring(m_interpreter,ONCHARSTR);
+ lua_gettable(m_interpreter,LUA_REGISTRYINDEX);
+ bool result=true;
+ if (!lua_isnil(m_interpreter,lua_gettop(m_interpreter)))
+ {
+ lua_pushstring(m_interpreter,QString(c).utf8().data());
+ if (lua_pcall(m_interpreter,1,0,0)!=0)
+ {
+ errorMsg=i18n("Lua indenting script had errors: %1").arg(lua_tostring(m_interpreter,lua_gettop(m_interpreter)));
+ kdDebug()<<errorMsg<<endl;
+ result=false;
+ }
+ }
+ lua_settop(m_interpreter,oldtop);
+ return result;
+}
+
+bool KateLUAIndentScriptImpl::processLine(Kate::View *view, const KateDocCursor &line, QString &errorMsg )
+{
+ if (!setupInterpreter(errorMsg)) return false;
+ return true;
+}
+
+bool KateLUAIndentScriptImpl::processNewline( class Kate::View *view, const KateDocCursor &begin, bool needcontinue, QString &errorMsg )
+{
+ if (!setupInterpreter(errorMsg)) return false;
+ katelua_doc=((KateView*)view)->doc();
+ katelua_view=view;
+ int oldtop=lua_gettop(m_interpreter);
+ lua_pushstring(m_interpreter,ONNEWLINESTR);
+ lua_gettable(m_interpreter,LUA_REGISTRYINDEX);
+ bool result=true;
+ if (!lua_isnil(m_interpreter,lua_gettop(m_interpreter)))
+ {
+ if (lua_pcall(m_interpreter,0,0,0)!=0)
+ {
+ errorMsg=i18n("Lua indenting script had errors: %1").arg(lua_tostring(m_interpreter,lua_gettop(m_interpreter)));
+ kdDebug()<<errorMsg<<endl;
+ result=false;
+ }
+ }
+ lua_settop(m_interpreter,oldtop);
+ return result;
+}
+//END
+
+//BEGIN KateLUAIndentScriptManager
+KateLUAIndentScriptManager::KateLUAIndentScriptManager():KateIndentScriptManagerAbstract()
+{
+ collectScripts();
+}
+
+KateLUAIndentScriptManager::~KateLUAIndentScriptManager ()
+{
+}
+
+void KateLUAIndentScriptManager::collectScripts (bool force)
+{
+// If there's something in myModeList the Mode List was already built so, don't do it again
+ if (!m_scripts.isEmpty())
+ return;
+
+ kdDebug()<<"================================================="<<endl<<"Trying to find Lua scripts"<<endl
+ <<"================================================="<<endl;
+
+ // We'll store the scripts list in this config
+ KConfig config("katepartluaindentscriptrc", false, false);
+#if 0
+ // figure out if the kate install is too new
+ config.setGroup ("General");
+ if (config.readNumEntry ("Version") > config.readNumEntry ("CachedVersion"))
+ {
+ config.writeEntry ("CachedVersion", config.readNumEntry ("Version"));
+ force = true;
+ }
+#endif
+
+ // Let's get a list of all the .js files
+ QStringList list = KGlobal::dirs()->findAllResources("data","katepart/scripts/indent/*.lua",false,true);
+
+ // Let's iterate through the list and build the Mode List
+ for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it )
+ {
+ // Each file has a group ed:
+ QString Group="Cache "+ *it;
+
+ // Let's go to this group
+ config.setGroup(Group);
+
+ // stat the file
+ struct stat sbuf;
+ memset (&sbuf, 0, sizeof(sbuf));
+ stat(QFile::encodeName(*it), &sbuf);
+ kdDebug()<<"Lua script file:"<<(*it)<<endl;
+ // If the group exist and we're not forced to read the .js file, let's build myModeList for katepartjscriptrc
+ bool readnew=false;
+ if (!force && config.hasGroup(Group) && (sbuf.st_mtime == config.readNumEntry("lastModified")))
+ {
+ config.setGroup(Group);
+ QString filePath=*it;
+ QString internalName=config.readEntry("internlName","KATE-ERROR");
+ if (internalName=="KATE-ERROR") readnew=true;
+ else
+ {
+ QString niceName=config.readEntry("niceName",internalName);
+ QString copyright=config.readEntry("copyright",i18n("(Unknown)"));
+ double version=config.readDoubleNumEntry("version",0.0);
+ KateLUAIndentScriptImpl *s=new KateLUAIndentScriptImpl(
+ internalName,filePath,niceName,copyright,version);
+ m_scripts.insert (internalName, s);
+ }
+ }
+ else readnew=true;
+ if (readnew)
+ {
+ QFileInfo fi (*it);
+
+ if (m_scripts[fi.baseName()])
+ continue;
+
+ QString internalName=fi.baseName();
+ QString filePath=*it;
+ QString niceName=internalName;
+ QString copyright=i18n("(Unknown)");
+ double version=0.0;
+ parseScriptHeader(filePath,&niceName,&copyright,&version);
+ /*save the information for retrieval*/
+ config.setGroup(Group);
+ config.writeEntry("lastModified",sbuf.st_mtime);
+ config.writeEntry("internalName",internalName);
+ config.writeEntry("niceName",niceName);
+ config.writeEntry("copyright",copyright);
+ config.writeEntry("version",version);
+ KateLUAIndentScriptImpl *s=new KateLUAIndentScriptImpl(
+ internalName,filePath,niceName,copyright,version);
+ m_scripts.insert (internalName, s);
+ }
+ }
+
+ // Syncronize with the file katepartjscriptrc
+ config.sync();
+}
+
+KateIndentScript KateLUAIndentScriptManager::script(const QString &scriptname) {
+ KateLUAIndentScriptImpl *s=m_scripts[scriptname];
+ kdDebug(13050)<<scriptname<<"=="<<s<<endl;
+ return KateIndentScript(s);
+}
+
+void KateLUAIndentScriptManager::parseScriptHeader(const QString &filePath,
+ QString *niceName,QString *copyright,double *version)
+{
+#if 0
+ QFile f(QFile::encodeName(filePath));
+ if (!f.open(IO_ReadOnly) ) {
+ kdDebug(13050)<<"Header could not be parsed, because file could not be opened"<<endl;
+ return;
+ }
+ QTextStream st(&f);
+ st.setEncoding (QTextStream::UnicodeUTF8);
+ if (!st.readLine().upper().startsWith("/**KATE")) {
+ kdDebug(13050)<<"No header found"<<endl;
+ f.close();
+ return;
+ }
+ // here the real parsing begins
+ kdDebug(13050)<<"Parsing indent script header"<<endl;
+ enum {NOTHING=0,COPYRIGHT=1} currentState=NOTHING;
+ QString line;
+ QString tmpblockdata="";
+ QRegExp endExpr("[\\s\\t]*\\*\\*\\/[\\s\\t]*$");
+ QRegExp keyValue("[\\s\\t]*\\*\\s*(.+):(.*)$");
+ QRegExp blockContent("[\\s\\t]*\\*(.*)$");
+ while ((line=st.readLine())!=QString::null) {
+ if (endExpr.exactMatch(line)) {
+ kdDebug(13050)<<"end of config block"<<endl;
+ if (currentState==NOTHING) break;
+ if (currentState==COPYRIGHT) {
+ *copyright=tmpblockdata;
+ break;
+ }
+ Q_ASSERT(0);
+ }
+ if (currentState==NOTHING)
+ {
+ if (keyValue.exactMatch(line)) {
+ QStringList sl=keyValue.capturedTexts();
+ kdDebug(13050)<<"key:"<<sl[1]<<endl<<"value:"<<sl[2]<<endl;
+ kdDebug(13050)<<"key-length:"<<sl[1].length()<<endl<<"value-length:"<<sl[2].length()<<endl;
+ QString key=sl[1];
+ QString value=sl[2];
+ if (key=="NAME") (*niceName)=value.stripWhiteSpace();
+ else if (key=="VERSION") (*version)=value.stripWhiteSpace().toDouble(0);
+ else if (key=="COPYRIGHT")
+ {
+ tmpblockdata="";
+ if (value.stripWhiteSpace().length()>0) tmpblockdata=value;
+ currentState=COPYRIGHT;
+ } else kdDebug(13050)<<"ignoring key"<<endl;
+ }
+ } else {
+ if (blockContent.exactMatch(line))
+ {
+ QString bl=blockContent.capturedTexts()[1];
+ //kdDebug(13050)<<"block content line:"<<bl<<endl<<bl.length()<<" "<<bl.isEmpty()<<endl;
+ if (bl.isEmpty())
+ {
+ (*copyright)=tmpblockdata;
+ kdDebug(13050)<<"Copyright block:"<<endl<<(*copyright)<<endl;
+ currentState=NOTHING;
+ } else tmpblockdata=tmpblockdata+"\n"+bl;
+ }
+ }
+ }
+ f.close();
+#endif
+}
+//END
+
+#endif
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/kateluaindentscript.h b/kate/part/kateluaindentscript.h
new file mode 100644
index 000000000..4cccf85ba
--- /dev/null
+++ b/kate/part/kateluaindentscript.h
@@ -0,0 +1,69 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2005 Joseph Wenninger <jowenn@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "config.h"
+#ifdef HAVE_LUA
+
+#ifndef _KATELUAINDENTSCRIPT_H_
+#define _KATELUAINDENTSCRIPT_H_
+
+#include "kateindentscriptabstracts.h"
+#include <qdict.h>
+
+struct lua_State;
+
+class KateLUAIndentScriptImpl: public KateIndentScriptImplAbstract {
+ public:
+ KateLUAIndentScriptImpl(const QString& internalName,
+ const QString &filePath, const QString &niceName,
+ const QString &copyright, double version);
+ ~KateLUAIndentScriptImpl();
+
+ virtual bool processChar( class Kate::View *view, QChar c, QString &errorMsg );
+ virtual bool processLine( class Kate::View *view, const KateDocCursor &line, QString &errorMsg );
+ virtual bool processNewline( class Kate::View *view, const KateDocCursor &begin, bool needcontinue, QString &errorMsg );
+ protected:
+ virtual void decRef();
+ private:
+ bool setupInterpreter(QString &errorMsg);
+ void deleteInterpreter();
+ struct lua_State *m_interpreter;
+};
+
+class KateLUAIndentScriptManager: public KateIndentScriptManagerAbstract
+{
+
+ public:
+ KateLUAIndentScriptManager ();
+ virtual ~KateLUAIndentScriptManager ();
+ virtual KateIndentScript script(const QString &scriptname);
+ private:
+ /**
+ * go, search our scripts
+ * @param force force cache updating?
+ */
+ void collectScripts (bool force = false);
+ void parseScriptHeader(const QString &filePath,
+ QString *niceName,QString *copyright,double *version);
+ QDict<KateLUAIndentScriptImpl> m_scripts;
+};
+
+#endif
+
+#endif
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/kateprinter.cpp b/kate/part/kateprinter.cpp
new file mode 100644
index 000000000..380c50216
--- /dev/null
+++ b/kate/part/kateprinter.cpp
@@ -0,0 +1,1005 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (c) 2001-2002 Michael Goffioul <kdeprint@swing.be>
+ * Complete rewrite on Sat Jun 15 2002 (c) Anders Lund <anders@alweb.dk>
+ * Copyright (c) 2002, 2003 Anders Lund <anders@alweb.dk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#include "kateprinter.h"
+
+#include <kateconfig.h>
+#include <katedocument.h>
+#include <katefactory.h>
+#include <katehighlight.h>
+#include <katelinerange.h>
+#include <katerenderer.h>
+#include <kateschema.h>
+#include <katetextline.h>
+
+#include <kapplication.h>
+#include <kcolorbutton.h>
+#include <kdebug.h>
+#include <kdialog.h> // for spacingHint()
+#include <kfontdialog.h>
+#include <klocale.h>
+#include <kprinter.h>
+#include <kurl.h>
+#include <kuser.h> // for loginName
+
+#include <qpainter.h>
+#include <qpopupmenu.h>
+#include <qpaintdevicemetrics.h>
+#include <qcheckbox.h>
+#include <qcombobox.h>
+#include <qgroupbox.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qlineedit.h>
+#include <qspinbox.h>
+#include <qstringlist.h>
+#include <qwhatsthis.h>
+
+//BEGIN KatePrinter
+bool KatePrinter::print (KateDocument *doc)
+{
+#ifndef Q_WS_WIN //TODO: reenable
+ KPrinter printer;
+
+ // docname is now always there, including the right Untitled name
+ printer.setDocName(doc->docName());
+
+ KatePrintTextSettings *kpts = new KatePrintTextSettings(&printer, NULL);
+ kpts->enableSelection( doc->hasSelection() );
+ printer.addDialogPage( kpts );
+ printer.addDialogPage( new KatePrintHeaderFooter(&printer, NULL) );
+ printer.addDialogPage( new KatePrintLayout(&printer, NULL) );
+
+ if ( printer.setup( kapp->mainWidget(), i18n("Print %1").arg(printer.docName()) ) )
+ {
+ KateRenderer renderer(doc);
+ //renderer.config()->setSchema (1);
+ renderer.setPrinterFriendly(true);
+
+ QPainter paint( &printer );
+ QPaintDeviceMetrics pdm( &printer );
+ /*
+ We work in tree cycles:
+ 1) initialize variables and retrieve print settings
+ 2) prepare data according to those settings
+ 3) draw to the printer
+ */
+ uint pdmWidth = pdm.width();
+ uint y = 0;
+ uint xstart = 0; // beginning point for painting lines
+ uint lineCount = 0;
+ uint maxWidth = pdmWidth;
+ uint headerWidth = pdmWidth;
+ int startCol = 0;
+ int endCol = 0;
+ bool needWrap = true;
+ bool pageStarted = true;
+
+ // Text Settings Page
+ bool selectionOnly = ( doc->hasSelection() &&
+ ( printer.option("app-kate-printselection") == "true" ) );
+ int selStartCol = 0;
+ int selEndCol = 0;
+
+ bool useGuide = ( printer.option("app-kate-printguide") == "true" );
+ int guideHeight = 0;
+ int guideCols = 0;
+
+ bool printLineNumbers = ( printer.option("app-kate-printlinenumbers") == "true" );
+ uint lineNumberWidth( 0 );
+
+ // Header/Footer Page
+ QFont headerFont; // used for header/footer
+ QString f = printer.option("app-kate-hffont");
+ if (!f.isEmpty())
+ headerFont.fromString( f );
+
+ bool useHeader = (printer.option("app-kate-useheader") == "true");
+ QColor headerBgColor(printer.option("app-kate-headerbg"));
+ QColor headerFgColor(printer.option("app-kate-headerfg"));
+ uint headerHeight( 0 ); // further init only if needed
+ QStringList headerTagList; // do
+ bool headerDrawBg = false; // do
+
+ bool useFooter = (printer.option("app-kate-usefooter") == "true");
+ QColor footerBgColor(printer.option("app-kate-footerbg"));
+ QColor footerFgColor(printer.option("app-kate-footerfg"));
+ uint footerHeight( 0 ); // further init only if needed
+ QStringList footerTagList = 0; // do
+ bool footerDrawBg = 0; // do
+
+ // Layout Page
+ renderer.config()->setSchema( KateFactory::self()->schemaManager()->number(
+ printer.option("app-kate-colorscheme") ) );
+ bool useBackground = ( printer.option("app-kate-usebackground") == "true" );
+ bool useBox = (printer.option("app-kate-usebox") == "true");
+ int boxWidth(printer.option("app-kate-boxwidth").toInt());
+ QColor boxColor(printer.option("app-kate-boxcolor"));
+ int innerMargin = useBox ? printer.option("app-kate-boxmargin").toInt() : 6;
+
+ // Post initialization
+ uint maxHeight = (useBox ? pdm.height()-innerMargin : pdm.height());
+ uint currentPage( 1 );
+ uint lastline = doc->lastLine(); // nessecary to print selection only
+ uint firstline( 0 );
+
+ KateHlItemDataList ilist;
+
+ if (useGuide)
+ doc->highlight()->getKateHlItemDataListCopy (renderer.config()->schema(), ilist);
+
+ /*
+ Now on for preparations...
+ during preparations, variable names starting with a "_" means
+ those variables are local to the enclosing block.
+ */
+ {
+ if ( selectionOnly )
+ {
+ // set a line range from the first selected line to the last
+ firstline = doc->selStartLine();
+ selStartCol = doc->selStartCol();
+ lastline = doc->selEndLine();
+ selEndCol = doc->selEndCol();
+
+ lineCount = firstline;
+ }
+
+ if ( printLineNumbers )
+ {
+ // figure out the horiizontal space required
+ QString s( QString("%1 ").arg( doc->numLines() ) );
+ s.fill('5', -1); // some non-fixed fonts haven't equally wide numbers
+ // FIXME calculate which is actually the widest...
+ lineNumberWidth = renderer.currentFontMetrics()->width( s );
+ // a small space between the line numbers and the text
+ int _adj = renderer.currentFontMetrics()->width( "5" );
+ // adjust available width and set horizontal start point for data
+ maxWidth -= (lineNumberWidth + _adj);
+ xstart += lineNumberWidth + _adj;
+ }
+
+ if ( useHeader || useFooter )
+ {
+ // Set up a tag map
+ // This retrieves all tags, ued or not, but
+ // none of theese operations should be expensive,
+ // and searcing each tag in the format strings is avoided.
+ QDateTime dt = QDateTime::currentDateTime();
+ QMap<QString,QString> tags;
+
+ KUser u (KUser::UseRealUserID);
+ tags["u"] = u.loginName();
+
+ tags["d"] = KGlobal::locale()->formatDateTime(dt, true, false);
+ tags["D"] = KGlobal::locale()->formatDateTime(dt, false, false);
+ tags["h"] = KGlobal::locale()->formatTime(dt.time(), false);
+ tags["y"] = KGlobal::locale()->formatDate(dt.date(), true);
+ tags["Y"] = KGlobal::locale()->formatDate(dt.date(), false);
+ tags["f"] = doc->url().fileName();
+ tags["U"] = doc->url().prettyURL();
+ if ( selectionOnly )
+ {
+ QString s( i18n("(Selection of) ") );
+ tags["f"].prepend( s );
+ tags["U"].prepend( s );
+ }
+
+ QRegExp reTags( "%([dDfUhuyY])" ); // TODO tjeck for "%%<TAG>"
+
+ if (useHeader)
+ {
+ headerDrawBg = ( printer.option("app-kate-headerusebg") == "true" );
+ headerHeight = QFontMetrics( headerFont ).height();
+ if ( useBox || headerDrawBg )
+ headerHeight += innerMargin * 2;
+ else
+ headerHeight += 1 + QFontMetrics( headerFont ).leading();
+
+ QString headerTags = printer.option("app-kate-headerformat");
+ int pos = reTags.search( headerTags );
+ QString rep;
+ while ( pos > -1 )
+ {
+ rep = tags[reTags.cap( 1 )];
+ headerTags.replace( (uint)pos, 2, rep );
+ pos += rep.length();
+ pos = reTags.search( headerTags, pos );
+ }
+ headerTagList = QStringList::split('|', headerTags, true);
+
+ if (!headerBgColor.isValid())
+ headerBgColor = Qt::lightGray;
+ if (!headerFgColor.isValid())
+ headerFgColor = Qt::black;
+ }
+
+ if (useFooter)
+ {
+ footerDrawBg = ( printer.option("app-kate-footerusebg") == "true" );
+ footerHeight = QFontMetrics( headerFont ).height();
+ if ( useBox || footerDrawBg )
+ footerHeight += 2*innerMargin;
+ else
+ footerHeight += 1; // line only
+
+ QString footerTags = printer.option("app-kate-footerformat");
+ int pos = reTags.search( footerTags );
+ QString rep;
+ while ( pos > -1 )
+ {
+ rep = tags[reTags.cap( 1 )];
+ footerTags.replace( (uint)pos, 2, rep );
+ pos += rep.length();
+ pos = reTags.search( footerTags, pos );
+ }
+
+ footerTagList = QStringList::split('|', footerTags, true);
+ if (!footerBgColor.isValid())
+ footerBgColor = Qt::lightGray;
+ if (!footerFgColor.isValid())
+ footerFgColor = Qt::black;
+ // adjust maxheight, so we can know when/where to print footer
+ maxHeight -= footerHeight;
+ }
+ } // if ( useHeader || useFooter )
+
+ if ( useBackground )
+ {
+ if ( ! useBox )
+ {
+ xstart += innerMargin;
+ maxWidth -= innerMargin * 2;
+ }
+ }
+
+ if ( useBox )
+ {
+ if (!boxColor.isValid())
+ boxColor = Qt::black;
+ if (boxWidth < 1) // shouldn't be pssible no more!
+ boxWidth = 1;
+ // set maxwidth to something sensible
+ maxWidth -= ( ( boxWidth + innerMargin ) * 2 );
+ xstart += boxWidth + innerMargin;
+ // maxheight too..
+ maxHeight -= boxWidth;
+ }
+ else
+ boxWidth = 0;
+
+ if ( useGuide )
+ {
+ // calculate the height required
+ // the number of columns is a side effect, saved for drawing time
+ // first width is needed
+ int _w = pdmWidth - innerMargin * 2;
+ if ( useBox )
+ _w -= boxWidth * 2;
+ else
+ {
+ if ( useBackground )
+ _w -= ( innerMargin * 2 );
+ _w -= 2; // 1 px line on each side
+ }
+
+ // base of height: margins top/bottom, above and below tetle sep line
+ guideHeight = ( innerMargin * 4 ) + 1;
+
+ // get a title and add the height required to draw it
+ QString _title = i18n("Typographical Conventions for %1").arg(doc->highlight()->name());
+ guideHeight += paint.boundingRect( 0, 0, _w, 1000, Qt::AlignTop|Qt::AlignHCenter, _title ).height();
+
+ // see how many columns we can fit in
+ int _widest( 0 );
+
+ QPtrListIterator<KateHlItemData> it( ilist );
+ KateHlItemData *_d;
+
+ int _items ( 0 );
+ while ( ( _d = it.current()) != 0 )
+ {
+ _widest = kMax( _widest, ((QFontMetrics)(
+ _d->bold() ?
+ _d->italic() ?
+ renderer.config()->fontStruct()->myFontMetricsBI :
+ renderer.config()->fontStruct()->myFontMetricsBold :
+ _d->italic() ?
+ renderer.config()->fontStruct()->myFontMetricsItalic :
+ renderer.config()->fontStruct()->myFontMetrics
+ ) ).width( _d->name ) );
+ _items++;
+ ++it;
+ }
+ guideCols = _w/( _widest + innerMargin );
+ // add height for required number of lines needed given columns
+ guideHeight += renderer.fontHeight() * ( _items/guideCols );
+ if ( _items%guideCols )
+ guideHeight += renderer.fontHeight();
+ }
+
+ // now that we know the vertical amount of space needed,
+ // it is possible to calculate the total number of pages
+ // if needed, that is if any header/footer tag contains "%P".
+ if ( headerTagList.grep("%P").count() || footerTagList.grep("%P").count() )
+ {
+ kdDebug(13020)<<"'%P' found! calculating number of pages..."<<endl;
+ uint _pages = 0;
+ uint _ph = maxHeight;
+ if ( useHeader )
+ _ph -= ( headerHeight + innerMargin );
+ if ( useFooter )
+ _ph -= innerMargin;
+ int _lpp = _ph / renderer.fontHeight();
+ uint _lt = 0, _c=0;
+
+ // add space for guide if required
+ if ( useGuide )
+ _lt += (guideHeight + (renderer.fontHeight() /2)) / renderer.fontHeight();
+ long _lw;
+ for ( uint i = firstline; i < lastline; i++ )
+ {
+ _lw = renderer.textWidth( doc->kateTextLine( i ), -1 );
+ while ( _lw >= 0 )
+ {
+ _c++;
+ _lt++;
+ if ( (int)_lt == _lpp )
+ {
+ _pages++;
+ _lt = 0;
+ }
+ _lw -= maxWidth;
+ if ( ! _lw ) _lw--; // skip lines matching exactly!
+ }
+ }
+ if ( _lt ) _pages++; // last page
+
+ // substitute both tag lists
+ QString re("%P");
+ QStringList::Iterator it;
+ for ( it=headerTagList.begin(); it!=headerTagList.end(); ++it )
+ (*it).replace( re, QString( "%1" ).arg( _pages ) );
+ for ( it=footerTagList.begin(); it!=footerTagList.end(); ++it )
+ (*it).replace( re, QString( "%1" ).arg( _pages ) );
+ }
+ } // end prepare block
+
+ /*
+ On to draw something :-)
+ */
+ uint _count = 0;
+ while ( lineCount <= lastline )
+ {
+ startCol = 0;
+ endCol = 0;
+ needWrap = true;
+
+ while (needWrap)
+ {
+ if ( y + renderer.fontHeight() >= (uint)(maxHeight) )
+ {
+ kdDebug(13020)<<"Starting new page, "<<_count<<" lines up to now."<<endl;
+ printer.newPage();
+ currentPage++;
+ pageStarted = true;
+ y=0;
+ }
+
+ if ( pageStarted )
+ {
+
+ if ( useHeader )
+ {
+ paint.setPen(headerFgColor);
+ paint.setFont(headerFont);
+ if ( headerDrawBg )
+ paint.fillRect(0, 0, headerWidth, headerHeight, headerBgColor);
+ if (headerTagList.count() == 3)
+ {
+ int valign = ( (useBox||headerDrawBg||useBackground) ?
+ Qt::AlignVCenter : Qt::AlignTop );
+ int align = valign|Qt::AlignLeft;
+ int marg = ( useBox || headerDrawBg ) ? innerMargin : 0;
+ if ( useBox ) marg += boxWidth;
+ QString s;
+ for (int i=0; i<3; i++)
+ {
+ s = headerTagList[i];
+ if (s.find("%p") != -1) s.replace("%p", QString::number(currentPage));
+ paint.drawText(marg, 0, headerWidth-(marg*2), headerHeight, align, s);
+ align = valign|(i == 0 ? Qt::AlignHCenter : Qt::AlignRight);
+ }
+ }
+ if ( ! ( headerDrawBg || useBox || useBackground ) ) // draw a 1 px (!?) line to separate header from contents
+ {
+ paint.drawLine( 0, headerHeight-1, headerWidth, headerHeight-1 );
+ //y += 1; now included in headerHeight
+ }
+ y += headerHeight + innerMargin;
+ }
+
+ if ( useFooter )
+ {
+ if ( ! ( footerDrawBg || useBox || useBackground ) ) // draw a 1 px (!?) line to separate footer from contents
+ paint.drawLine( 0, maxHeight + innerMargin - 1, headerWidth, maxHeight + innerMargin - 1 );
+ if ( footerDrawBg )
+ paint.fillRect(0, maxHeight+innerMargin+boxWidth, headerWidth, footerHeight, footerBgColor);
+ if (footerTagList.count() == 3)
+ {
+ int align = Qt::AlignVCenter|Qt::AlignLeft;
+ int marg = ( useBox || footerDrawBg ) ? innerMargin : 0;
+ if ( useBox ) marg += boxWidth;
+ QString s;
+ for (int i=0; i<3; i++)
+ {
+ s = footerTagList[i];
+ if (s.find("%p") != -1) s.replace("%p", QString::number(currentPage));
+ paint.drawText(marg, maxHeight+innerMargin, headerWidth-(marg*2), footerHeight, align, s);
+ align = Qt::AlignVCenter|(i == 0 ? Qt::AlignHCenter : Qt::AlignRight);
+ }
+ }
+ } // done footer
+
+ if ( useBackground )
+ {
+ // If we have a box, or the header/footer has backgrounds, we want to paint
+ // to the border of those. Otherwise just the contents area.
+ int _y = y, _h = maxHeight - y;
+ if ( useBox )
+ {
+ _y -= innerMargin;
+ _h += 2 * innerMargin;
+ }
+ else
+ {
+ if ( headerDrawBg )
+ {
+ _y -= innerMargin;
+ _h += innerMargin;
+ }
+ if ( footerDrawBg )
+ {
+ _h += innerMargin;
+ }
+ }
+ paint.fillRect( 0, _y, pdmWidth, _h, renderer.config()->backgroundColor());
+ }
+
+ if ( useBox )
+ {
+ paint.setPen(QPen(boxColor, boxWidth));
+ paint.drawRect(0, 0, pdmWidth, pdm.height());
+ if (useHeader)
+ paint.drawLine(0, headerHeight, headerWidth, headerHeight);
+ else
+ y += innerMargin;
+
+ if ( useFooter ) // drawline is not trustable, grr.
+ paint.fillRect( 0, maxHeight+innerMargin, headerWidth, boxWidth, boxColor );
+ }
+
+ if ( useGuide && currentPage == 1 )
+ { // FIXME - this may span more pages...
+ // draw a box unless we have boxes, in which case we end with a box line
+
+ // use color of dsNormal for the title string and the hline
+ KateAttributeList _dsList;
+ KateHlManager::self()->getDefaults ( renderer.config()->schema(), _dsList );
+ paint.setPen( _dsList.at(0)->textColor() );
+ int _marg = 0; // this could be available globally!??
+ if ( useBox )
+ {
+ _marg += (2*boxWidth) + (2*innerMargin);
+ paint.fillRect( 0, y+guideHeight-innerMargin-boxWidth, headerWidth, boxWidth, boxColor );
+ }
+ else
+ {
+ if ( useBackground )
+ _marg += 2*innerMargin;
+ paint.drawRect( _marg, y, pdmWidth-(2*_marg), guideHeight );
+ _marg += 1;
+ y += 1 + innerMargin;
+ }
+ // draw a title string
+ paint.setFont( renderer.config()->fontStruct()->myFontBold );
+ QRect _r;
+ paint.drawText( _marg, y, pdmWidth-(2*_marg), maxHeight - y,
+ Qt::AlignTop|Qt::AlignHCenter,
+ i18n("Typographical Conventions for %1").arg(doc->highlight()->name()), -1, &_r );
+ int _w = pdmWidth - (_marg*2) - (innerMargin*2);
+ int _x = _marg + innerMargin;
+ y += _r.height() + innerMargin;
+ paint.drawLine( _x, y, _x + _w, y );
+ y += 1 + innerMargin;
+ // draw attrib names using their styles
+
+ QPtrListIterator<KateHlItemData> _it( ilist );
+ KateHlItemData *_d;
+ int _cw = _w/guideCols;
+ int _i(0);
+
+ while ( ( _d = _it.current() ) != 0 )
+ {
+ paint.setPen( renderer.attribute(_i)->textColor() );
+ paint.setFont( renderer.attribute(_i)->font( *renderer.currentFont() ) );
+ paint.drawText(( _x + ((_i%guideCols)*_cw)), y, _cw, renderer.fontHeight(),
+ Qt::AlignVCenter|Qt::AlignLeft, _d->name, -1, &_r );
+ _i++;
+ if ( _i && ! ( _i%guideCols ) ) y += renderer.fontHeight();
+ ++_it;
+ }
+ if ( _i%guideCols ) y += renderer.fontHeight();// last row not full
+ y += ( useBox ? boxWidth : 1 ) + (innerMargin*2);
+ }
+
+ pageStarted = false;
+ } // pageStarted; move on to contents:)
+
+ if ( printLineNumbers && ! startCol ) // don't repeat!
+ {
+ paint.setFont( renderer.config()->fontStruct()->font( false, false ) );
+ paint.setPen( renderer.config()->lineNumberColor() );
+ paint.drawText( (( useBox || useBackground ) ? innerMargin : 0), y,
+ lineNumberWidth, renderer.fontHeight(),
+ Qt::AlignRight, QString("%1").arg( lineCount + 1 ) );
+ }
+ endCol = renderer.textWidth(doc->kateTextLine(lineCount), startCol, maxWidth, &needWrap);
+
+ if ( endCol < startCol )
+ {
+ //kdDebug(13020)<<"--- Skipping garbage, line: "<<lineCount<<" start: "<<startCol<<" end: "<<endCol<<" real EndCol; "<< buffer->line(lineCount)->length()<< " !?"<<endl;
+ lineCount++;
+ continue; // strange case...
+ // Happens if the line fits exactly.
+ // When it happens, a line of garbage would be printed.
+ // FIXME Most likely this is an error in textWidth(),
+ // failing to correctly set needWrap to false in this case?
+ }
+
+ // if we print only selection:
+ // print only selected range of chars.
+ bool skip = false;
+ if ( selectionOnly )
+ {
+ bool inBlockSelection = ( doc->blockSelectionMode() && lineCount >= firstline && lineCount <= lastline );
+ if ( lineCount == firstline || inBlockSelection )
+ {
+ if ( startCol < selStartCol )
+ startCol = selStartCol;
+ }
+ if ( lineCount == lastline || inBlockSelection )
+ {
+ if ( endCol > selEndCol )
+ {
+ endCol = selEndCol;
+ skip = true;
+ }
+ }
+ }
+
+ // HA! this is where we print [part of] a line ;]]
+ // FIXME Convert this function + related functionality to a separate KatePrintView
+ KateLineRange range;
+ range.line = lineCount;
+ range.startCol = startCol;
+ range.endCol = endCol;
+ range.wrap = needWrap;
+ paint.translate(xstart, y);
+ renderer.paintTextLine(paint, &range, 0, maxWidth);
+ paint.resetXForm();
+ if ( skip )
+ {
+ needWrap = false;
+ startCol = 0;
+ }
+ else
+ {
+ startCol = endCol;
+ }
+
+ y += renderer.fontHeight();
+ _count++;
+ } // done while ( needWrap )
+
+ lineCount++;
+ } // done lineCount <= lastline
+ return true;
+ }
+
+#endif //!Q_WS_WIN
+ return false;
+}
+//END KatePrinter
+
+#ifndef Q_WS_WIN //TODO: reenable
+//BEGIN KatePrintTextSettings
+KatePrintTextSettings::KatePrintTextSettings( KPrinter * /*printer*/, QWidget *parent, const char *name )
+ : KPrintDialogPage( parent, name )
+{
+ setTitle( i18n("Te&xt Settings") );
+
+ QVBoxLayout *lo = new QVBoxLayout ( this );
+ lo->setSpacing( KDialog::spacingHint() );
+
+ cbSelection = new QCheckBox( i18n("Print &selected text only"), this );
+ lo->addWidget( cbSelection );
+
+ cbLineNumbers = new QCheckBox( i18n("Print &line numbers"), this );
+ lo->addWidget( cbLineNumbers );
+
+ cbGuide = new QCheckBox( i18n("Print syntax &guide"), this );
+ lo->addWidget( cbGuide );
+
+ lo->addStretch( 1 );
+
+ // set defaults - nothing to do :-)
+
+ // whatsthis
+ QWhatsThis::add( cbSelection, i18n(
+ "<p>This option is only available if some text is selected in the document.</p>"
+ "<p>If available and enabled, only the selected text is printed.</p>") );
+ QWhatsThis::add( cbLineNumbers, i18n(
+ "<p>If enabled, line numbers will be printed on the left side of the page(s).</p>") );
+ QWhatsThis::add( cbGuide, i18n(
+ "<p>Print a box displaying typographical conventions for the document type, as "
+ "defined by the syntax highlighting being used.") );
+}
+
+void KatePrintTextSettings::getOptions( QMap<QString,QString>& opts, bool )
+{
+ opts["app-kate-printselection"] = cbSelection->isChecked() ? "true" : "false";
+ opts["app-kate-printlinenumbers"] = cbLineNumbers->isChecked() ? "true" : "false";
+ opts["app-kate-printguide"] = cbGuide->isChecked() ? "true" : "false" ;
+}
+
+void KatePrintTextSettings::setOptions( const QMap<QString,QString>& opts )
+{
+ QString v;
+ v = opts["app-kate-printselection"];
+ if ( ! v.isEmpty() )
+ cbSelection->setChecked( v == "true" );
+ v = opts["app-kate-printlinenumbers"];
+ if ( ! v.isEmpty() )
+ cbLineNumbers->setChecked( v == "true" );
+ v = opts["app-kate-printguide"];
+ if ( ! v.isEmpty() )
+ cbGuide->setChecked( v == "true" );
+}
+
+void KatePrintTextSettings::enableSelection( bool enable )
+{
+ cbSelection->setEnabled( enable );
+}
+
+//END KatePrintTextSettings
+
+//BEGIN KatePrintHeaderFooter
+KatePrintHeaderFooter::KatePrintHeaderFooter( KPrinter * /*printer*/, QWidget *parent, const char *name )
+ : KPrintDialogPage( parent, name )
+{
+ setTitle( i18n("Hea&der && Footer") );
+
+ QVBoxLayout *lo = new QVBoxLayout ( this );
+ uint sp = KDialog::spacingHint();
+ lo->setSpacing( sp );
+
+ // enable
+ QHBoxLayout *lo1 = new QHBoxLayout ( lo );
+ cbEnableHeader = new QCheckBox( i18n("Pr&int header"), this );
+ lo1->addWidget( cbEnableHeader );
+ cbEnableFooter = new QCheckBox( i18n("Pri&nt footer"), this );
+ lo1->addWidget( cbEnableFooter );
+
+ // font
+ QHBoxLayout *lo2 = new QHBoxLayout( lo );
+ lo2->addWidget( new QLabel( i18n("Header/footer font:"), this ) );
+ lFontPreview = new QLabel( this );
+ lFontPreview->setFrameStyle( QFrame::Panel|QFrame::Sunken );
+ lo2->addWidget( lFontPreview );
+ lo2->setStretchFactor( lFontPreview, 1 );
+ QPushButton *btnChooseFont = new QPushButton( i18n("Choo&se Font..."), this );
+ lo2->addWidget( btnChooseFont );
+ connect( btnChooseFont, SIGNAL(clicked()), this, SLOT(setHFFont()) );
+ // header
+ gbHeader = new QGroupBox( 2, Qt::Horizontal, i18n("Header Properties"), this );
+ lo->addWidget( gbHeader );
+
+ QLabel *lHeaderFormat = new QLabel( i18n("&Format:"), gbHeader );
+ QHBox *hbHeaderFormat = new QHBox( gbHeader );
+ hbHeaderFormat->setSpacing( sp );
+ leHeaderLeft = new QLineEdit( hbHeaderFormat );
+ leHeaderCenter = new QLineEdit( hbHeaderFormat );
+ leHeaderRight = new QLineEdit( hbHeaderFormat );
+ lHeaderFormat->setBuddy( leHeaderLeft );
+ new QLabel( i18n("Colors:"), gbHeader );
+ QHBox *hbHeaderColors = new QHBox( gbHeader );
+ hbHeaderColors->setSpacing( sp );
+ QLabel *lHeaderFgCol = new QLabel( i18n("Foreground:"), hbHeaderColors );
+ kcbtnHeaderFg = new KColorButton( hbHeaderColors );
+ lHeaderFgCol->setBuddy( kcbtnHeaderFg );
+ cbHeaderEnableBgColor = new QCheckBox( i18n("Bac&kground"), hbHeaderColors );
+ kcbtnHeaderBg = new KColorButton( hbHeaderColors );
+
+ gbFooter = new QGroupBox( 2, Qt::Horizontal, i18n("Footer Properties"), this );
+ lo->addWidget( gbFooter );
+
+ // footer
+ QLabel *lFooterFormat = new QLabel( i18n("For&mat:"), gbFooter );
+ QHBox *hbFooterFormat = new QHBox( gbFooter );
+ hbFooterFormat->setSpacing( sp );
+ leFooterLeft = new QLineEdit( hbFooterFormat );
+ leFooterCenter = new QLineEdit( hbFooterFormat );
+ leFooterRight = new QLineEdit( hbFooterFormat );
+ lFooterFormat->setBuddy( leFooterLeft );
+
+ new QLabel( i18n("Colors:"), gbFooter );
+ QHBox *hbFooterColors = new QHBox( gbFooter );
+ hbFooterColors->setSpacing( sp );
+ QLabel *lFooterBgCol = new QLabel( i18n("Foreground:"), hbFooterColors );
+ kcbtnFooterFg = new KColorButton( hbFooterColors );
+ lFooterBgCol->setBuddy( kcbtnFooterFg );
+ cbFooterEnableBgColor = new QCheckBox( i18n("&Background"), hbFooterColors );
+ kcbtnFooterBg = new KColorButton( hbFooterColors );
+
+ lo->addStretch( 1 );
+
+ // user friendly
+ connect( cbEnableHeader, SIGNAL(toggled(bool)), gbHeader, SLOT(setEnabled(bool)) );
+ connect( cbEnableFooter, SIGNAL(toggled(bool)), gbFooter, SLOT(setEnabled(bool)) );
+ connect( cbHeaderEnableBgColor, SIGNAL(toggled(bool)), kcbtnHeaderBg, SLOT(setEnabled(bool)) );
+ connect( cbFooterEnableBgColor, SIGNAL(toggled(bool)), kcbtnFooterBg, SLOT(setEnabled(bool)) );
+
+ // set defaults
+ cbEnableHeader->setChecked( true );
+ leHeaderLeft->setText( "%y" );
+ leHeaderCenter->setText( "%f" );
+ leHeaderRight->setText( "%p" );
+ kcbtnHeaderFg->setColor( QColor("black") );
+ cbHeaderEnableBgColor->setChecked( true );
+ kcbtnHeaderBg->setColor( QColor("lightgrey") );
+
+ cbEnableFooter->setChecked( true );
+ leFooterRight->setText( "%U" );
+ kcbtnFooterFg->setColor( QColor("black") );
+ cbFooterEnableBgColor->setChecked( true );
+ kcbtnFooterBg->setColor( QColor("lightgrey") );
+
+ // whatsthis
+ QString s = i18n("<p>Format of the page header. The following tags are supported:</p>");
+ QString s1 = i18n(
+ "<ul><li><tt>%u</tt>: current user name</li>"
+ "<li><tt>%d</tt>: complete date/time in short format</li>"
+ "<li><tt>%D</tt>: complete date/time in long format</li>"
+ "<li><tt>%h</tt>: current time</li>"
+ "<li><tt>%y</tt>: current date in short format</li>"
+ "<li><tt>%Y</tt>: current date in long format</li>"
+ "<li><tt>%f</tt>: file name</li>"
+ "<li><tt>%U</tt>: full URL of the document</li>"
+ "<li><tt>%p</tt>: page number</li>"
+ "</ul><br>"
+ "<u>Note:</u> Do <b>not</b> use the '|' (vertical bar) character.");
+ QWhatsThis::add(leHeaderRight, s + s1 );
+ QWhatsThis::add(leHeaderCenter, s + s1 );
+ QWhatsThis::add(leHeaderLeft, s + s1 );
+ s = i18n("<p>Format of the page footer. The following tags are supported:</p>");
+ QWhatsThis::add(leFooterRight, s + s1 );
+ QWhatsThis::add(leFooterCenter, s + s1 );
+ QWhatsThis::add(leFooterLeft, s + s1 );
+
+
+}
+
+void KatePrintHeaderFooter::getOptions(QMap<QString,QString>& opts, bool )
+{
+ opts["app-kate-hffont"] = strFont;
+
+ opts["app-kate-useheader"] = (cbEnableHeader->isChecked() ? "true" : "false");
+ opts["app-kate-headerfg"] = kcbtnHeaderFg->color().name();
+ opts["app-kate-headerusebg"] = (cbHeaderEnableBgColor->isChecked() ? "true" : "false");
+ opts["app-kate-headerbg"] = kcbtnHeaderBg->color().name();
+ opts["app-kate-headerformat"] = leHeaderLeft->text() + "|" + leHeaderCenter->text() + "|" + leHeaderRight->text();
+
+ opts["app-kate-usefooter"] = (cbEnableFooter->isChecked() ? "true" : "false");
+ opts["app-kate-footerfg"] = kcbtnFooterFg->color().name();
+ opts["app-kate-footerusebg"] = (cbFooterEnableBgColor->isChecked() ? "true" : "false");
+ opts["app-kate-footerbg"] = kcbtnFooterBg->color().name();
+ opts["app-kate-footerformat"] = leFooterLeft->text() + "|" + leFooterCenter->text() + "|" + leFooterRight->text();
+}
+
+void KatePrintHeaderFooter::setOptions( const QMap<QString,QString>& opts )
+{
+ QString v;
+ v = opts["app-kate-hffont"];
+ strFont = v;
+ QFont f = font();
+ if ( ! v.isEmpty() )
+ {
+ if (!strFont.isEmpty())
+ f.fromString( strFont );
+
+ lFontPreview->setFont( f );
+ }
+ lFontPreview->setText( (f.family() + ", %1pt").arg( f.pointSize() ) );
+
+ v = opts["app-kate-useheader"];
+ if ( ! v.isEmpty() )
+ cbEnableHeader->setChecked( v == "true" );
+ v = opts["app-kate-headerfg"];
+ if ( ! v.isEmpty() )
+ kcbtnHeaderFg->setColor( QColor( v ) );
+ v = opts["app-kate-headerusebg"];
+ if ( ! v.isEmpty() )
+ cbHeaderEnableBgColor->setChecked( v == "true" );
+ v = opts["app-kate-headerbg"];
+ if ( ! v.isEmpty() )
+ kcbtnHeaderBg->setColor( QColor( v ) );
+
+ QStringList tags = QStringList::split('|', opts["app-kate-headerformat"], "true");
+ if (tags.count() == 3)
+ {
+ leHeaderLeft->setText(tags[0]);
+ leHeaderCenter->setText(tags[1]);
+ leHeaderRight->setText(tags[2]);
+ }
+
+ v = opts["app-kate-usefooter"];
+ if ( ! v.isEmpty() )
+ cbEnableFooter->setChecked( v == "true" );
+ v = opts["app-kate-footerfg"];
+ if ( ! v.isEmpty() )
+ kcbtnFooterFg->setColor( QColor( v ) );
+ v = opts["app-kate-footerusebg"];
+ if ( ! v.isEmpty() )
+ cbFooterEnableBgColor->setChecked( v == "true" );
+ v = opts["app-kate-footerbg"];
+ if ( ! v.isEmpty() )
+ kcbtnFooterBg->setColor( QColor( v ) );
+
+ tags = QStringList::split('|', opts["app-kate-footerformat"], "true");
+ if (tags.count() == 3)
+ {
+ leFooterLeft->setText(tags[0]);
+ leFooterCenter->setText(tags[1]);
+ leFooterRight->setText(tags[2]);
+ }
+}
+
+void KatePrintHeaderFooter::setHFFont()
+{
+ QFont fnt( lFontPreview->font() );
+ // display a font dialog
+ if ( KFontDialog::getFont( fnt, false, this ) == KFontDialog::Accepted )
+ {
+ // change strFont
+ strFont = fnt.toString();
+ // set preview
+ lFontPreview->setFont( fnt );
+ lFontPreview->setText( (fnt.family() + ", %1pt").arg( fnt.pointSize() ) );
+ }
+}
+
+//END KatePrintHeaderFooter
+
+//BEGIN KatePrintLayout
+
+KatePrintLayout::KatePrintLayout( KPrinter * /*printer*/, QWidget *parent, const char *name )
+ : KPrintDialogPage( parent, name )
+{
+ setTitle( i18n("L&ayout") );
+
+ QVBoxLayout *lo = new QVBoxLayout ( this );
+ lo->setSpacing( KDialog::spacingHint() );
+
+ QHBox *hb = new QHBox( this );
+ lo->addWidget( hb );
+ QLabel *lSchema = new QLabel( i18n("&Schema:"), hb );
+ cmbSchema = new QComboBox( false, hb );
+ lSchema->setBuddy( cmbSchema );
+
+ cbDrawBackground = new QCheckBox( i18n("Draw bac&kground color"), this );
+ lo->addWidget( cbDrawBackground );
+
+ cbEnableBox = new QCheckBox( i18n("Draw &boxes"), this );
+ lo->addWidget( cbEnableBox );
+
+ gbBoxProps = new QGroupBox( 2, Qt::Horizontal, i18n("Box Properties"), this );
+ lo->addWidget( gbBoxProps );
+
+ QLabel *lBoxWidth = new QLabel( i18n("W&idth:"), gbBoxProps );
+ sbBoxWidth = new QSpinBox( 1, 100, 1, gbBoxProps );
+ lBoxWidth->setBuddy( sbBoxWidth );
+
+ QLabel *lBoxMargin = new QLabel( i18n("&Margin:"), gbBoxProps );
+ sbBoxMargin = new QSpinBox( 0, 100, 1, gbBoxProps );
+ lBoxMargin->setBuddy( sbBoxMargin );
+
+ QLabel *lBoxColor = new QLabel( i18n("Co&lor:"), gbBoxProps );
+ kcbtnBoxColor = new KColorButton( gbBoxProps );
+ lBoxColor->setBuddy( kcbtnBoxColor );
+
+ connect( cbEnableBox, SIGNAL(toggled(bool)), gbBoxProps, SLOT(setEnabled(bool)) );
+
+ lo->addStretch( 1 );
+ // set defaults:
+ sbBoxMargin->setValue( 6 );
+ gbBoxProps->setEnabled( false );
+ cmbSchema->insertStringList (KateFactory::self()->schemaManager()->list ());
+ cmbSchema->setCurrentItem( 1 );
+
+ // whatsthis
+ // FIXME uncomment when string freeze is over
+// QWhatsThis::add ( cmbSchema, i18n(
+// "Select the color scheme to use for the print." ) );
+ QWhatsThis::add( cbDrawBackground, i18n(
+ "<p>If enabled, the background color of the editor will be used.</p>"
+ "<p>This may be useful if your color scheme is designed for a dark background.</p>") );
+ QWhatsThis::add( cbEnableBox, i18n(
+ "<p>If enabled, a box as defined in the properties below will be drawn "
+ "around the contents of each page. The Header and Footer will be separated "
+ "from the contents with a line as well.</p>") );
+ QWhatsThis::add( sbBoxWidth, i18n(
+ "The width of the box outline" ) );
+ QWhatsThis::add( sbBoxMargin, i18n(
+ "The margin inside boxes, in pixels") );
+ QWhatsThis::add( kcbtnBoxColor, i18n(
+ "The line color to use for boxes") );
+}
+
+void KatePrintLayout::getOptions(QMap<QString,QString>& opts, bool )
+{
+ opts["app-kate-colorscheme"] = cmbSchema->currentText();
+ opts["app-kate-usebackground"] = cbDrawBackground->isChecked() ? "true" : "false";
+ opts["app-kate-usebox"] = cbEnableBox->isChecked() ? "true" : "false";
+ opts["app-kate-boxwidth"] = sbBoxWidth->cleanText();
+ opts["app-kate-boxmargin"] = sbBoxMargin->cleanText();
+ opts["app-kate-boxcolor"] = kcbtnBoxColor->color().name();
+}
+
+void KatePrintLayout::setOptions( const QMap<QString,QString>& opts )
+{
+ QString v;
+ v = opts["app-kate-colorscheme"];
+ if ( ! v.isEmpty() )
+ cmbSchema->setCurrentItem( KateFactory::self()->schemaManager()->number( v ) );
+ v = opts["app-kate-usebackground"];
+ if ( ! v.isEmpty() )
+ cbDrawBackground->setChecked( v == "true" );
+ v = opts["app-kate-usebox"];
+ if ( ! v.isEmpty() )
+ cbEnableBox->setChecked( v == "true" );
+ v = opts["app-kate-boxwidth"];
+ if ( ! v.isEmpty() )
+ sbBoxWidth->setValue( v.toInt() );
+ v = opts["app-kate-boxmargin"];
+ if ( ! v.isEmpty() )
+ sbBoxMargin->setValue( v.toInt() );
+ v = opts["app-kate-boxcolor"];
+ if ( ! v.isEmpty() )
+ kcbtnBoxColor->setColor( QColor( v ) );
+}
+//END KatePrintLayout
+
+#include "kateprinter.moc"
+#endif //!Q_WS_WIN
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/kateprinter.h b/kate/part/kateprinter.h
new file mode 100644
index 000000000..f2fb0bc87
--- /dev/null
+++ b/kate/part/kateprinter.h
@@ -0,0 +1,136 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (c) 2002 Michael Goffioul <kdeprint@swing.be>
+ * Complete rewrite on Sat Jun 15 2002 (c) Anders Lund <anders@alweb.dk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#ifndef __KATE_PRINTER_H__
+#define __KATE_PRINTER_H__
+
+#include <kprintdialogpage.h>
+
+class KateDocument;
+
+class KColorButton;
+class KPrinter;
+class QCheckBox;
+class QComboBox;
+class QGroupBox;
+class QLabel;
+class QLineEdit;
+class QSpinBox;
+
+class KatePrinter
+{
+ public:
+ static bool print (KateDocument *doc);
+};
+
+#ifndef Q_WS_WIN //TODO: reenable
+//BEGIN Text settings
+/*
+ Text settings page:
+ - [ ] Print Selection (enabled if there is a selection in the view)
+ - Print Line Numbers
+ () Smart () Yes () No
+*/
+class KatePrintTextSettings : public KPrintDialogPage
+{
+ Q_OBJECT
+ public:
+ KatePrintTextSettings( KPrinter *printer, QWidget *parent=0, const char *name=0 );
+ ~KatePrintTextSettings(){};
+
+ void getOptions(QMap<QString,QString>& opts, bool incldef = false);
+ void setOptions(const QMap<QString,QString>& opts);
+
+ /* call if view has a selection, enables the seelction checkbox according to the arg */
+ void enableSelection( bool );
+
+ private:
+ QCheckBox *cbSelection, *cbLineNumbers, *cbGuide;
+};
+//END Text Settings
+
+//BEGIN Header/Footer
+/*
+ Header & Footer page:
+ - enable header/footer
+ - header/footer props
+ o formats
+ o colors
+*/
+
+class KatePrintHeaderFooter : public KPrintDialogPage
+{
+ Q_OBJECT
+ public:
+ KatePrintHeaderFooter( KPrinter *printer, QWidget *parent=0, const char *name=0 );
+ ~KatePrintHeaderFooter(){};
+
+ void getOptions(QMap<QString,QString>& opts, bool incldef = false);
+ void setOptions(const QMap<QString,QString>& opts);
+
+ public slots:
+ void setHFFont();
+
+ private:
+ QCheckBox *cbEnableHeader, *cbEnableFooter;
+ QLabel *lFontPreview;
+ QString strFont;
+ QGroupBox *gbHeader, *gbFooter;
+ QLineEdit *leHeaderLeft, *leHeaderCenter, *leHeaderRight;
+ KColorButton *kcbtnHeaderFg, *kcbtnHeaderBg;
+ QCheckBox *cbHeaderEnableBgColor;
+ QLineEdit *leFooterLeft, *leFooterCenter, *leFooterRight;
+ KColorButton *kcbtnFooterFg, *kcbtnFooterBg;
+ QCheckBox *cbFooterEnableBgColor;
+};
+
+//END Header/Footer
+
+//BEGIN Layout
+/*
+ Layout page:
+ - Color scheme
+ - Use Box
+ - Box properties
+ o Width
+ o Margin
+ o Color
+*/
+class KatePrintLayout : public KPrintDialogPage
+{
+ Q_OBJECT
+ public:
+ KatePrintLayout( KPrinter *printer, QWidget *parent=0, const char *name=0 );
+ ~KatePrintLayout(){};
+
+ void getOptions(QMap<QString,QString>& opts, bool incldef = false);
+ void setOptions(const QMap<QString,QString>& opts);
+
+ private:
+ QComboBox *cmbSchema;
+ QCheckBox *cbEnableBox, *cbDrawBackground;
+ QGroupBox *gbBoxProps;
+ QSpinBox *sbBoxWidth, *sbBoxMargin;
+ KColorButton* kcbtnBoxColor;
+};
+//END Layout
+#endif //!Q_WS_WIN
+
+#endif
diff --git a/kate/part/katerenderer.cpp b/kate/part/katerenderer.cpp
new file mode 100644
index 000000000..60851481d
--- /dev/null
+++ b/kate/part/katerenderer.cpp
@@ -0,0 +1,1032 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2003 Hamish Rodda <rodda@kde.org>
+ Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
+ Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "katerenderer.h"
+
+#include "katelinerange.h"
+#include "katedocument.h"
+#include "katearbitraryhighlight.h"
+#include "kateconfig.h"
+#include "katehighlight.h"
+#include "katefactory.h"
+#include "kateview.h"
+
+#include <kdebug.h>
+
+#include <qpainter.h>
+#include <qpopupmenu.h>
+
+KateRenderer::KateRenderer(KateDocument* doc, KateView *view)
+ : m_doc(doc), m_view (view), m_caretStyle(KateRenderer::Insert)
+ , m_drawCaret(true)
+ , m_showSelections(true)
+ , m_showTabs(true)
+ , m_printerFriendly(false)
+{
+ KateFactory::self()->registerRenderer ( this );
+ m_config = new KateRendererConfig (this);
+
+ m_tabWidth = m_doc->config()->tabWidth();
+ m_indentWidth = m_tabWidth;
+ if (m_doc->config()->configFlags() & KateDocumentConfig::cfSpaceIndent)
+ {
+ m_indentWidth = m_doc->config()->indentationWidth();
+ }
+
+ updateAttributes ();
+}
+
+KateRenderer::~KateRenderer()
+{
+ delete m_config;
+ KateFactory::self()->deregisterRenderer ( this );
+}
+
+void KateRenderer::updateAttributes ()
+{
+ m_schema = config()->schema ();
+ m_attributes = m_doc->highlight()->attributes (m_schema);
+}
+
+KateAttribute* KateRenderer::attribute(uint pos)
+{
+ if (pos < m_attributes->size())
+ return &m_attributes->at(pos);
+
+ return &m_attributes->at(0);
+}
+
+void KateRenderer::setDrawCaret(bool drawCaret)
+{
+ m_drawCaret = drawCaret;
+}
+
+void KateRenderer::setCaretStyle(KateRenderer::caretStyles style)
+{
+ m_caretStyle = style;
+}
+
+void KateRenderer::setShowTabs(bool showTabs)
+{
+ m_showTabs = showTabs;
+}
+
+void KateRenderer::setTabWidth(int tabWidth)
+{
+ m_tabWidth = tabWidth;
+}
+
+bool KateRenderer::showIndentLines() const
+{
+ return m_config->showIndentationLines();
+}
+
+void KateRenderer::setShowIndentLines(bool showIndentLines)
+{
+ m_config->setShowIndentationLines(showIndentLines);
+}
+
+void KateRenderer::setIndentWidth(int indentWidth)
+{
+ m_indentWidth = m_tabWidth;
+ if (m_doc->config()->configFlags() & KateDocumentConfig::cfSpaceIndent)
+ {
+ m_indentWidth = indentWidth;
+ }
+}
+
+void KateRenderer::setShowSelections(bool showSelections)
+{
+ m_showSelections = showSelections;
+}
+
+void KateRenderer::increaseFontSizes()
+{
+ QFont f ( *config()->font () );
+ f.setPointSize (f.pointSize ()+1);
+
+ config()->setFont (f);
+}
+
+void KateRenderer::decreaseFontSizes()
+{
+ QFont f ( *config()->font () );
+
+ if ((f.pointSize ()-1) > 0)
+ f.setPointSize (f.pointSize ()-1);
+
+ config()->setFont (f);
+}
+
+bool KateRenderer::isPrinterFriendly() const
+{
+ return m_printerFriendly;
+}
+
+void KateRenderer::setPrinterFriendly(bool printerFriendly)
+{
+ m_printerFriendly = printerFriendly;
+ setShowTabs(false);
+ setShowSelections(false);
+ setDrawCaret(false);
+}
+
+bool KateRenderer::paintTextLineBackground(QPainter& paint, int line, bool isCurrentLine, int xStart, int xEnd)
+{
+ if (isPrinterFriendly())
+ return false;
+
+ // font data
+ KateFontStruct *fs = config()->fontStruct();
+
+ // Normal background color
+ QColor backgroundColor( config()->backgroundColor() );
+
+ bool selectionPainted = false;
+ if (showSelections() && m_view->lineSelected(line))
+ {
+ backgroundColor = config()->selectionColor();
+ selectionPainted = true;
+ }
+ else
+ {
+ // paint the current line background if we're on the current line
+ if (isCurrentLine)
+ backgroundColor = config()->highlightedLineColor();
+
+ // Check for mark background
+ int markRed = 0, markGreen = 0, markBlue = 0, markCount = 0;
+
+ // Retrieve marks for this line
+ uint mrk = m_doc->mark( line );
+ if (mrk)
+ {
+ for (uint bit = 0; bit < 32; bit++)
+ {
+ KTextEditor::MarkInterface::MarkTypes markType = (KTextEditor::MarkInterface::MarkTypes)(1<<bit);
+ if (mrk & markType)
+ {
+ QColor markColor = config()->lineMarkerColor(markType);
+
+ if (markColor.isValid()) {
+ markCount++;
+ markRed += markColor.red();
+ markGreen += markColor.green();
+ markBlue += markColor.blue();
+ }
+ }
+ } // for
+ } // Marks
+
+ if (markCount) {
+ markRed /= markCount;
+ markGreen /= markCount;
+ markBlue /= markCount;
+ backgroundColor.setRgb(
+ int((backgroundColor.red() * 0.9) + (markRed * 0.1)),
+ int((backgroundColor.green() * 0.9) + (markGreen * 0.1)),
+ int((backgroundColor.blue() * 0.9) + (markBlue * 0.1))
+ );
+ }
+ } // background preprocessing
+
+ // Draw line background
+ paint.fillRect(0, 0, xEnd - xStart, fs->fontHeight, backgroundColor);
+
+ return selectionPainted;
+}
+
+void KateRenderer::paintWhitespaceMarker(QPainter &paint, uint x, uint y)
+{
+ QPen penBackup( paint.pen() );
+ paint.setPen( config()->tabMarkerColor() );
+ paint.drawPoint(x, y);
+ paint.drawPoint(x + 1, y);
+ paint.drawPoint(x, y - 1);
+ paint.setPen( penBackup );
+}
+
+
+void KateRenderer::paintIndentMarker(QPainter &paint, uint x, uint row)
+{
+ QPen penBackup( paint.pen() );
+ paint.setPen( config()->tabMarkerColor() );
+
+ const int top = paint.window().top();
+ const int bottom = paint.window().bottom();
+ const int h = bottom - top + 1;
+
+ // Dot padding.
+ int pad = 0;
+ if(row & 1 && h & 1) pad = 1;
+
+ for(int i = top; i <= bottom; i++)
+ {
+ if((i + pad) & 1)
+ {
+ paint.drawPoint(x + 2, i);
+ }
+ }
+
+ paint.setPen( penBackup );
+}
+
+
+void KateRenderer::paintTextLine(QPainter& paint, const KateLineRange* range, int xStart, int xEnd, const KateTextCursor* cursor, const KateBracketRange* bracketmark)
+{
+ int line = range->line;
+
+ // textline
+ KateTextLine::Ptr textLine = m_doc->kateTextLine(line);
+ if (!textLine)
+ return;
+
+ bool showCursor = drawCaret() && cursor && range->includesCursor(*cursor);
+
+ KateSuperRangeList& superRanges = m_doc->arbitraryHL()->rangesIncluding(range->line, 0);
+
+ int minIndent = 0;
+
+ // A bit too verbose for my tastes
+ // Re-write a bracketmark class? put into its own function? add more helper constructors to the range stuff?
+ // Also, need a light-weight arbitraryhighlightrange class for static stuff
+ KateArbitraryHighlightRange* bracketStartRange (0L);
+ KateArbitraryHighlightRange* bracketEndRange (0L);
+ if (bracketmark && bracketmark->isValid()) {
+ if (range->includesCursor(bracketmark->start())) {
+ KateTextCursor startend = bracketmark->start();
+ startend.setCol(startend.col()+1);
+ bracketStartRange = new KateArbitraryHighlightRange(m_doc, bracketmark->start(), startend);
+ bracketStartRange->setBGColor(config()->highlightedBracketColor());
+ bracketStartRange->setBold(true);
+ superRanges.append(bracketStartRange);
+ }
+
+ if (range->includesCursor(bracketmark->end())) {
+ KateTextCursor endend = bracketmark->end();
+ endend.setCol(endend.col()+1);
+ bracketEndRange = new KateArbitraryHighlightRange(m_doc, bracketmark->end(), endend);
+ bracketEndRange->setBGColor(config()->highlightedBracketColor());
+ bracketEndRange->setBold(true);
+ superRanges.append(bracketEndRange);
+ }
+
+ Q_ASSERT(bracketmark->start().line() <= bracketmark->end().line());
+ if (bracketmark->start().line() < line && bracketmark->end().line() >= line)
+ {
+ minIndent = bracketmark->getMinIndent();
+ }
+ }
+
+
+ // length, chars + raw attribs
+ uint len = textLine->length();
+ uint oldLen = len;
+
+ // should the cursor be painted (if it is in the current xstart - xend range)
+ bool cursorVisible = false;
+ int cursorMaxWidth = 0;
+
+ // font data
+ KateFontStruct * fs = config()->fontStruct();
+
+ // Paint selection background as the whole line is selected
+ // selection startcol/endcol calc
+ bool hasSel = false;
+ uint startSel = 0;
+ uint endSel = 0;
+
+ // was the selection background already completely painted ?
+ bool selectionPainted = false;
+ bool isCurrentLine = (cursor && range->includesCursor(*cursor));
+ selectionPainted = paintTextLineBackground(paint, line, isCurrentLine, xStart, xEnd);
+ if (selectionPainted)
+ {
+ hasSel = true;
+ startSel = 0;
+ endSel = len + 1;
+ }
+
+ int startcol = range->startCol;
+ if (startcol > (int)len)
+ startcol = len;
+
+ if (startcol < 0)
+ startcol = 0;
+
+ int endcol = range->wrap ? range->endCol : -1;
+ if (endcol < 0)
+ len = len - startcol;
+ else
+ len = endcol - startcol;
+
+ // text attribs font/style data
+ KateAttribute* attr = m_doc->highlight()->attributes(m_schema)->data();
+
+ const QColor *cursorColor = &attr[0].textColor();
+
+ // Start arbitrary highlighting
+ KateTextCursor currentPos(line, startcol);
+ superRanges.firstBoundary(&currentPos);
+
+ if (showSelections() && !selectionPainted)
+ hasSel = getSelectionBounds(line, oldLen, startSel, endSel);
+
+ // Draws the dashed underline at the start of a folded block of text.
+ if (range->startsInvisibleBlock) {
+ paint.setPen(QPen(config()->wordWrapMarkerColor(), 1, Qt::DashLine));
+ paint.drawLine(0, fs->fontHeight - 1, xEnd - xStart, fs->fontHeight - 1);
+ }
+
+ // draw word-wrap-honor-indent filling
+ if (range->xOffset() && range->xOffset() > xStart)
+ {
+ paint.fillRect(0, 0, range->xOffset() - xStart, fs->fontHeight,
+ QBrush(config()->wordWrapMarkerColor(), QBrush::DiagCrossPattern));
+ }
+
+ // painting loop
+ uint xPos = range->xOffset();
+ int cursorXPos = 0;
+
+ // Optimisation to quickly draw an empty line of text
+ if (len < 1)
+ {
+ if (showCursor && (cursor->col() >= int(startcol)))
+ {
+ cursorVisible = true;
+ cursorXPos = xPos + cursor->col() * fs->myFontMetrics.width(QChar(' '));
+ }
+ }
+ else
+ {
+ bool isIMSel = false;
+ bool isIMEdit = false;
+
+ bool isSel = false;
+
+ KateAttribute customHL;
+
+ const QColor *curColor = 0;
+ const QColor *oldColor = 0;
+
+ KateAttribute* oldAt = &attr[0];
+
+ uint oldXPos = xPos;
+ uint xPosAfter = xPos;
+
+ KateAttribute currentHL;
+
+ uint blockStartCol = startcol;
+ uint curCol = startcol;
+ uint nextCol = curCol + 1;
+
+ // text + attrib data from line
+ const uchar *textAttributes = textLine->attributes ();
+ bool noAttribs = !textAttributes;
+
+ // adjust to startcol ;)
+ textAttributes = textAttributes + startcol;
+
+ uint atLen = m_doc->highlight()->attributes(m_schema)->size();
+
+ // Determine if we have trailing whitespace and store the column
+ // if lastChar == -1, set to 0, if lastChar exists, increase by one
+ uint trailingWhitespaceColumn = textLine->lastChar() + 1;
+ const uint lastIndentColumn = textLine->firstChar();
+
+ // Could be precomputed.
+ const uint spaceWidth = fs->width (QChar(' '), false, false, m_tabWidth);
+
+ // Get current x position.
+ int curPos = textLine->cursorX(curCol, m_tabWidth);
+
+ while (curCol - startcol < len)
+ {
+ // make sure curPos is updated correctly.
+ // ### if uncommented, causes an O(n^2) behaviour
+ //Q_ASSERT(curPos == textLine->cursorX(curCol, m_tabWidth));
+
+ QChar curChar = textLine->string()[curCol];
+ // Decide if this character is a tab - we treat the spacing differently
+ // TODO: move tab width calculation elsewhere?
+ bool isTab = curChar == QChar('\t');
+
+ // Determine current syntax highlighting attribute
+ // A bit legacy but doesn't need to change
+ KateAttribute* curAt = (noAttribs || ((*textAttributes) >= atLen)) ? &attr[0] : &attr[*textAttributes];
+
+ // X position calculation. Incorrect for fonts with non-zero leftBearing() and rightBearing() results.
+ // TODO: make internal charWidth() function, use QFontMetrics::charWidth().
+ xPosAfter += curAt->width(*fs, curChar, m_tabWidth);
+
+ // Tab special treatment, move to charWidth().
+ if (isTab)
+ xPosAfter -= (xPosAfter % curAt->width(*fs, curChar, m_tabWidth));
+
+ // Only draw after the starting X value
+ // Haha, this was always wrong, due to the use of individual char width calculations...?? :(
+ if ((int)xPosAfter >= xStart)
+ {
+ // Determine if we're in a selection and should be drawing it
+ isSel = (showSelections() && hasSel && (curCol >= startSel) && (curCol < endSel));
+
+ // input method edit area
+ isIMEdit = m_view && m_view->isIMEdit( line, curCol );
+
+ // input method selection
+ isIMSel = m_view && m_view->isIMSelection( line, curCol );
+
+ // Determine current color, taking into account selection
+ curColor = isSel ? &(curAt->selectedTextColor()) : &(curAt->textColor());
+
+ // Incorporate in arbitrary highlighting
+ if (curAt != oldAt || curColor != oldColor || (superRanges.count() && superRanges.currentBoundary() && *(superRanges.currentBoundary()) == currentPos)) {
+ if (superRanges.count() && superRanges.currentBoundary() && *(superRanges.currentBoundary()) == currentPos)
+ customHL = KateArbitraryHighlightRange::merge(superRanges.rangesIncluding(currentPos));
+
+ KateAttribute hl = customHL;
+
+ hl += *curAt;
+
+ // use default highlighting color if we haven't defined one above.
+ if (!hl.itemSet(KateAttribute::TextColor))
+ hl.setTextColor(*curColor);
+
+ if (!isSel)
+ paint.setPen(hl.textColor());
+ else
+ paint.setPen(hl.selectedTextColor());
+
+ paint.setFont(hl.font(*currentFont()));
+
+ if (superRanges.currentBoundary() && *(superRanges.currentBoundary()) == currentPos)
+ superRanges.nextBoundary();
+
+ currentHL = hl;
+ }
+
+ // Determine whether we can delay painting to draw a block of similarly formatted
+ // characters or not
+ // Reasons for NOT delaying the drawing until the next character
+ // You have to detect the change one character in advance.
+ // TODO: KateAttribute::canBatchRender()
+ bool renderNow = false;
+ if ((isTab)
+ // formatting has changed OR
+ || (superRanges.count() && superRanges.currentBoundary() && *(superRanges.currentBoundary()) == KateTextCursor(line, nextCol))
+
+ // it is the end of the line OR
+ || (curCol - startcol >= len - 1)
+
+ // the rest of the line is trailing whitespace OR
+ || (curCol + 1 >= trailingWhitespaceColumn)
+
+ // indentation lines OR
+ || (showIndentLines() && curCol < lastIndentColumn)
+
+ // the x position is past the end OR
+ || ((int)xPos > xEnd)
+
+ // it is a different attribute OR
+ || (!noAttribs && curAt != &attr[*(textAttributes+1)])
+
+ // the selection boundary was crossed OR
+ || (isSel != (hasSel && (nextCol >= startSel) && (nextCol < endSel)))
+
+ // the next char is a tab (removed the "and this isn't" because that's dealt with above)
+ // i.e. we have to draw the current text so the tab can be rendered as above.
+ || (textLine->string()[nextCol] == QChar('\t'))
+
+ // input method edit area
+ || ( m_view && (isIMEdit != m_view->isIMEdit( line, nextCol )) )
+
+ // input method selection
+ || ( m_view && (isIMSel != m_view->isIMSelection( line, nextCol )) )
+ )
+ {
+ renderNow = true;
+ }
+
+ if (renderNow)
+ {
+ if (!isPrinterFriendly())
+ {
+ bool paintBackground = true;
+ uint width = xPosAfter - oldXPos;
+ QColor fillColor;
+
+ if (isIMSel && !isTab)
+ {
+ // input method selection
+ fillColor = m_view->colorGroup().color(QColorGroup::Foreground);
+ }
+ else if (isIMEdit && !isTab)
+ {
+ // XIM support
+ // input method edit area
+ const QColorGroup& cg = m_view->colorGroup();
+ int h1, s1, v1, h2, s2, v2;
+ cg.color( QColorGroup::Base ).hsv( &h1, &s1, &v1 );
+ cg.color( QColorGroup::Background ).hsv( &h2, &s2, &v2 );
+ fillColor.setHsv( h1, s1, ( v1 + v2 ) / 2 );
+ }
+ else if (!selectionPainted && (isSel || currentHL.itemSet(KateAttribute::BGColor)))
+ {
+ if (isSel)
+ {
+ fillColor = config()->selectionColor();
+
+ // If this is the last block of text, fill up to the end of the line if the
+ // selection stretches that far
+ if ((curCol >= len - 1) && m_view->lineEndSelected (line, endcol))
+ width = xEnd - oldXPos;
+ }
+ else
+ {
+ fillColor = currentHL.bgColor();
+ }
+ }
+ else
+ {
+ paintBackground = false;
+ }
+
+ if (paintBackground)
+ paint.fillRect(oldXPos - xStart, 0, width, fs->fontHeight, fillColor);
+
+ if (isIMSel && paintBackground && !isTab)
+ {
+ paint.save();
+ paint.setPen( m_view->colorGroup().color( QColorGroup::BrightText ) );
+ }
+
+ // Draw indentation markers.
+ if (showIndentLines() && curCol < lastIndentColumn)
+ {
+ // Draw multiple guides when tab width greater than indent width.
+ const int charWidth = isTab ? m_tabWidth - curPos % m_tabWidth : 1;
+
+ // Do not draw indent guides on the first line.
+ int i = 0;
+ if (curPos == 0 || curPos % m_indentWidth > 0)
+ i = m_indentWidth - curPos % m_indentWidth;
+
+ for (; i < charWidth; i += m_indentWidth)
+ {
+ // In most cases this is done one or zero times.
+ paintIndentMarker(paint, xPos - xStart + i * spaceWidth, line);
+
+ // Draw highlighted line.
+ if (curPos+i == minIndent)
+ {
+ paintIndentMarker(paint, xPos - xStart + 1 + i * spaceWidth, line+1);
+ }
+ }
+ }
+ }
+
+ // or we will see no text ;)
+ int y = fs->fontAscent;
+
+ // make sure we redraw the right character groups on attrib/selection changes
+ // Special case... de-special case some of it
+ if (isTab || (curCol >= trailingWhitespaceColumn))
+ {
+ // Draw spaces too, because it might be eg. underlined
+ static QString spaces;
+ if (int(spaces.length()) != m_tabWidth)
+ spaces.fill(' ', m_tabWidth);
+
+ paint.drawText(oldXPos-xStart, y, isTab ? spaces : QString(" "));
+
+ if (showTabs())
+ {
+ // trailing spaces and tabs may also have to be different options.
+ // if( curCol >= lastIndentColumn )
+ paintWhitespaceMarker(paint, xPos - xStart, y);
+ }
+
+ // variable advancement
+ blockStartCol = nextCol;
+ oldXPos = xPosAfter;
+ }
+ else
+ {
+ // Here's where the money is...
+ paint.drawText(oldXPos-xStart, y, textLine->string(), blockStartCol, nextCol-blockStartCol);
+
+ // Draw preedit's underline
+ if (isIMEdit) {
+ QRect r( oldXPos - xStart, 0, xPosAfter - oldXPos, fs->fontHeight );
+ paint.drawLine( r.bottomLeft(), r.bottomRight() );
+ }
+
+ // Put pen color back
+ if (isIMSel) paint.restore();
+
+ // We're done drawing?
+ if ((int)xPos > xEnd)
+ break;
+
+ // variable advancement
+ blockStartCol = nextCol;
+ oldXPos = xPosAfter;
+ //oldS = s+1;
+ }
+ } // renderNow
+
+ // determine cursor X position
+ if (showCursor && (cursor->col() == int(curCol)))
+ {
+ cursorVisible = true;
+ cursorXPos = xPos;
+ cursorMaxWidth = xPosAfter - xPos;
+ cursorColor = &curAt->textColor();
+ }
+ } // xPosAfter >= xStart
+ else
+ {
+ // variable advancement
+ blockStartCol = nextCol;
+ oldXPos = xPosAfter;
+ }
+
+ // increase xPos
+ xPos = xPosAfter;
+
+ // increase attribs pos
+ textAttributes++;
+
+ // to only switch font/color if needed
+ oldAt = curAt;
+ oldColor = curColor;
+
+ // col move
+ curCol++;
+ nextCol++;
+ currentPos.setCol(currentPos.col() + 1);
+
+ // Update the current indentation pos.
+ if (isTab)
+ {
+ curPos += m_tabWidth - (curPos % m_tabWidth);
+ }
+ else
+ {
+ curPos++;
+ }
+ }
+
+ // If this line has a partial selection that's the start of a multi-line selection,
+ // we have to fill areas on the right side of the text with the selection color.
+ if (showSelections() && hasSel && !selectionPainted && xStart >= (int)xPos && m_view->lineEndSelected(line, -1))
+ {
+ paint.fillRect(0, 0, xEnd-xStart, fs->fontHeight, config()->selectionColor());
+ }
+
+ // Determine cursor position (if it is not within the range being drawn)
+ if (showCursor && (cursor->col() >= int(curCol)))
+ {
+ cursorVisible = true;
+ cursorXPos = xPos + (cursor->col() - int(curCol)) * fs->myFontMetrics.width(QChar(' '));
+ cursorMaxWidth = xPosAfter - xPos;
+ cursorColor = &oldAt->textColor();
+ }
+ }
+
+ // Paint cursor
+ if (cursorVisible)
+ {
+ uint cursorWidth = (caretStyle() == Replace && (cursorMaxWidth > 2)) ? cursorMaxWidth : 2;
+ paint.fillRect(cursorXPos-xStart, 0, cursorWidth, fs->fontHeight, *cursorColor);
+ }
+
+ // show word wrap marker if desirable
+ if (!isPrinterFriendly() && config()->wordWrapMarker() && fs->fixedPitch())
+ {
+ paint.setPen( config()->wordWrapMarkerColor() );
+ int _x = m_doc->config()->wordWrapAt() * fs->myFontMetrics.width('x') - xStart;
+ paint.drawLine( _x,0,_x,fs->fontHeight );
+ }
+
+ // cleanup ;)
+ delete bracketStartRange;
+ delete bracketEndRange;
+}
+
+uint KateRenderer::textWidth(const KateTextLine::Ptr &textLine, int cursorCol)
+{
+ if (!textLine)
+ return 0;
+
+ const int len = textLine->length();
+
+ if (cursorCol < 0)
+ cursorCol = len;
+
+ KateFontStruct *fs = config()->fontStruct();
+
+ const QChar *unicode = textLine->text();
+ const QString &textString = textLine->string();
+
+ int x = 0;
+ int width;
+ for (int z = 0; z < cursorCol; z++) {
+ KateAttribute* a = attribute(textLine->attribute(z));
+
+ if (z < len) {
+ width = a->width(*fs, textString, z, m_tabWidth);
+ } else {
+ // DF: commented out. It happens all the time.
+ //Q_ASSERT(!m_doc->wrapCursor());
+ width = a->width(*fs, QChar(' '), m_tabWidth);
+ }
+
+ x += width;
+
+ if (z < len && unicode[z] == QChar('\t'))
+ x -= x % width;
+ }
+
+ return x;
+}
+
+uint KateRenderer::textWidth(const KateTextLine::Ptr &textLine, uint startcol, uint maxwidth, bool *needWrap, int *endX)
+{
+ KateFontStruct *fs = config()->fontStruct();
+ uint x = 0;
+ uint endcol = startcol;
+ int endX2 = 0;
+ int lastWhiteSpace = -1;
+ int lastWhiteSpaceX = -1;
+
+ // used to not wrap a solitary word off the first line, ie. the
+ // first line should not wrap until some characters have been displayed if possible
+ bool foundNonWhitespace = startcol != 0;
+ bool foundWhitespaceAfterNonWhitespace = startcol != 0;
+
+ *needWrap = false;
+
+ const uint len = textLine->length();
+ const QChar *unicode = textLine->text();
+ const QString &textString = textLine->string();
+
+ uint z = startcol;
+ for (; z < len; z++)
+ {
+ KateAttribute* a = attribute(textLine->attribute(z));
+ int width = a->width(*fs, textString, z, m_tabWidth);
+ Q_ASSERT(width);
+ x += width;
+
+ // How should tabs be treated when they word-wrap on a print-out?
+ // if startcol != 0, this messes up (then again, word wrapping messes up anyway)
+ if (unicode[z] == QChar('\t'))
+ x -= x % width;
+
+ if (unicode[z].isSpace())
+ {
+ lastWhiteSpace = z+1;
+ lastWhiteSpaceX = x;
+
+ if (foundNonWhitespace)
+ foundWhitespaceAfterNonWhitespace = true;
+ }
+ else
+ {
+ if (!foundWhitespaceAfterNonWhitespace) {
+ foundNonWhitespace = true;
+
+ lastWhiteSpace = z+1;
+ lastWhiteSpaceX = x;
+ }
+ }
+
+ if (x <= maxwidth)
+ {
+ if (lastWhiteSpace > -1)
+ {
+ endcol = lastWhiteSpace;
+ endX2 = lastWhiteSpaceX;
+ }
+ else
+ {
+ endcol = z+1;
+ endX2 = x;
+ }
+ }
+ else if (z == startcol)
+ {
+ // require a minimum of 1 character advancement per call, even if it means drawing gets cut off
+ // (geez gideon causes troubles with starting the views very small)
+ endcol = z+1;
+ endX2 = x;
+ }
+
+ if (x >= maxwidth)
+ {
+ *needWrap = true;
+ break;
+ }
+ }
+
+ if (*needWrap)
+ {
+ if (endX)
+ *endX = endX2;
+
+ return endcol;
+ }
+ else
+ {
+ if (endX)
+ *endX = x;
+
+ return z+1;
+ }
+}
+
+uint KateRenderer::textWidth(const KateTextCursor &cursor)
+{
+ int line = kMin(kMax(0, cursor.line()), (int)m_doc->numLines() - 1);
+ int col = kMax(0, cursor.col());
+
+ return textWidth(m_doc->kateTextLine(line), col);
+}
+
+uint KateRenderer::textWidth( KateTextCursor &cursor, int xPos, uint startCol)
+{
+ bool wrapCursor = m_view->wrapCursor();
+ int x, oldX;
+
+ KateFontStruct *fs = config()->fontStruct();
+
+ if (cursor.line() < 0) cursor.setLine(0);
+ if (cursor.line() > (int)m_doc->lastLine()) cursor.setLine(m_doc->lastLine());
+ KateTextLine::Ptr textLine = m_doc->kateTextLine(cursor.line());
+
+ if (!textLine) return 0;
+
+ const uint len = textLine->length();
+ const QChar *unicode = textLine->text();
+ const QString &textString = textLine->string();
+
+ x = oldX = 0;
+ uint z = startCol;
+ while (x < xPos && (!wrapCursor || z < len)) {
+ oldX = x;
+
+ KateAttribute* a = attribute(textLine->attribute(z));
+
+ int width = 0;
+
+ if (z < len)
+ width = a->width(*fs, textString, z, m_tabWidth);
+ else
+ width = a->width(*fs, QChar(' '), m_tabWidth);
+
+ x += width;
+
+ if (z < len && unicode[z] == QChar('\t'))
+ x -= x % width;
+
+ z++;
+ }
+ if (xPos - oldX < x - xPos && z > 0) {
+ z--;
+ x = oldX;
+ }
+ cursor.setCol(z);
+ return x;
+}
+
+const QFont *KateRenderer::currentFont()
+{
+ return config()->font();
+}
+
+const QFontMetrics* KateRenderer::currentFontMetrics()
+{
+ return config()->fontMetrics();
+}
+
+uint KateRenderer::textPos(uint line, int xPos, uint startCol, bool nearest)
+{
+ return textPos(m_doc->kateTextLine(line), xPos, startCol, nearest);
+}
+
+uint KateRenderer::textPos(const KateTextLine::Ptr &textLine, int xPos, uint startCol, bool nearest)
+{
+ Q_ASSERT(textLine);
+ if (!textLine)
+ return 0;
+
+ KateFontStruct *fs = config()->fontStruct();
+
+ int x, oldX;
+ x = oldX = 0;
+
+ uint z = startCol;
+ const uint len = textLine->length();
+ const QString &textString = textLine->string();
+
+ while ( (x < xPos) && (z < len)) {
+ oldX = x;
+
+ KateAttribute* a = attribute(textLine->attribute(z));
+ x += a->width(*fs, textString, z, m_tabWidth);
+
+ z++;
+ }
+ if ( ( (! nearest) || xPos - oldX < x - xPos ) && z > 0 ) {
+ z--;
+ // newXPos = oldX;
+ }// else newXPos = x;
+ return z;
+}
+
+uint KateRenderer::fontHeight()
+{
+ return config()->fontStruct ()->fontHeight;
+}
+
+uint KateRenderer::documentHeight()
+{
+ return m_doc->numLines() * fontHeight();
+}
+
+bool KateRenderer::getSelectionBounds(uint line, uint lineLength, uint &start, uint &end)
+{
+ bool hasSel = false;
+
+ if (m_view->hasSelection() && !m_view->blockSelectionMode())
+ {
+ if (m_view->lineIsSelection(line))
+ {
+ start = m_view->selStartCol();
+ end = m_view->selEndCol();
+ hasSel = true;
+ }
+ else if ((int)line == m_view->selStartLine())
+ {
+ start = m_view->selStartCol();
+ end = lineLength;
+ hasSel = true;
+ }
+ else if ((int)line == m_view->selEndLine())
+ {
+ start = 0;
+ end = m_view->selEndCol();
+ hasSel = true;
+ }
+ }
+ else if (m_view->lineHasSelected(line))
+ {
+ start = m_view->selStartCol();
+ end = m_view->selEndCol();
+ hasSel = true;
+ }
+
+ if (start > end) {
+ int temp = end;
+ end = start;
+ start = temp;
+ }
+
+ return hasSel;
+}
+
+void KateRenderer::updateConfig ()
+{
+ // update the attibute list pointer
+ updateAttributes ();
+
+ if (m_view)
+ m_view->updateRendererConfig();
+}
+
+uint KateRenderer::spaceWidth()
+{
+ return attribute(0)->width(*config()->fontStruct(), QChar(' '), m_tabWidth);
+}
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katerenderer.h b/kate/part/katerenderer.h
new file mode 100644
index 000000000..fc4865ab1
--- /dev/null
+++ b/kate/part/katerenderer.h
@@ -0,0 +1,272 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2003 Hamish Rodda <rodda@kde.org>
+ Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
+ Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KATE_RENDERER_H__
+#define __KATE_RENDERER_H__
+
+#include "katecursor.h"
+#include "kateattribute.h"
+#include "katetextline.h"
+
+#include <qfont.h>
+#include <qfontmetrics.h>
+
+class KateDocument;
+class KateView;
+class KateLineRange;
+class KateRendererConfig;
+
+/**
+ * Handles all of the work of rendering the text
+ * (used for the views and printing)
+ *
+ **/
+class KateRenderer
+{
+public:
+ /**
+ * Style of Caret (Insert or Replace mode)
+ */
+ enum caretStyles {
+ Insert,
+ Replace
+ };
+
+ /**
+ * Constructor
+ * @param doc document to render
+ * @param view view which is output (0 for example for rendering to print)
+ */
+ KateRenderer(KateDocument* doc, KateView *view = 0);
+
+ /**
+ * Destructor
+ */
+ ~KateRenderer();
+
+ /**
+ * update the highlighting attributes
+ * (for example after an hl change or after hl config changed)
+ */
+ void updateAttributes ();
+
+ /**
+ * Determine whether the caret (text cursor) will be drawn.
+ * @return should it be drawn?
+ */
+ inline bool drawCaret() const { return m_drawCaret; }
+
+ /**
+ * Set whether the caret (text cursor) will be drawn.
+ * @param drawCaret should caret be drawn?
+ */
+ void setDrawCaret(bool drawCaret);
+
+ /**
+ * The style of the caret (text cursor) to be painted.
+ * @return caretStyle
+ */
+ inline KateRenderer::caretStyles caretStyle() const { return m_caretStyle; }
+
+ /**
+ * Set the style of caret to be painted.
+ * @param style style to set
+ */
+ void setCaretStyle(KateRenderer::caretStyles style);
+
+ /**
+ * @returns whether tabs should be shown (ie. a small mark
+ * drawn to identify a tab)
+ * @return tabs should be shown
+ */
+ inline bool showTabs() const { return m_showTabs; }
+
+ /**
+ * Set whether a mark should be painted to help identifying tabs.
+ * @param showTabs show the tabs?
+ */
+ void setShowTabs(bool showTabs);
+
+ /**
+ * Sets the width of the tab. Helps performance.
+ * @param tabWidth new tab width
+ */
+ void setTabWidth(int tabWidth);
+
+ /**
+ * @returns whether indent lines should be shown
+ * @return indent lines should be shown
+ */
+ bool showIndentLines() const;
+
+ /**
+ * Set whether a guide should be painted to help identifying indent lines.
+ * @param showLines show the indent lines?
+ */
+ void setShowIndentLines(bool showLines);
+
+ /**
+ * Sets the width of the tab. Helps performance.
+ * @param indentWidth new indent width
+ */
+ void setIndentWidth(int indentWidth);
+
+ /**
+ * Show the view's selection?
+ * @return show sels?
+ */
+ inline bool showSelections() const { return m_showSelections; }
+
+ /**
+ * Set whether the view's selections should be shown.
+ * The default is true.
+ * @param showSelections show the selections?
+ */
+ void setShowSelections(bool showSelections);
+
+ /**
+ * Change to a different font (soon to be font set?)
+ */
+ void increaseFontSizes();
+ void decreaseFontSizes();
+ const QFont* currentFont();
+ const QFontMetrics* currentFontMetrics();
+
+ /**
+ * @return whether the renderer is configured to paint in a
+ * printer-friendly fashion.
+ */
+ bool isPrinterFriendly() const;
+
+ /**
+ * Configure this renderer to paint in a printer-friendly fashion.
+ *
+ * Sets the other options appropriately if true.
+ */
+ void setPrinterFriendly(bool printerFriendly);
+
+ /**
+ * Text width & height calculation functions...
+ */
+
+ // Width calculators
+ uint spaceWidth();
+ uint textWidth(const KateTextLine::Ptr &, int cursorCol);
+ uint textWidth(const KateTextLine::Ptr &textLine, uint startcol, uint maxwidth, bool *needWrap, int *endX = 0);
+ uint textWidth(const KateTextCursor &cursor);
+
+ // Cursor constrainer
+ uint textWidth(KateTextCursor &cursor, int xPos, uint startCol = 0);
+
+ // Column calculators
+ /**
+ * @return the index of the character at the horixontal position @p xpos
+ * in @p line.
+ *
+ * If @p nearest is true, the character starting nearest to
+ * @p xPos is returned. If @p nearest is false, the index of the character
+ * containing @p xPos is returned.
+ **/
+ uint textPos(uint line, int xPos, uint startCol = 0, bool nearest=true);
+ /**
+ * @overload
+ */
+ uint textPos(const KateTextLine::Ptr &, int xPos, uint startCol = 0, bool nearest=true);
+
+ // Font height
+ uint fontHeight();
+
+ // Document height
+ uint documentHeight();
+
+ // Selection boundaries
+ bool getSelectionBounds(uint line, uint lineLength, uint &start, uint &end);
+
+ /**
+ * This is the ultimate function to perform painting of a text line.
+ * (supports startcol/endcol, startx/endx)
+ *
+ * The text line is painted from the upper limit of (0,0). To move that,
+ * apply a transform to your painter.
+ */
+ void paintTextLine(QPainter& paint, const KateLineRange* range, int xStart, int xEnd, const KateTextCursor* cursor = 0L, const KateBracketRange* bracketmark = 0L);
+
+ /**
+ * Paint the background of a line
+ *
+ * Split off from the main @ref paintTextLine method to make it smaller. As it's being
+ * called only once per line it shouldn't noticably affect performance and it
+ * helps readability a LOT.
+ *
+ * @return whether the selection has been painted or not
+ */
+ bool paintTextLineBackground(QPainter& paint, int line, bool isCurrentLine, int xStart, int xEnd);
+
+ /**
+ * This takes an in index, and returns all the attributes for it.
+ * For example, if you have a ktextline, and want the KateAttribute
+ * for a given position, do:
+ *
+ * attribute(myktextline->attribute(position));
+ */
+ KateAttribute* attribute(uint pos);
+
+ private:
+ /**
+ * Paint a whitespace marker on position (x, y).
+ *
+ * Currently only used by the tabs, but it will also be used for highlighting trailing whitespace
+ */
+ void paintWhitespaceMarker(QPainter &paint, uint x, uint y);
+
+ /** Paint a SciTE-like indent marker. */
+ void paintIndentMarker(QPainter &paint, uint x, uint y);
+
+ KateDocument* m_doc;
+ KateView *m_view;
+
+ // cache of config values
+ int m_tabWidth;
+ int m_indentWidth;
+ uint m_schema;
+
+ // some internal flags
+ KateRenderer::caretStyles m_caretStyle;
+ bool m_drawCaret;
+ bool m_showSelections;
+ bool m_showTabs;
+ bool m_printerFriendly;
+
+ QMemArray<KateAttribute> *m_attributes;
+
+ /**
+ * Configuration
+ */
+ public:
+ inline KateRendererConfig *config () { return m_config; };
+
+ void updateConfig ();
+
+ private:
+ KateRendererConfig *m_config;
+};
+
+#endif
diff --git a/kate/part/kateschema.cpp b/kate/part/kateschema.cpp
new file mode 100644
index 000000000..e0a5d5dba
--- /dev/null
+++ b/kate/part/kateschema.cpp
@@ -0,0 +1,1611 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2001-2003 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2002, 2003 Anders Lund <anders.lund@lund.tdcadsl.dk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+//BEGIN Includes
+#include "kateschema.h"
+#include "kateschema.moc"
+
+#include "kateconfig.h"
+#include "katedocument.h"
+#include "katefactory.h"
+#include "kateview.h"
+#include "katerenderer.h"
+
+#include <klocale.h>
+#include <kdialogbase.h>
+#include <kcolorbutton.h>
+#include <kcombobox.h>
+#include <kinputdialog.h>
+#include <kfontdialog.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <kmessagebox.h>
+#include <kpopupmenu.h>
+#include <kcolordialog.h>
+#include <kapplication.h>
+#include <kaboutdata.h>
+#include <ktexteditor/markinterface.h>
+
+#include <qbuttongroup.h>
+#include <qcheckbox.h>
+#include <qptrcollection.h>
+#include <qdialog.h>
+#include <qgrid.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qtextcodec.h>
+#include <qlayout.h>
+#include <qlineedit.h>
+#include <qheader.h>
+#include <qlistbox.h>
+#include <qhbox.h>
+#include <qpainter.h>
+#include <qobjectlist.h>
+#include <qpixmap.h>
+#include <qpushbutton.h>
+#include <qradiobutton.h>
+#include <qspinbox.h>
+#include <qstringlist.h>
+#include <qtabwidget.h>
+#include <qvbox.h>
+#include <qvgroupbox.h>
+#include <qwhatsthis.h>
+//END
+
+//BEGIN KateStyleListViewItem decl
+/*
+ QListViewItem subclass to display/edit a style, bold/italic is check boxes,
+ normal and selected colors are boxes, which will display a color chooser when
+ activated.
+ The context name for the style will be drawn using the editor default font and
+ the chosen colors.
+ This widget id designed to handle the default as well as the individual hl style
+ lists.
+ This widget is designed to work with the KateStyleListView class exclusively.
+ Added by anders, jan 23 2002.
+*/
+class KateStyleListItem : public QListViewItem
+{
+ public:
+ KateStyleListItem( QListViewItem *parent=0, const QString & stylename=0,
+ class KateAttribute* defaultstyle=0, class KateHlItemData *data=0 );
+ KateStyleListItem( QListView *parent, const QString & stylename=0,
+ class KateAttribute* defaultstyle=0, class KateHlItemData *data=0 );
+ ~KateStyleListItem() { if (st) delete is; };
+
+ /* mainly for readability */
+ enum Property { ContextName, Bold, Italic, Underline, Strikeout, Color, SelColor, BgColor, SelBgColor, UseDefStyle };
+
+ /* initializes the style from the default and the hldata */
+ void initStyle();
+ /* updates the hldata's style */
+ void updateStyle();
+ /* reimp */
+ virtual int width ( const QFontMetrics & fm, const QListView * lv, int c ) const;
+ /* calls changeProperty() if it makes sense considering pos. */
+ void activate( int column, const QPoint &localPos );
+ /* For bool fields, toggles them, for color fields, display a color chooser */
+ void changeProperty( Property p );
+ /** unset a color.
+ * c is 100 (BGColor) or 101 (SelectedBGColor) for now.
+ */
+ void unsetColor( int c );
+ /* style context name */
+ QString contextName() { return text(0); };
+ /* only true for a hl mode item using it's default style */
+ bool defStyle();
+ /* true for default styles */
+ bool isDefault();
+ /* whichever style is active (st for hl mode styles not using
+ the default style, ds otherwise) */
+ class KateAttribute* style() { return is; };
+
+ protected:
+ /* reimp */
+ void paintCell(QPainter *p, const QColorGroup& cg, int col, int width, int align);
+
+ private:
+ /* private methods to change properties */
+ void toggleDefStyle();
+ void setColor( int );
+ /* helper function to copy the default style into the KateHlItemData,
+ when a property is changed and we are using default style. */
+
+ class KateAttribute *is, // the style currently in use
+ *ds; // default style for hl mode contexts and default styles
+ class KateHlItemData *st; // itemdata for hl mode contexts
+};
+//END
+
+//BEGIN KateStyleListCaption decl
+/*
+ This is a simple subclass for drawing the language names in a nice treeview
+ with the styles. It is needed because we do not like to mess with the default
+ palette of the containing ListView. Only the paintCell method is overwritten
+ to use our own palette (that is set on the viewport rather than on the listview
+ itself).
+*/
+class KateStyleListCaption : public QListViewItem
+{
+ public:
+ KateStyleListCaption( QListView *parent, const QString & name );
+ ~KateStyleListCaption() {};
+
+ protected:
+ void paintCell(QPainter *p, const QColorGroup& cg, int col, int width, int align);
+};
+//END
+
+//BEGIN KateSchemaManager
+QString KateSchemaManager::normalSchema ()
+{
+ return KApplication::kApplication()->aboutData()->appName () + QString (" - Normal");
+}
+
+QString KateSchemaManager::printingSchema ()
+{
+ return KApplication::kApplication()->aboutData()->appName () + QString (" - Printing");
+}
+
+KateSchemaManager::KateSchemaManager ()
+ : m_config ("kateschemarc", false, false)
+{
+ update ();
+}
+
+KateSchemaManager::~KateSchemaManager ()
+{
+}
+
+//
+// read the types from config file and update the internal list
+//
+void KateSchemaManager::update (bool readfromfile)
+{
+ if (readfromfile)
+ m_config.reparseConfiguration ();
+
+ m_schemas = m_config.groupList();
+ m_schemas.sort ();
+
+ m_schemas.remove (printingSchema());
+ m_schemas.remove (normalSchema());
+ m_schemas.prepend (printingSchema());
+ m_schemas.prepend (normalSchema());
+}
+
+//
+// get the right group
+// special handling of the default schemas ;)
+//
+KConfig *KateSchemaManager::schema (uint number)
+{
+ if ((number>1) && (number < m_schemas.count()))
+ m_config.setGroup (m_schemas[number]);
+ else if (number == 1)
+ m_config.setGroup (printingSchema());
+ else
+ m_config.setGroup (normalSchema());
+
+ return &m_config;
+}
+
+void KateSchemaManager::addSchema (const QString &t)
+{
+ m_config.setGroup (t);
+ m_config.writeEntry("Color Background", KGlobalSettings::baseColor());
+
+ update (false);
+}
+
+void KateSchemaManager::removeSchema (uint number)
+{
+ if (number >= m_schemas.count())
+ return;
+
+ if (number < 2)
+ return;
+
+ m_config.deleteGroup (name (number));
+
+ update (false);
+}
+
+bool KateSchemaManager::validSchema (uint number)
+{
+ if (number < m_schemas.count())
+ return true;
+
+ return false;
+}
+
+uint KateSchemaManager::number (const QString &name)
+{
+ if (name == normalSchema())
+ return 0;
+
+ if (name == printingSchema())
+ return 1;
+
+ int i;
+ if ((i = m_schemas.findIndex(name)) > -1)
+ return i;
+
+ return 0;
+}
+
+QString KateSchemaManager::name (uint number)
+{
+ if ((number>1) && (number < m_schemas.count()))
+ return m_schemas[number];
+ else if (number == 1)
+ return printingSchema();
+
+ return normalSchema();
+}
+//END
+
+//
+// DIALOGS !!!
+//
+
+//BEGIN KateSchemaConfigColorTab -- 'Colors' tab
+KateSchemaConfigColorTab::KateSchemaConfigColorTab( QWidget *parent, const char * )
+ : QWidget (parent)
+{
+ m_schema = -1;
+
+ QHBox *b;
+ QLabel *label;
+
+ QVBoxLayout *blay=new QVBoxLayout(this, 0, KDialog::spacingHint());
+
+ QVGroupBox *gbTextArea = new QVGroupBox(i18n("Text Area Background"), this);
+
+ b = new QHBox (gbTextArea);
+ b->setSpacing(KDialog::spacingHint());
+ label = new QLabel( i18n("Normal text:"), b);
+ label->setAlignment( AlignLeft|AlignVCenter);
+ m_back = new KColorButton(b);
+
+ b = new QHBox (gbTextArea);
+ b->setSpacing(KDialog::spacingHint());
+ label = new QLabel( i18n("Selected text:"), b);
+ label->setAlignment( AlignLeft|AlignVCenter);
+ m_selected = new KColorButton(b);
+
+ b = new QHBox (gbTextArea);
+ b->setSpacing(KDialog::spacingHint());
+ label = new QLabel( i18n("Current line:"), b);
+ label->setAlignment( AlignLeft|AlignVCenter);
+ m_current = new KColorButton(b);
+
+ // Markers from kdelibs/interfaces/ktextinterface/markinterface.h
+ b = new QHBox (gbTextArea);
+ b->setSpacing(KDialog::spacingHint());
+ m_combobox = new KComboBox(b, "color_combo_box");
+ // add the predefined mark types as defined in markinterface.h
+ m_combobox->insertItem(i18n("Bookmark")); // markType01
+ m_combobox->insertItem(i18n("Active Breakpoint")); // markType02
+ m_combobox->insertItem(i18n("Reached Breakpoint")); // markType03
+ m_combobox->insertItem(i18n("Disabled Breakpoint")); // markType04
+ m_combobox->insertItem(i18n("Execution")); // markType05
+ m_combobox->insertItem(i18n("Warning")); // markType06
+ m_combobox->insertItem(i18n("Error")); // markType07
+ m_combobox->setCurrentItem(0);
+ m_markers = new KColorButton(b, "marker_color_button");
+ connect( m_combobox, SIGNAL( activated( int ) ), SLOT( slotComboBoxChanged( int ) ) );
+
+ blay->addWidget(gbTextArea);
+
+ QVGroupBox *gbBorder = new QVGroupBox(i18n("Additional Elements"), this);
+
+ b = new QHBox (gbBorder);
+ b->setSpacing(KDialog::spacingHint());
+ label = new QLabel( i18n("Left border background:"), b);
+ label->setAlignment( AlignLeft|AlignVCenter);
+ m_iconborder = new KColorButton(b);
+
+ b = new QHBox (gbBorder);
+ b->setSpacing(KDialog::spacingHint());
+ label = new QLabel( i18n("Line numbers:"), b);
+ label->setAlignment( AlignLeft|AlignVCenter);
+ m_linenumber = new KColorButton(b);
+
+ b = new QHBox (gbBorder);
+ b->setSpacing(KDialog::spacingHint());
+ label = new QLabel( i18n("Bracket highlight:"), b);
+ label->setAlignment( AlignLeft|AlignVCenter);
+ m_bracket = new KColorButton(b);
+
+ b = new QHBox (gbBorder);
+ b->setSpacing(KDialog::spacingHint());
+ label = new QLabel( i18n("Word wrap markers:"), b);
+ label->setAlignment( AlignLeft|AlignVCenter);
+ m_wwmarker = new KColorButton(b);
+
+ b = new QHBox (gbBorder);
+ b->setSpacing(KDialog::spacingHint());
+ label = new QLabel( i18n("Tab markers:"), b);
+ label->setAlignment( AlignLeft|AlignVCenter);
+ m_tmarker = new KColorButton(b);
+
+ blay->addWidget(gbBorder);
+
+ blay->addStretch();
+
+ // connect signal changed(); changed is emitted by a ColorButton change!
+ connect( this, SIGNAL( changed() ), parent->parentWidget(), SLOT( slotChanged() ) );
+
+ // QWhatsThis help
+ QWhatsThis::add(m_back, i18n("<p>Sets the background color of the editing area.</p>"));
+ QWhatsThis::add(m_selected, i18n("<p>Sets the background color of the selection.</p>"
+ "<p>To set the text color for selected text, use the \"<b>Configure "
+ "Highlighting</b>\" dialog.</p>"));
+ QWhatsThis::add(m_markers, i18n("<p>Sets the background color of the selected "
+ "marker type.</p><p><b>Note</b>: The marker color is displayed lightly because "
+ "of transparency.</p>"));
+ QWhatsThis::add(m_combobox, i18n("<p>Select the marker type you want to change.</p>"));
+ QWhatsThis::add(m_current, i18n("<p>Sets the background color of the currently "
+ "active line, which means the line where your cursor is positioned.</p>"));
+ QWhatsThis::add( m_linenumber, i18n(
+ "<p>This color will be used to draw the line numbers (if enabled) and the "
+ "lines in the code-folding pane.</p>" ) );
+ QWhatsThis::add(m_bracket, i18n("<p>Sets the bracket matching color. This means, "
+ "if you place the cursor e.g. at a <b>(</b>, the matching <b>)</b> will "
+ "be highlighted with this color.</p>"));
+ QWhatsThis::add(m_wwmarker, i18n(
+ "<p>Sets the color of Word Wrap-related markers:</p>"
+ "<dl><dt>Static Word Wrap</dt><dd>A vertical line which shows the column where "
+ "text is going to be wrapped</dd>"
+ "<dt>Dynamic Word Wrap</dt><dd>An arrow shown to the left of "
+ "visually-wrapped lines</dd></dl>"));
+ QWhatsThis::add(m_tmarker, i18n(
+ "<p>Sets the color of the tabulator marks:</p>"));
+}
+
+KateSchemaConfigColorTab::~KateSchemaConfigColorTab()
+{
+}
+
+void KateSchemaConfigColorTab::schemaChanged ( int newSchema )
+{
+ // save curent schema
+ if ( m_schema > -1 )
+ {
+ m_schemas[ m_schema ].back = m_back->color();
+ m_schemas[ m_schema ].selected = m_selected->color();
+ m_schemas[ m_schema ].current = m_current->color();
+ m_schemas[ m_schema ].bracket = m_bracket->color();
+ m_schemas[ m_schema ].wwmarker = m_wwmarker->color();
+ m_schemas[ m_schema ].iconborder = m_iconborder->color();
+ m_schemas[ m_schema ].tmarker = m_tmarker->color();
+ m_schemas[ m_schema ].linenumber = m_linenumber->color();
+ }
+
+ if ( newSchema == m_schema ) return;
+
+ // switch
+ m_schema = newSchema;
+
+ // first disconnect all signals otherwise setColor emits changed
+ m_back ->disconnect( SIGNAL( changed( const QColor & ) ) );
+ m_selected ->disconnect( SIGNAL( changed( const QColor & ) ) );
+ m_current ->disconnect( SIGNAL( changed( const QColor & ) ) );
+ m_bracket ->disconnect( SIGNAL( changed( const QColor & ) ) );
+ m_wwmarker ->disconnect( SIGNAL( changed( const QColor & ) ) );
+ m_iconborder->disconnect( SIGNAL( changed( const QColor & ) ) );
+ m_tmarker ->disconnect( SIGNAL( changed( const QColor & ) ) );
+ m_markers ->disconnect( SIGNAL( changed( const QColor & ) ) );
+ m_linenumber->disconnect( SIGNAL( changed( const QColor & ) ) );
+
+ // If we havent this schema, read in from config file
+ if ( ! m_schemas.contains( newSchema ) )
+ {
+ // fallback defaults
+ QColor tmp0 (KGlobalSettings::baseColor());
+ QColor tmp1 (KGlobalSettings::highlightColor());
+ QColor tmp2 (KGlobalSettings::alternateBackgroundColor());
+ QColor tmp3 ( "#FFFF99" );
+ QColor tmp4 (tmp2.dark());
+ QColor tmp5 ( KGlobalSettings::textColor() );
+ QColor tmp6 ( "#EAE9E8" );
+ QColor tmp7 ( "#000000" );
+
+ // same std colors like in KateDocument::markColor
+ QValueVector <QColor> mark(KTextEditor::MarkInterface::reservedMarkersCount());
+ Q_ASSERT(mark.size() > 6);
+ mark[0] = Qt::blue;
+ mark[1] = Qt::red;
+ mark[2] = Qt::yellow;
+ mark[3] = Qt::magenta;
+ mark[4] = Qt::gray;
+ mark[5] = Qt::green;
+ mark[6] = Qt::red;
+
+ SchemaColors c;
+ KConfig *config = KateFactory::self()->schemaManager()->schema(newSchema);
+
+ c.back= config->readColorEntry("Color Background", &tmp0);
+ c.selected = config->readColorEntry("Color Selection", &tmp1);
+ c.current = config->readColorEntry("Color Highlighted Line", &tmp2);
+ c.bracket = config->readColorEntry("Color Highlighted Bracket", &tmp3);
+ c.wwmarker = config->readColorEntry("Color Word Wrap Marker", &tmp4);
+ c.tmarker = config->readColorEntry("Color Tab Marker", &tmp5);
+ c.iconborder = config->readColorEntry("Color Icon Bar", &tmp6);
+ c.linenumber = config->readColorEntry("Color Line Number", &tmp7);
+
+ for (int i = 0; i < KTextEditor::MarkInterface::reservedMarkersCount(); i++)
+ c.markerColors[i] = config->readColorEntry( QString("Color MarkType%1").arg(i+1), &mark[i] );
+
+ m_schemas[ newSchema ] = c;
+ }
+
+ m_back->setColor( m_schemas[ newSchema ].back);
+ m_selected->setColor( m_schemas [ newSchema ].selected );
+ m_current->setColor( m_schemas [ newSchema ].current );
+ m_bracket->setColor( m_schemas [ newSchema ].bracket );
+ m_wwmarker->setColor( m_schemas [ newSchema ].wwmarker );
+ m_tmarker->setColor( m_schemas [ newSchema ].tmarker );
+ m_iconborder->setColor( m_schemas [ newSchema ].iconborder );
+ m_linenumber->setColor( m_schemas [ newSchema ].linenumber );
+
+ // map from 0..reservedMarkersCount()-1 - the same index as in markInterface
+ for (int i = 0; i < KTextEditor::MarkInterface::reservedMarkersCount(); i++)
+ {
+ QPixmap pix(16, 16);
+ pix.fill( m_schemas [ newSchema ].markerColors[i]);
+ m_combobox->changeItem(pix, m_combobox->text(i), i);
+ }
+ m_markers->setColor( m_schemas [ newSchema ].markerColors[ m_combobox->currentItem() ] );
+
+ connect( m_back , SIGNAL( changed( const QColor& ) ), SIGNAL( changed() ) );
+ connect( m_selected , SIGNAL( changed( const QColor& ) ), SIGNAL( changed() ) );
+ connect( m_current , SIGNAL( changed( const QColor& ) ), SIGNAL( changed() ) );
+ connect( m_bracket , SIGNAL( changed( const QColor& ) ), SIGNAL( changed() ) );
+ connect( m_wwmarker , SIGNAL( changed( const QColor& ) ), SIGNAL( changed() ) );
+ connect( m_iconborder, SIGNAL( changed( const QColor& ) ), SIGNAL( changed() ) );
+ connect( m_tmarker , SIGNAL( changed( const QColor& ) ), SIGNAL( changed() ) );
+ connect( m_linenumber, SIGNAL( changed( const QColor& ) ), SIGNAL( changed() ) );
+ connect( m_markers , SIGNAL( changed( const QColor& ) ), SLOT( slotMarkerColorChanged( const QColor& ) ) );
+}
+
+void KateSchemaConfigColorTab::apply ()
+{
+ schemaChanged( m_schema );
+ QMap<int,SchemaColors>::Iterator it;
+ for ( it = m_schemas.begin(); it != m_schemas.end(); ++it )
+ {
+ kdDebug(13030)<<"APPLY scheme = "<<it.key()<<endl;
+ KConfig *config = KateFactory::self()->schemaManager()->schema( it.key() );
+ kdDebug(13030)<<"Using config group "<<config->group()<<endl;
+ SchemaColors c = it.data();
+
+ config->writeEntry("Color Background", c.back);
+ config->writeEntry("Color Selection", c.selected);
+ config->writeEntry("Color Highlighted Line", c.current);
+ config->writeEntry("Color Highlighted Bracket", c.bracket);
+ config->writeEntry("Color Word Wrap Marker", c.wwmarker);
+ config->writeEntry("Color Tab Marker", c.tmarker);
+ config->writeEntry("Color Icon Bar", c.iconborder);
+ config->writeEntry("Color Line Number", c.linenumber);
+
+ for (int i = 0; i < KTextEditor::MarkInterface::reservedMarkersCount(); i++)
+ {
+ config->writeEntry(QString("Color MarkType%1").arg(i + 1), c.markerColors[i]);
+ }
+ }
+}
+
+void KateSchemaConfigColorTab::slotMarkerColorChanged( const QColor& color)
+{
+ int index = m_combobox->currentItem();
+ m_schemas[ m_schema ].markerColors[ index ] = color;
+ QPixmap pix(16, 16);
+ pix.fill(color);
+ m_combobox->changeItem(pix, m_combobox->text(index), index);
+
+ emit changed();
+}
+
+void KateSchemaConfigColorTab::slotComboBoxChanged(int index)
+{
+ // temporarily disconnect the changed-signal because setColor emits changed as well
+ m_markers->disconnect( SIGNAL( changed( const QColor& ) ) );
+ m_markers->setColor( m_schemas[m_schema].markerColors[index] );
+ connect( m_markers, SIGNAL( changed( const QColor& ) ), SLOT( slotMarkerColorChanged( const QColor& ) ) );
+}
+
+//END KateSchemaConfigColorTab
+
+//BEGIN FontConfig -- 'Fonts' tab
+KateSchemaConfigFontTab::KateSchemaConfigFontTab( QWidget *parent, const char * )
+ : QWidget (parent)
+{
+ // sizemanagment
+ QGridLayout *grid = new QGridLayout( this, 1, 1 );
+
+ m_fontchooser = new KFontChooser ( this, 0L, false, QStringList(), false );
+ m_fontchooser->enableColumn(KFontChooser::StyleList, false);
+ grid->addWidget( m_fontchooser, 0, 0);
+
+ connect (this, SIGNAL( changed()), parent->parentWidget(), SLOT (slotChanged()));
+ m_schema = -1;
+}
+
+KateSchemaConfigFontTab::~KateSchemaConfigFontTab()
+{
+}
+
+void KateSchemaConfigFontTab::slotFontSelected( const QFont &font )
+{
+ if ( m_schema > -1 )
+ {
+ m_fonts[m_schema] = font;
+ emit changed();
+ }
+}
+
+void KateSchemaConfigFontTab::apply()
+{
+ FontMap::Iterator it;
+ for ( it = m_fonts.begin(); it != m_fonts.end(); ++it )
+ {
+ KateFactory::self()->schemaManager()->schema( it.key() )->writeEntry( "Font", it.data() );
+ }
+}
+
+void KateSchemaConfigFontTab::schemaChanged( int newSchema )
+{
+ if ( m_schema > -1 )
+ m_fonts[ m_schema ] = m_fontchooser->font();
+
+ m_schema = newSchema;
+
+ QFont f (KGlobalSettings::fixedFont());
+
+ m_fontchooser->disconnect ( this );
+ m_fontchooser->setFont ( KateFactory::self()->schemaManager()->schema( newSchema )->readFontEntry("Font", &f) );
+ m_fonts[ newSchema ] = m_fontchooser->font();
+ connect (m_fontchooser, SIGNAL (fontSelected( const QFont & )), this, SLOT (slotFontSelected( const QFont & )));
+}
+//END FontConfig
+
+//BEGIN FontColorConfig -- 'Normal Text Styles' tab
+KateSchemaConfigFontColorTab::KateSchemaConfigFontColorTab( QWidget *parent, const char * )
+ : QWidget (parent)
+{
+ m_defaultStyleLists.setAutoDelete(true);
+
+ // sizemanagment
+ QGridLayout *grid = new QGridLayout( this, 1, 1 );
+
+ m_defaultStyles = new KateStyleListView( this, false );
+ grid->addWidget( m_defaultStyles, 0, 0);
+
+ connect (m_defaultStyles, SIGNAL (changed()), parent->parentWidget(), SLOT (slotChanged()));
+
+ QWhatsThis::add( m_defaultStyles, i18n(
+ "This list displays the default styles for the current schema and "
+ "offers the means to edit them. The style name reflects the current "
+ "style settings."
+ "<p>To edit the colors, click the colored squares, or select the color "
+ "to edit from the popup menu.<p>You can unset the Background and Selected "
+ "Background colors from the popup menu when appropriate.") );
+}
+
+KateSchemaConfigFontColorTab::~KateSchemaConfigFontColorTab()
+{
+}
+
+KateAttributeList *KateSchemaConfigFontColorTab::attributeList (uint schema)
+{
+ if (!m_defaultStyleLists[schema])
+ {
+ KateAttributeList *list = new KateAttributeList ();
+ KateHlManager::self()->getDefaults(schema, *list);
+
+ m_defaultStyleLists.insert (schema, list);
+ }
+
+ return m_defaultStyleLists[schema];
+}
+
+void KateSchemaConfigFontColorTab::schemaChanged (uint schema)
+{
+ m_defaultStyles->clear ();
+
+ KateAttributeList *l = attributeList (schema);
+
+ // set colors
+ QPalette p ( m_defaultStyles->palette() );
+ QColor _c ( KGlobalSettings::baseColor() );
+ p.setColor( QColorGroup::Base,
+ KateFactory::self()->schemaManager()->schema(schema)->
+ readColorEntry( "Color Background", &_c ) );
+ _c = KGlobalSettings::highlightColor();
+ p.setColor( QColorGroup::Highlight,
+ KateFactory::self()->schemaManager()->schema(schema)->
+ readColorEntry( "Color Selection", &_c ) );
+ _c = l->at(0)->textColor(); // not quite as much of an assumption ;)
+ p.setColor( QColorGroup::Text, _c );
+ m_defaultStyles->viewport()->setPalette( p );
+
+ // insert the default styles backwards to get them in the right order
+ for ( int i = KateHlManager::self()->defaultStyles() - 1; i >= 0; i-- )
+ {
+ new KateStyleListItem( m_defaultStyles, KateHlManager::self()->defaultStyleName(i, true), l->at( i ) );
+ }
+}
+
+void KateSchemaConfigFontColorTab::reload ()
+{
+ m_defaultStyles->clear ();
+ m_defaultStyleLists.clear ();
+}
+
+void KateSchemaConfigFontColorTab::apply ()
+{
+ for ( QIntDictIterator<KateAttributeList> it( m_defaultStyleLists ); it.current(); ++it )
+ KateHlManager::self()->setDefaults(it.currentKey(), *(it.current()));
+}
+
+//END FontColorConfig
+
+//BEGIN KateSchemaConfigHighlightTab -- 'Highlighting Text Styles' tab
+KateSchemaConfigHighlightTab::KateSchemaConfigHighlightTab( QWidget *parent, const char *, KateSchemaConfigFontColorTab *page, uint hl )
+ : QWidget (parent)
+{
+ m_defaults = page;
+
+ m_schema = 0;
+ m_hl = 0;
+
+ m_hlDict.setAutoDelete (true);
+
+ QVBoxLayout *layout = new QVBoxLayout(this, 0, KDialog::spacingHint() );
+
+ // hl chooser
+ QHBox *hbHl = new QHBox( this );
+ layout->add (hbHl);
+
+ hbHl->setSpacing( KDialog::spacingHint() );
+ QLabel *lHl = new QLabel( i18n("H&ighlight:"), hbHl );
+ hlCombo = new QComboBox( false, hbHl );
+ lHl->setBuddy( hlCombo );
+ connect( hlCombo, SIGNAL(activated(int)),
+ this, SLOT(hlChanged(int)) );
+
+ for( int i = 0; i < KateHlManager::self()->highlights(); i++) {
+ if (KateHlManager::self()->hlSection(i).length() > 0)
+ hlCombo->insertItem(KateHlManager::self()->hlSection(i) + QString ("/") + KateHlManager::self()->hlNameTranslated(i));
+ else
+ hlCombo->insertItem(KateHlManager::self()->hlNameTranslated(i));
+ }
+ hlCombo->setCurrentItem(0);
+
+ // styles listview
+ m_styles = new KateStyleListView( this, true );
+ layout->addWidget (m_styles, 999);
+
+ hlCombo->setCurrentItem ( hl );
+ hlChanged ( hl );
+
+ QWhatsThis::add( m_styles, i18n(
+ "This list displays the contexts of the current syntax highlight mode and "
+ "offers the means to edit them. The context name reflects the current "
+ "style settings.<p>To edit using the keyboard, press "
+ "<strong>&lt;SPACE&gt;</strong> and choose a property from the popup menu."
+ "<p>To edit the colors, click the colored squares, or select the color "
+ "to edit from the popup menu.<p>You can unset the Background and Selected "
+ "Background colors from the context menu when appropriate.") );
+
+ connect (m_styles, SIGNAL (changed()), parent->parentWidget(), SLOT (slotChanged()));
+}
+
+KateSchemaConfigHighlightTab::~KateSchemaConfigHighlightTab()
+{
+}
+
+void KateSchemaConfigHighlightTab::hlChanged(int z)
+{
+ m_hl = z;
+
+ schemaChanged (m_schema);
+}
+
+void KateSchemaConfigHighlightTab::schemaChanged (uint schema)
+{
+ m_schema = schema;
+
+ kdDebug(13030) << "NEW SCHEMA: " << m_schema << " NEW HL: " << m_hl << endl;
+
+ m_styles->clear ();
+
+ if (!m_hlDict[m_schema])
+ {
+ kdDebug(13030) << "NEW SCHEMA, create dict" << endl;
+
+ m_hlDict.insert (schema, new QIntDict<KateHlItemDataList>);
+ m_hlDict[m_schema]->setAutoDelete (true);
+ }
+
+ if (!m_hlDict[m_schema]->find(m_hl))
+ {
+ kdDebug(13030) << "NEW HL, create list" << endl;
+
+ KateHlItemDataList *list = new KateHlItemDataList ();
+ KateHlManager::self()->getHl( m_hl )->getKateHlItemDataListCopy (m_schema, *list);
+ m_hlDict[m_schema]->insert (m_hl, list);
+ }
+
+ KateAttributeList *l = m_defaults->attributeList (schema);
+
+ // Set listview colors
+ // We do that now, because we can now get the "normal text" color.
+ // TODO this reads of the KConfig object, which should be changed when
+ // the color tab is fixed.
+ QPalette p ( m_styles->palette() );
+ QColor _c ( KGlobalSettings::baseColor() );
+ p.setColor( QColorGroup::Base,
+ KateFactory::self()->schemaManager()->schema(m_schema)->
+ readColorEntry( "Color Background", &_c ) );
+ _c = KGlobalSettings::highlightColor();
+ p.setColor( QColorGroup::Highlight,
+ KateFactory::self()->schemaManager()->schema(m_schema)->
+ readColorEntry( "Color Selection", &_c ) );
+ _c = l->at(0)->textColor(); // not quite as much of an assumption ;)
+ p.setColor( QColorGroup::Text, _c );
+ m_styles->viewport()->setPalette( p );
+
+ QDict<KateStyleListCaption> prefixes;
+ for ( KateHlItemData *itemData = m_hlDict[m_schema]->find(m_hl)->last();
+ itemData != 0L;
+ itemData = m_hlDict[m_schema]->find(m_hl)->prev())
+ {
+ kdDebug(13030) << "insert items " << itemData->name << endl;
+
+ // All stylenames have their language mode prefixed, e.g. HTML:Comment
+ // split them and put them into nice substructures.
+ int c = itemData->name.find(':');
+ if ( c > 0 ) {
+ QString prefix = itemData->name.left(c);
+ QString name = itemData->name.mid(c+1);
+
+ KateStyleListCaption *parent = prefixes.find( prefix );
+ if ( ! parent )
+ {
+ parent = new KateStyleListCaption( m_styles, prefix );
+ parent->setOpen(true);
+ prefixes.insert( prefix, parent );
+ }
+ new KateStyleListItem( parent, name, l->at(itemData->defStyleNum), itemData );
+ } else {
+ new KateStyleListItem( m_styles, itemData->name, l->at(itemData->defStyleNum), itemData );
+ }
+ }
+}
+
+void KateSchemaConfigHighlightTab::reload ()
+{
+ m_styles->clear ();
+ m_hlDict.clear ();
+
+ hlChanged (0);
+}
+
+void KateSchemaConfigHighlightTab::apply ()
+{
+ for ( QIntDictIterator< QIntDict<KateHlItemDataList> > it( m_hlDict ); it.current(); ++it )
+ for ( QIntDictIterator< KateHlItemDataList > it2( *it.current() ); it2.current(); ++it2 )
+ {
+ KateHlManager::self()->getHl( it2.currentKey() )->setKateHlItemDataList (it.currentKey(), *(it2.current()));
+ }
+}
+
+//END KateSchemaConfigHighlightTab
+
+//BEGIN KateSchemaConfigPage -- Main dialog page
+KateSchemaConfigPage::KateSchemaConfigPage( QWidget *parent, KateDocument *doc )
+ : KateConfigPage( parent ),
+ m_lastSchema (-1)
+{
+ QVBoxLayout *layout = new QVBoxLayout(this, 0, KDialog::spacingHint() );
+
+ QHBox *hbHl = new QHBox( this );
+ layout->add (hbHl);
+ hbHl->setSpacing( KDialog::spacingHint() );
+ QLabel *lHl = new QLabel( i18n("&Schema:"), hbHl );
+ schemaCombo = new QComboBox( false, hbHl );
+ lHl->setBuddy( schemaCombo );
+ connect( schemaCombo, SIGNAL(activated(int)),
+ this, SLOT(schemaChanged(int)) );
+
+ QPushButton *btnnew = new QPushButton( i18n("&New..."), hbHl );
+ connect( btnnew, SIGNAL(clicked()), this, SLOT(newSchema()) );
+
+ btndel = new QPushButton( i18n("&Delete"), hbHl );
+ connect( btndel, SIGNAL(clicked()), this, SLOT(deleteSchema()) );
+
+ m_tabWidget = new QTabWidget ( this );
+ m_tabWidget->setMargin (KDialog::marginHint());
+ layout->add (m_tabWidget);
+
+ connect (m_tabWidget, SIGNAL (currentChanged (QWidget *)), this, SLOT (newCurrentPage (QWidget *)));
+
+ m_colorTab = new KateSchemaConfigColorTab (m_tabWidget);
+ m_tabWidget->addTab (m_colorTab, i18n("Colors"));
+
+ m_fontTab = new KateSchemaConfigFontTab (m_tabWidget);
+ m_tabWidget->addTab (m_fontTab, i18n("Font"));
+
+ m_fontColorTab = new KateSchemaConfigFontColorTab (m_tabWidget);
+ m_tabWidget->addTab (m_fontColorTab, i18n("Normal Text Styles"));
+
+ uint hl = doc ? doc->hlMode() : 0;
+ m_highlightTab = new KateSchemaConfigHighlightTab (m_tabWidget, "", m_fontColorTab, hl );
+ m_tabWidget->addTab (m_highlightTab, i18n("Highlighting Text Styles"));
+
+ hbHl = new QHBox( this );
+ layout->add (hbHl);
+ hbHl->setSpacing( KDialog::spacingHint() );
+ lHl = new QLabel( i18n("&Default schema for %1:").arg(KApplication::kApplication()->aboutData()->programName ()), hbHl );
+ defaultSchemaCombo = new QComboBox( false, hbHl );
+ lHl->setBuddy( defaultSchemaCombo );
+
+
+ m_defaultSchema = (doc && doc->activeView()) ? doc->activeView()->renderer()->config()->schema() : KateRendererConfig::global()->schema();
+
+ reload();
+
+ connect( defaultSchemaCombo, SIGNAL(activated(int)),
+ this, SLOT(slotChanged()) );
+}
+
+KateSchemaConfigPage::~KateSchemaConfigPage ()
+{
+ // just reload config from disc
+ KateFactory::self()->schemaManager()->update ();
+}
+
+void KateSchemaConfigPage::apply()
+{
+ m_colorTab->apply();
+ m_fontTab->apply();
+ m_fontColorTab->apply ();
+ m_highlightTab->apply ();
+
+ // just sync the config
+ KateFactory::self()->schemaManager()->schema (0)->sync();
+
+ KateFactory::self()->schemaManager()->update ();
+
+ // clear all attributes
+ for (int i = 0; i < KateHlManager::self()->highlights(); ++i)
+ KateHlManager::self()->getHl (i)->clearAttributeArrays ();
+
+ // than reload the whole stuff
+ KateRendererConfig::global()->setSchema (defaultSchemaCombo->currentItem());
+ KateRendererConfig::global()->reloadSchema();
+
+ // sync the hl config for real
+ KateHlManager::self()->getKConfig()->sync ();
+}
+
+void KateSchemaConfigPage::reload()
+{
+ // just reload the config from disc
+ KateFactory::self()->schemaManager()->update ();
+
+ // special for the highlighting stuff
+ m_fontColorTab->reload ();
+
+ update ();
+
+ defaultSchemaCombo->setCurrentItem (KateRendererConfig::global()->schema());
+
+ // initialize to the schema in the current document, or default schema
+ schemaCombo->setCurrentItem( m_defaultSchema );
+ schemaChanged( m_defaultSchema );
+}
+
+void KateSchemaConfigPage::reset()
+{
+ reload ();
+}
+
+void KateSchemaConfigPage::defaults()
+{
+ reload ();
+}
+
+void KateSchemaConfigPage::update ()
+{
+ // soft update, no load from disk
+ KateFactory::self()->schemaManager()->update (false);
+
+ schemaCombo->clear ();
+ schemaCombo->insertStringList (KateFactory::self()->schemaManager()->list ());
+
+ defaultSchemaCombo->clear ();
+ defaultSchemaCombo->insertStringList (KateFactory::self()->schemaManager()->list ());
+
+ schemaCombo->setCurrentItem (0);
+ schemaChanged (0);
+
+ schemaCombo->setEnabled (schemaCombo->count() > 0);
+}
+
+void KateSchemaConfigPage::deleteSchema ()
+{
+ int t = schemaCombo->currentItem ();
+
+ KateFactory::self()->schemaManager()->removeSchema (t);
+
+ update ();
+}
+
+void KateSchemaConfigPage::newSchema ()
+{
+ QString t = KInputDialog::getText (i18n("Name for New Schema"), i18n ("Name:"), i18n("New Schema"), 0, this);
+
+ KateFactory::self()->schemaManager()->addSchema (t);
+
+ // soft update, no load from disk
+ KateFactory::self()->schemaManager()->update (false);
+ int i = KateFactory::self()->schemaManager()->list ().findIndex (t);
+
+ update ();
+ if (i > -1)
+ {
+ schemaCombo->setCurrentItem (i);
+ schemaChanged (i);
+ }
+}
+
+void KateSchemaConfigPage::schemaChanged (int schema)
+{
+ btndel->setEnabled( schema > 1 );
+
+ m_colorTab->schemaChanged( schema );
+ m_fontTab->schemaChanged( schema );
+ m_fontColorTab->schemaChanged (schema);
+ m_highlightTab->schemaChanged (schema);
+
+ m_lastSchema = schema;
+}
+
+void KateSchemaConfigPage::newCurrentPage (QWidget *w)
+{
+ if (w == m_highlightTab)
+ m_highlightTab->schemaChanged (m_lastSchema);
+}
+//END KateSchemaConfigPage
+
+//BEGIN SCHEMA ACTION -- the 'View->Schema' menu action
+void KateViewSchemaAction::init()
+{
+ m_view = 0;
+ last = 0;
+
+ connect(popupMenu(),SIGNAL(aboutToShow()),this,SLOT(slotAboutToShow()));
+}
+
+void KateViewSchemaAction::updateMenu (KateView *view)
+{
+ m_view = view;
+}
+
+void KateViewSchemaAction::slotAboutToShow()
+{
+ KateView *view=m_view;
+ int count = KateFactory::self()->schemaManager()->list().count();
+
+ for (int z=0; z<count; z++)
+ {
+ QString hlName = KateFactory::self()->schemaManager()->list().operator[](z);
+
+ if (names.contains(hlName) < 1)
+ {
+ names << hlName;
+ popupMenu()->insertItem ( hlName, this, SLOT(setSchema(int)), 0, z+1);
+ }
+ }
+
+ if (!view) return;
+
+ popupMenu()->setItemChecked (last, false);
+ popupMenu()->setItemChecked (view->renderer()->config()->schema()+1, true);
+
+ last = view->renderer()->config()->schema()+1;
+}
+
+void KateViewSchemaAction::setSchema (int mode)
+{
+ KateView *view=m_view;
+
+ if (view)
+ view->renderer()->config()->setSchema (mode-1);
+}
+//END SCHEMA ACTION
+
+//BEGIN KateStyleListView
+KateStyleListView::KateStyleListView( QWidget *parent, bool showUseDefaults )
+ : QListView( parent )
+{
+ setSorting( -1 ); // disable sorting, let the styles appear in their defined order
+ addColumn( i18n("Context") );
+ addColumn( SmallIconSet("text_bold"), QString::null );
+ addColumn( SmallIconSet("text_italic"), QString::null );
+ addColumn( SmallIconSet("text_under"), QString::null );
+ addColumn( SmallIconSet("text_strike"), QString::null );
+ addColumn( i18n("Normal") );
+ addColumn( i18n("Selected") );
+ addColumn( i18n("Background") );
+ addColumn( i18n("Background Selected") );
+ if ( showUseDefaults )
+ addColumn( i18n("Use Default Style") );
+ connect( this, SIGNAL(mouseButtonPressed(int, QListViewItem*, const QPoint&, int)),
+ this, SLOT(slotMousePressed(int, QListViewItem*, const QPoint&, int)) );
+ connect( this, SIGNAL(contextMenuRequested(QListViewItem*,const QPoint&, int)),
+ this, SLOT(showPopupMenu(QListViewItem*, const QPoint&)) );
+ // grap the bg color, selected color and default font
+ normalcol = KGlobalSettings::textColor();
+ bgcol = KateRendererConfig::global()->backgroundColor();
+ selcol = KateRendererConfig::global()->selectionColor();
+ docfont = *KateRendererConfig::global()->font();
+
+ viewport()->setPaletteBackgroundColor( bgcol );
+}
+
+void KateStyleListView::showPopupMenu( KateStyleListItem *i, const QPoint &globalPos, bool showtitle )
+{
+ if ( !dynamic_cast<KateStyleListItem*>(i) ) return;
+
+ KPopupMenu m( this );
+ KateAttribute *is = i->style();
+ int id;
+ // the title is used, because the menu obscures the context name when
+ // displayed on behalf of spacePressed().
+ QPixmap cl(16,16);
+ cl.fill( i->style()->textColor() );
+ QPixmap scl(16,16);
+ scl.fill( i->style()->selectedTextColor() );
+ QPixmap bgcl(16,16);
+ bgcl.fill( i->style()->itemSet(KateAttribute::BGColor) ? i->style()->bgColor() : viewport()->colorGroup().base() );
+ QPixmap sbgcl(16,16);
+ sbgcl.fill( i->style()->itemSet(KateAttribute::SelectedBGColor) ? i->style()->selectedBGColor() : viewport()->colorGroup().base() );
+
+ if ( showtitle )
+ m.insertTitle( i->contextName(), KateStyleListItem::ContextName );
+ id = m.insertItem( i18n("&Bold"), this, SLOT(mSlotPopupHandler(int)), 0, KateStyleListItem::Bold );
+ m.setItemChecked( id, is->bold() );
+ id = m.insertItem( i18n("&Italic"), this, SLOT(mSlotPopupHandler(int)), 0, KateStyleListItem::Italic );
+ m.setItemChecked( id, is->italic() );
+ id = m.insertItem( i18n("&Underline"), this, SLOT(mSlotPopupHandler(int)), 0, KateStyleListItem::Underline );
+ m.setItemChecked( id, is->underline() );
+ id = m.insertItem( i18n("S&trikeout"), this, SLOT(mSlotPopupHandler(int)), 0, KateStyleListItem::Strikeout );
+ m.setItemChecked( id, is->strikeOut() );
+
+ m.insertSeparator();
+
+ m.insertItem( QIconSet(cl), i18n("Normal &Color..."), this, SLOT(mSlotPopupHandler(int)), 0, KateStyleListItem::Color );
+ m.insertItem( QIconSet(scl), i18n("&Selected Color..."), this, SLOT(mSlotPopupHandler(int)), 0, KateStyleListItem::SelColor );
+ m.insertItem( QIconSet(bgcl), i18n("&Background Color..."), this, SLOT(mSlotPopupHandler(int)), 0, KateStyleListItem::BgColor );
+ m.insertItem( QIconSet(sbgcl), i18n("S&elected Background Color..."), this, SLOT(mSlotPopupHandler(int)), 0, KateStyleListItem::SelBgColor );
+
+ // Unset [some] colors. I could show one only if that button was clicked, but that
+ // would disable setting this with the keyboard (how many aren't doing just
+ // that every day? ;)
+ // ANY ideas for doing this in a nicer way will be warmly wellcomed.
+ KateAttribute *style = i->style();
+ if ( style->itemSet( KateAttribute::BGColor) || style->itemSet( KateAttribute::SelectedBGColor ) )
+ {
+ m.insertSeparator();
+ if ( style->itemSet( KateAttribute::BGColor) )
+ m.insertItem( i18n("Unset Background Color"), this, SLOT(unsetColor(int)), 0, 100 );
+ if ( style->itemSet( KateAttribute::SelectedBGColor ) )
+ m.insertItem( i18n("Unset Selected Background Color"), this, SLOT(unsetColor(int)), 0, 101 );
+ }
+
+ if ( ! i->isDefault() && ! i->defStyle() ) {
+ m.insertSeparator();
+ id = m.insertItem( i18n("Use &Default Style"), this, SLOT(mSlotPopupHandler(int)), 0, KateStyleListItem::UseDefStyle );
+ m.setItemChecked( id, i->defStyle() );
+ }
+ m.exec( globalPos );
+}
+
+void KateStyleListView::showPopupMenu( QListViewItem *i, const QPoint &pos )
+{
+ if ( dynamic_cast<KateStyleListItem*>(i) )
+ showPopupMenu( (KateStyleListItem*)i, pos, true );
+}
+
+void KateStyleListView::mSlotPopupHandler( int z )
+{
+ ((KateStyleListItem*)currentItem())->changeProperty( (KateStyleListItem::Property)z );
+}
+
+void KateStyleListView::unsetColor( int c )
+{
+ ((KateStyleListItem*)currentItem())->unsetColor( c );
+ emitChanged();
+}
+
+// Because QListViewItem::activatePos() is going to become deprecated,
+// and also because this attempt offers more control, I connect mousePressed to this.
+void KateStyleListView::slotMousePressed(int btn, QListViewItem* i, const QPoint& pos, int c)
+{
+ if ( dynamic_cast<KateStyleListItem*>(i) ) {
+ if ( btn == Qt::LeftButton && c > 0 ) {
+ // map pos to item/column and call KateStyleListItem::activate(col, pos)
+ ((KateStyleListItem*)i)->activate( c, viewport()->mapFromGlobal( pos ) - QPoint( 0, itemRect(i).top() ) );
+ }
+ }
+}
+
+//END
+
+//BEGIN KateStyleListItem
+static const int BoxSize = 16;
+static const int ColorBtnWidth = 32;
+
+KateStyleListItem::KateStyleListItem( QListViewItem *parent, const QString & stylename,
+ KateAttribute *style, KateHlItemData *data )
+ : QListViewItem( parent, stylename ),
+ ds( style ),
+ st( data )
+{
+ initStyle();
+}
+
+KateStyleListItem::KateStyleListItem( QListView *parent, const QString & stylename,
+ KateAttribute *style, KateHlItemData *data )
+ : QListViewItem( parent, stylename ),
+ ds( style ),
+ st( data )
+{
+ initStyle();
+}
+
+void KateStyleListItem::initStyle()
+{
+ if (!st)
+ is = ds;
+ else
+ {
+ is = new KateAttribute (*ds);
+
+ if (st->isSomethingSet())
+ *is += *st;
+ }
+}
+
+void KateStyleListItem::updateStyle()
+{
+ // nothing there, not update it, will crash
+ if (!st)
+ return;
+
+ if ( is->itemSet(KateAttribute::Weight) )
+ {
+ if ( is->weight() != st->weight())
+ st->setWeight( is->weight() );
+ }
+ else st->clearAttribute( KateAttribute::Weight );
+
+ if ( is->itemSet(KateAttribute::Italic) )
+ {
+ if ( is->italic() != st->italic())
+ st->setItalic( is->italic() );
+ }
+ else st->clearAttribute( KateAttribute::Italic );
+
+ if ( is->itemSet(KateAttribute::StrikeOut) )
+ {
+ if ( is->strikeOut() != st->strikeOut())
+
+ st->setStrikeOut( is->strikeOut() );
+ }
+ else st->clearAttribute( KateAttribute::StrikeOut );
+
+ if ( is->itemSet(KateAttribute::Underline) )
+ {
+ if ( is->underline() != st->underline())
+ st->setUnderline( is->underline() );
+ }
+ else st->clearAttribute( KateAttribute::Underline );
+
+ if ( is->itemSet(KateAttribute::Outline) )
+ {
+ if ( is->outline() != st->outline())
+ st->setOutline( is->outline() );
+ }
+ else st->clearAttribute( KateAttribute::Outline );
+
+ if ( is->itemSet(KateAttribute::TextColor) )
+ {
+ if ( is->textColor() != st->textColor())
+ st->setTextColor( is->textColor() );
+ }
+ else st->clearAttribute( KateAttribute::TextColor );
+
+ if ( is->itemSet(KateAttribute::SelectedTextColor) )
+ {
+ if ( is->selectedTextColor() != st->selectedTextColor())
+ st->setSelectedTextColor( is->selectedTextColor() );
+ }
+ else st->clearAttribute( KateAttribute::SelectedTextColor);
+
+ if ( is->itemSet(KateAttribute::BGColor) )
+ {
+ if ( is->bgColor() != st->bgColor())
+ st->setBGColor( is->bgColor() );
+ }
+ else st->clearAttribute( KateAttribute::BGColor );
+
+ if ( is->itemSet(KateAttribute::SelectedBGColor) )
+ {
+ if ( is->selectedBGColor() != st->selectedBGColor())
+ st->setSelectedBGColor( is->selectedBGColor() );
+ }
+ else st->clearAttribute( KateAttribute::SelectedBGColor );
+}
+
+/* only true for a hl mode item using it's default style */
+bool KateStyleListItem::defStyle() { return st && st->itemsSet() != ds->itemsSet(); }
+
+/* true for default styles */
+bool KateStyleListItem::isDefault() { return st ? false : true; }
+
+int KateStyleListItem::width( const QFontMetrics & /*fm*/, const QListView * lv, int col ) const
+{
+ int m = lv->itemMargin() * 2;
+ switch ( col ) {
+ case ContextName:
+ // FIXME: width for name column should reflect bold/italic
+ // (relevant for non-fixed fonts only - nessecary?)
+ return QListViewItem::width( QFontMetrics( ((KateStyleListView*)lv)->docfont), lv, col);
+ case Bold:
+ case Italic:
+ case UseDefStyle:
+ return BoxSize + m;
+ case Color:
+ case SelColor:
+ case BgColor:
+ case SelBgColor:
+ return ColorBtnWidth +m;
+ default:
+ return 0;
+ }
+}
+
+void KateStyleListItem::activate( int column, const QPoint &localPos )
+{
+ QListView *lv = listView();
+ int x = 0;
+ for( int c = 0; c < column-1; c++ )
+ x += lv->columnWidth( c );
+ int w;
+ switch( column ) {
+ case Bold:
+ case Italic:
+ case Underline:
+ case Strikeout:
+ case UseDefStyle:
+ w = BoxSize;
+ break;
+ case Color:
+ case SelColor:
+ case BgColor:
+ case SelBgColor:
+ w = ColorBtnWidth;
+ break;
+ default:
+ return;
+ }
+ if ( !QRect( x, 0, w, BoxSize ).contains( localPos ) )
+ changeProperty( (Property)column );
+}
+
+void KateStyleListItem::changeProperty( Property p )
+{
+ if ( p == Bold )
+ is->setBold( ! is->bold() );
+ else if ( p == Italic )
+ is->setItalic( ! is->italic() );
+ else if ( p == Underline )
+ is->setUnderline( ! is->underline() );
+ else if ( p == Strikeout )
+ is->setStrikeOut( ! is->strikeOut() );
+ else if ( p == UseDefStyle )
+ toggleDefStyle();
+ else
+ setColor( p );
+
+ updateStyle ();
+
+ ((KateStyleListView*)listView())->emitChanged();
+}
+
+void KateStyleListItem::toggleDefStyle()
+{
+ if ( *is == *ds ) {
+ KMessageBox::information( listView(),
+ i18n("\"Use Default Style\" will be automatically unset when you change any style properties."),
+ i18n("Kate Styles"),
+ "Kate hl config use defaults" );
+ }
+ else {
+ delete is;
+ is = new KateAttribute( *ds );
+ updateStyle();
+ repaint();
+ }
+}
+
+void KateStyleListItem::setColor( int column )
+{
+ QColor c; // use this
+ QColor d; // default color
+ if ( column == Color)
+ {
+ c = is->textColor();
+ d = ds->textColor();
+ }
+ else if ( column == SelColor )
+ {
+ c = is->selectedTextColor();
+ d = is->selectedTextColor();
+ }
+ else if ( column == BgColor )
+ {
+ c = is->bgColor();
+ d = ds->bgColor();
+ }
+ else if ( column == SelBgColor )
+ {
+ c = is->selectedBGColor();
+ d = ds->selectedBGColor();
+ }
+
+ if ( KColorDialog::getColor( c, d, listView() ) != QDialog::Accepted) return;
+
+ bool def = ! c.isValid();
+
+ // if set default, and the attrib is set in the default style use it
+ // else if set default, unset it
+ // else set the selected color
+ switch (column)
+ {
+ case Color:
+ if ( def )
+ {
+ if ( ds->itemSet(KateAttribute::TextColor) )
+ is->setTextColor( ds->textColor());
+ else
+ is->clearAttribute(KateAttribute::TextColor);
+ }
+ else
+ is->setTextColor( c );
+ break;
+ case SelColor:
+ if ( def )
+ {
+ if ( ds->itemSet(KateAttribute::SelectedTextColor) )
+ is->setSelectedTextColor( ds->selectedTextColor());
+ else
+ is->clearAttribute(KateAttribute::SelectedTextColor);
+ }
+ else
+ is->setSelectedTextColor( c );
+ break;
+ case BgColor:
+ if ( def )
+ {
+ if ( ds->itemSet(KateAttribute::BGColor) )
+ is->setBGColor( ds->bgColor());
+ else
+ is->clearAttribute(KateAttribute::BGColor);
+ }
+ else
+ is->setBGColor( c );
+ break;
+ case SelBgColor:
+ if ( def )
+ {
+ if ( ds->itemSet(KateAttribute::SelectedBGColor) )
+ is->setSelectedBGColor( ds->selectedBGColor());
+ else
+ is->clearAttribute(KateAttribute::SelectedBGColor);
+ }
+ else
+ is->setSelectedBGColor( c );
+ break;
+ }
+
+ repaint();
+}
+
+void KateStyleListItem::unsetColor( int c )
+{
+ if ( c == 100 && is->itemSet(KateAttribute::BGColor) )
+ is->clearAttribute(KateAttribute::BGColor);
+ else if ( c == 101 && is->itemSet(KateAttribute::SelectedBGColor) )
+ is->clearAttribute(KateAttribute::SelectedBGColor);
+ updateStyle();
+}
+
+void KateStyleListItem::paintCell( QPainter *p, const QColorGroup& /*cg*/, int col, int width, int align )
+{
+
+ if ( !p )
+ return;
+
+ QListView *lv = listView();
+ if ( !lv )
+ return;
+ Q_ASSERT( lv ); //###
+
+ // use a private color group and set the text/highlighted text colors
+ QColorGroup mcg = lv->viewport()->colorGroup();
+
+ if ( col ) // col 0 is drawn by the superclass method
+ p->fillRect( 0, 0, width, height(), QBrush( mcg.base() ) );
+
+ int marg = lv->itemMargin();
+
+ QColor c;
+
+ switch ( col )
+ {
+ case ContextName:
+ {
+ mcg.setColor(QColorGroup::Text, is->textColor());
+ mcg.setColor(QColorGroup::HighlightedText, is->selectedTextColor());
+ // text background color
+ c = is->bgColor();
+ if ( c.isValid() && is->itemSet(KateAttribute::BGColor) )
+ mcg.setColor( QColorGroup::Base, c );
+ if ( isSelected() && is->itemSet(KateAttribute::SelectedBGColor) )
+ {
+ c = is->selectedBGColor();
+ if ( c.isValid() )
+ mcg.setColor( QColorGroup::Highlight, c );
+ }
+ QFont f ( ((KateStyleListView*)lv)->docfont );
+ p->setFont( is->font(f) );
+ // FIXME - repainting when text is cropped, and the column is enlarged is buggy.
+ // Maybe I need painting the string myself :(
+ // (wilbert) it depends on the font used
+ QListViewItem::paintCell( p, mcg, col, width, align );
+ }
+ break;
+ case Bold:
+ case Italic:
+ case Underline:
+ case Strikeout:
+ case UseDefStyle:
+ {
+ // Bold/Italic/use default checkboxes
+ // code allmost identical to QCheckListItem
+ int x = 0;
+ int y = (height() - BoxSize) / 2;
+
+ if ( isEnabled() )
+ p->setPen( QPen( mcg.text(), 2 ) );
+ else
+ p->setPen( QPen( lv->palette().color( QPalette::Disabled, QColorGroup::Text ), 2 ) );
+
+ p->drawRect( x+marg, y+2, BoxSize-4, BoxSize-4 );
+ x++;
+ y++;
+ if ( (col == Bold && is->bold()) ||
+ (col == Italic && is->italic()) ||
+ (col == Underline && is->underline()) ||
+ (col == Strikeout && is->strikeOut()) ||
+ (col == UseDefStyle && *is == *ds ) )
+ {
+ QPointArray a( 7*2 );
+ int i, xx, yy;
+ xx = x+1+marg;
+ yy = y+5;
+ for ( i=0; i<3; i++ ) {
+ a.setPoint( 2*i, xx, yy );
+ a.setPoint( 2*i+1, xx, yy+2 );
+ xx++; yy++;
+ }
+ yy -= 2;
+ for ( i=3; i<7; i++ ) {
+ a.setPoint( 2*i, xx, yy );
+ a.setPoint( 2*i+1, xx, yy+2 );
+ xx++; yy--;
+ }
+ p->drawLineSegments( a );
+ }
+ }
+ break;
+ case Color:
+ case SelColor:
+ case BgColor:
+ case SelBgColor:
+ {
+ bool set( false );
+ if ( col == Color)
+ {
+ c = is->textColor();
+ set = is->itemSet(KateAttribute::TextColor);
+ }
+ else if ( col == SelColor )
+ {
+ c = is->selectedTextColor();
+ set = is->itemSet( KateAttribute::SelectedTextColor);
+ }
+ else if ( col == BgColor )
+ {
+ set = is->itemSet(KateAttribute::BGColor);
+ c = set ? is->bgColor() : mcg.base();
+ }
+ else if ( col == SelBgColor )
+ {
+ set = is->itemSet(KateAttribute::SelectedBGColor);
+ c = set ? is->selectedBGColor(): mcg.base();
+ }
+
+ // color "buttons"
+ int x = 0;
+ int y = (height() - BoxSize) / 2;
+ if ( isEnabled() )
+ p->setPen( QPen( mcg.text(), 2 ) );
+ else
+ p->setPen( QPen( lv->palette().color( QPalette::Disabled, QColorGroup::Text ), 2 ) );
+
+ p->drawRect( x+marg, y+2, ColorBtnWidth-4, BoxSize-4 );
+ p->fillRect( x+marg+1,y+3,ColorBtnWidth-7,BoxSize-7,QBrush( c ) );
+ // if this item is unset, draw a diagonal line over the button
+ if ( ! set )
+ p->drawLine( x+marg-1, BoxSize-3, ColorBtnWidth-4, y+1 );
+ }
+ //case default: // no warning...
+ }
+}
+//END
+
+//BEGIN KateStyleListCaption
+KateStyleListCaption::KateStyleListCaption( QListView *parent, const QString & name )
+ : QListViewItem( parent, name )
+{
+}
+
+void KateStyleListCaption::paintCell( QPainter *p, const QColorGroup& /*cg*/, int col, int width, int align )
+{
+ QListView *lv = listView();
+ if ( !lv )
+ return;
+ Q_ASSERT( lv ); //###
+
+ // use the same colorgroup as the other items in the viewport
+ QColorGroup mcg = lv->viewport()->colorGroup();
+
+ QListViewItem::paintCell( p, mcg, col, width, align );
+}
+//END
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/kateschema.h b/kate/part/kateschema.h
new file mode 100644
index 000000000..917530fcc
--- /dev/null
+++ b/kate/part/kateschema.h
@@ -0,0 +1,323 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2001-2003 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2002, 2003 Anders Lund <anders.lund@lund.tdcadsl.dk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KATE_SCHEMA_H__
+#define __KATE_SCHEMA_H__
+
+#include "katehighlight.h"
+#include "katedialogs.h"
+
+#include <qstringlist.h>
+#include <qintdict.h>
+#include <qmap.h>
+#include <qlistview.h>
+#include <qfont.h>
+
+#include <kconfig.h>
+#include <kaction.h>
+
+class KateView;
+class KateStyleListItem;
+class KateStyleListCaption;
+
+class KColorButton;
+
+class QPopupMenu;
+class KComboBox;
+
+class KateSchemaManager
+{
+ public:
+ KateSchemaManager ();
+ ~KateSchemaManager ();
+
+ /**
+ * Schema Config changed, update all renderers
+ */
+ void update (bool readfromfile = true);
+
+ /**
+ * return kconfig with right group set or set to Normal if not there
+ */
+ KConfig *schema (uint number);
+
+ void addSchema (const QString &t);
+
+ void removeSchema (uint number);
+
+ /**
+ * is this schema valid ? (does it exist ?)
+ */
+ bool validSchema (uint number);
+
+ /**
+ * if not found, defaults to 0
+ */
+ uint number (const QString &name);
+
+ /**
+ * group names in the end, no i18n involved
+ */
+ QString name (uint number);
+
+ /**
+ * Don't modify, list with the names of the schemas (i18n name for the default ones)
+ */
+ const QStringList &list () { return m_schemas; }
+
+ static QString normalSchema ();
+ static QString printingSchema ();
+
+ private:
+ KConfig m_config;
+ QStringList m_schemas;
+};
+
+
+class KateViewSchemaAction : public KActionMenu
+{
+ Q_OBJECT
+
+ public:
+ KateViewSchemaAction(const QString& text, QObject* parent = 0, const char* name = 0)
+ : KActionMenu(text, parent, name) { init(); };
+
+ ~KateViewSchemaAction(){;};
+
+ void updateMenu (KateView *view);
+
+ private:
+ void init();
+
+ QGuardedPtr<KateView> m_view;
+ QStringList names;
+ int last;
+
+ public slots:
+ void slotAboutToShow();
+
+ private slots:
+ void setSchema (int mode);
+};
+
+//
+// DIALOGS
+//
+
+/*
+ QListView that automatically adds columns for KateStyleListItems and provides a
+ popup menu and a slot to edit a style using the keyboard.
+ Added by anders, jan 23 2002.
+*/
+class KateStyleListView : public QListView
+{
+ Q_OBJECT
+
+ friend class KateStyleListItem;
+
+ public:
+ KateStyleListView( QWidget *parent=0, bool showUseDefaults=false);
+ ~KateStyleListView() {};
+ /* Display a popupmenu for item i at the specified global position, eventually with a title,
+ promoting the context name of that item */
+ void showPopupMenu( KateStyleListItem *i, const QPoint &globalPos, bool showtitle=false );
+ void emitChanged() { emit changed(); };
+
+ void setBgCol( const QColor &c ) { bgcol = c; }
+ void setSelCol( const QColor &c ) { selcol = c; }
+ void setNormalCol( const QColor &c ) { normalcol = c; }
+
+ private slots:
+ /* Display a popupmenu for item i at item position */
+ void showPopupMenu( QListViewItem *i, const QPoint &globalPos );
+ /* call item to change a property, or display a menu */
+ void slotMousePressed( int, QListViewItem*, const QPoint&, int );
+ /* asks item to change the property in q */
+ void mSlotPopupHandler( int z );
+ void unsetColor( int );
+
+ signals:
+ void changed();
+
+ private:
+ QColor bgcol, selcol, normalcol;
+ QFont docfont;
+};
+
+class KateSchemaConfigColorTab : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ KateSchemaConfigColorTab( QWidget *parent = 0, const char *name = 0 );
+ ~KateSchemaConfigColorTab();
+
+ private:
+ KColorButton *m_back;
+ KColorButton *m_selected;
+ KColorButton *m_current;
+ KColorButton *m_bracket;
+ KColorButton *m_wwmarker;
+ KColorButton *m_iconborder;
+ KColorButton *m_tmarker;
+ KColorButton *m_linenumber;
+
+ KColorButton *m_markers; // bg color for current selected marker
+ KComboBox* m_combobox; // switch marker type
+
+ // Class for storing the properties on 1 schema.
+ class SchemaColors {
+ public:
+ QColor back, selected, current, bracket, wwmarker, iconborder, tmarker, linenumber;
+ QMap<int, QColor> markerColors; // stores all markerColors
+ };
+
+ // schemaid=data, created when a schema is entered
+ QMap<int,SchemaColors> m_schemas;
+ // current schema
+ int m_schema;
+
+ public slots:
+ void apply();
+ void schemaChanged( int newSchema );
+
+ signals:
+ void changed(); // connected to parentWidget()->parentWidget() SLOT(slotChanged)
+
+ protected slots:
+ void slotMarkerColorChanged(const QColor&);
+ void slotComboBoxChanged(int index);
+};
+
+typedef QMap<int,QFont> FontMap; // ### remove it
+
+class KateSchemaConfigFontTab : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ KateSchemaConfigFontTab( QWidget *parent = 0, const char *name = 0 );
+ ~KateSchemaConfigFontTab();
+
+ public:
+ void readConfig (KConfig *config);
+
+ public slots:
+ void apply();
+ void schemaChanged( int newSchema );
+
+ signals:
+ void changed(); // connected to parentWidget()->parentWidget() SLOT(slotChanged)
+
+ private:
+ class KFontChooser *m_fontchooser;
+ FontMap m_fonts;
+ int m_schema;
+
+ private slots:
+ void slotFontSelected( const QFont &font );
+};
+
+class KateSchemaConfigFontColorTab : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ KateSchemaConfigFontColorTab( QWidget *parent = 0, const char *name = 0 );
+ ~KateSchemaConfigFontColorTab();
+
+ public:
+ void schemaChanged (uint schema);
+ void reload ();
+ void apply ();
+
+ KateAttributeList *attributeList (uint schema);
+
+ private:
+ KateStyleListView *m_defaultStyles;
+ QIntDict<KateAttributeList> m_defaultStyleLists;
+};
+
+class KateSchemaConfigHighlightTab : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ KateSchemaConfigHighlightTab( QWidget *parent = 0, const char *name = 0, KateSchemaConfigFontColorTab *page = 0, uint hl = 0 );
+ ~KateSchemaConfigHighlightTab();
+
+ public:
+ void schemaChanged (uint schema);
+ void reload ();
+ void apply ();
+
+ protected slots:
+ void hlChanged(int z);
+
+ private:
+ KateSchemaConfigFontColorTab *m_defaults;
+
+ QComboBox *hlCombo;
+ KateStyleListView *m_styles;
+
+ uint m_schema;
+ int m_hl;
+
+ QIntDict< QIntDict<KateHlItemDataList> > m_hlDict;
+};
+
+class KateSchemaConfigPage : public KateConfigPage
+{
+ Q_OBJECT
+
+ public:
+ KateSchemaConfigPage ( QWidget *parent, class KateDocument *doc=0 );
+ ~KateSchemaConfigPage ();
+
+ public slots:
+ void apply();
+ void reload();
+ void reset();
+ void defaults();
+
+ private slots:
+ void update ();
+ void deleteSchema ();
+ void newSchema ();
+ void schemaChanged (int schema);
+
+ void newCurrentPage (QWidget *w);
+
+ private:
+ int m_lastSchema;
+ int m_defaultSchema;
+
+ class QTabWidget *m_tabWidget;
+ class QPushButton *btndel;
+ class QComboBox *defaultSchemaCombo;
+ class QComboBox *schemaCombo;
+ KateSchemaConfigColorTab *m_colorTab;
+ KateSchemaConfigFontTab *m_fontTab;
+ KateSchemaConfigFontColorTab *m_fontColorTab;
+ KateSchemaConfigHighlightTab *m_highlightTab;
+};
+
+#endif
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katesearch.cpp b/kate/part/katesearch.cpp
new file mode 100644
index 000000000..8f4911137
--- /dev/null
+++ b/kate/part/katesearch.cpp
@@ -0,0 +1,1012 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2004-2005 Anders Lund <anders@alweb.dk>
+ Copyright (C) 2003 Clarence Dang <dang@kde.org>
+ Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org>
+ Copyright (C) 2001-2004 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
+ Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "katesearch.h"
+#include "katesearch.moc"
+
+#include "kateview.h"
+#include "katedocument.h"
+#include "katesupercursor.h"
+#include "katearbitraryhighlight.h"
+#include "kateconfig.h"
+#include "katehighlight.h"
+
+#include <klocale.h>
+#include <kstdaction.h>
+#include <kmessagebox.h>
+#include <kstringhandler.h>
+#include <kdebug.h>
+#include <kfinddialog.h>
+#include <kreplacedialog.h>
+#include <kpushbutton.h>
+
+#include <qlayout.h>
+#include <qlabel.h>
+
+//BEGIN KateSearch
+QStringList KateSearch::s_searchList = QStringList();
+QStringList KateSearch::s_replaceList = QStringList();
+QString KateSearch::s_pattern = QString();
+static const bool arbitraryHLExample = false;
+
+KateSearch::KateSearch( KateView* view )
+ : QObject( view, "kate search" )
+ , m_view( view )
+ , m_doc( view->doc() )
+ , replacePrompt( new KateReplacePrompt( view ) )
+{
+ m_arbitraryHLList = new KateSuperRangeList();
+ if (arbitraryHLExample) m_doc->arbitraryHL()->addHighlightToView(m_arbitraryHLList, m_view);
+
+ connect(replacePrompt,SIGNAL(clicked()),this,SLOT(replaceSlot()));
+}
+
+KateSearch::~KateSearch()
+{
+ delete m_arbitraryHLList;
+}
+
+void KateSearch::createActions( KActionCollection* ac )
+{
+ KStdAction::find( this, SLOT(find()), ac )->setWhatsThis(
+ i18n("Look up the first occurrence of a piece of text or regular expression."));
+ KStdAction::findNext( this, SLOT(slotFindNext()), ac )->setWhatsThis(
+ i18n("Look up the next occurrence of the search phrase."));
+ KStdAction::findPrev( this, SLOT(slotFindPrev()), ac, "edit_find_prev" )->setWhatsThis(
+ i18n("Look up the previous occurrence of the search phrase."));
+ KStdAction::replace( this, SLOT(replace()), ac )->setWhatsThis(
+ i18n("Look up a piece of text or regular expression and replace the result with some given text."));
+}
+
+void KateSearch::addToList( QStringList& list, const QString& s )
+{
+ if( list.count() > 0 ) {
+ QStringList::Iterator it = list.find( s );
+ if( *it != 0L )
+ list.remove( it );
+ if( list.count() >= 16 )
+ list.remove( list.fromLast() );
+ }
+ list.prepend( s );
+}
+
+void KateSearch::find()
+{
+ // if multiline selection around, search in it
+ long searchf = KateViewConfig::global()->searchFlags();
+ if (m_view->hasSelection() && m_view->selStartLine() != m_view->selEndLine())
+ searchf |= KFindDialog::SelectedText;
+
+ KFindDialog *findDialog = new KFindDialog ( m_view, "", searchf,
+ s_searchList, m_view->hasSelection() );
+
+ findDialog->setPattern (getSearchText());
+
+
+ if( findDialog->exec() == QDialog::Accepted ) {
+ s_searchList = findDialog->findHistory () ;
+ // Do *not* remove the QString() wrapping, it fixes a nasty crash
+ find( QString(s_searchList.first()), findDialog->options(), true, true );
+ }
+
+ delete findDialog;
+ m_view->repaintText ();
+}
+
+void KateSearch::find( const QString &pattern, long flags, bool add, bool shownotfound )
+{
+ KateViewConfig::global()->setSearchFlags( flags );
+ if( add )
+ addToList( s_searchList, pattern );
+
+ s_pattern = pattern;
+
+ SearchFlags searchFlags;
+
+ searchFlags.caseSensitive = KateViewConfig::global()->searchFlags() & KFindDialog::CaseSensitive;
+ searchFlags.wholeWords = KateViewConfig::global()->searchFlags() & KFindDialog::WholeWordsOnly;
+ searchFlags.fromBeginning = !(KateViewConfig::global()->searchFlags() & KFindDialog::FromCursor)
+ && !(KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText);
+ searchFlags.backward = KateViewConfig::global()->searchFlags() & KFindDialog::FindBackwards;
+ searchFlags.selected = KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText;
+ searchFlags.prompt = false;
+ searchFlags.replace = false;
+ searchFlags.finished = false;
+ searchFlags.regExp = KateViewConfig::global()->searchFlags() & KFindDialog::RegularExpression;
+ searchFlags.useBackRefs = KateViewConfig::global()->searchFlags() & KReplaceDialog::BackReference;
+
+ if ( searchFlags.selected )
+ {
+ s.selBegin = KateTextCursor( m_view->selStartLine(), m_view->selStartCol() );
+ s.selEnd = KateTextCursor( m_view->selEndLine(), m_view->selEndCol() );
+ s.cursor = s.flags.backward ? s.selEnd : s.selBegin;
+ } else {
+ s.cursor = getCursor( searchFlags );
+ }
+
+ s.wrappedEnd = s.cursor;
+ s.wrapped = false;
+ s.showNotFound = shownotfound;
+
+ search( searchFlags );
+}
+
+void KateSearch::replace()
+{
+ if (!doc()->isReadWrite()) return;
+
+ // if multiline selection around, search in it
+ long searchf = KateViewConfig::global()->searchFlags();
+ if (m_view->hasSelection() && m_view->selStartLine() != m_view->selEndLine())
+ searchf |= KFindDialog::SelectedText;
+
+ KReplaceDialog *replaceDialog = new KReplaceDialog ( m_view, "", searchf,
+ s_searchList, s_replaceList, m_view->hasSelection() );
+
+ replaceDialog->setPattern (getSearchText());
+
+ if( replaceDialog->exec() == QDialog::Accepted ) {
+ long opts = replaceDialog->options();
+ m_replacement = replaceDialog->replacement();
+ s_searchList = replaceDialog->findHistory () ;
+ s_replaceList = replaceDialog->replacementHistory () ;
+
+ // Do *not* remove the QString() wrapping, it fixes a nasty crash
+ replace( QString(s_searchList.first()), m_replacement, opts );
+ }
+
+ delete replaceDialog;
+ m_view->update ();
+}
+
+void KateSearch::replace( const QString& pattern, const QString &replacement, long flags )
+{
+ if (!doc()->isReadWrite()) return;
+
+ addToList( s_searchList, pattern );
+ s_pattern = pattern;
+ addToList( s_replaceList, replacement );
+ m_replacement = replacement;
+ KateViewConfig::global()->setSearchFlags( flags );
+
+ SearchFlags searchFlags;
+ searchFlags.caseSensitive = KateViewConfig::global()->searchFlags() & KFindDialog::CaseSensitive;
+ searchFlags.wholeWords = KateViewConfig::global()->searchFlags() & KFindDialog::WholeWordsOnly;
+ searchFlags.fromBeginning = !(KateViewConfig::global()->searchFlags() & KFindDialog::FromCursor)
+ && !(KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText);
+ searchFlags.backward = KateViewConfig::global()->searchFlags() & KFindDialog::FindBackwards;
+ searchFlags.selected = KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText;
+ searchFlags.prompt = KateViewConfig::global()->searchFlags() & KReplaceDialog::PromptOnReplace;
+ searchFlags.replace = true;
+ searchFlags.finished = false;
+ searchFlags.regExp = KateViewConfig::global()->searchFlags() & KFindDialog::RegularExpression;
+ searchFlags.useBackRefs = KateViewConfig::global()->searchFlags() & KReplaceDialog::BackReference;
+ if ( searchFlags.selected )
+ {
+ s.selBegin = KateTextCursor( m_view->selStartLine(), m_view->selStartCol() );
+ s.selEnd = KateTextCursor( m_view->selEndLine(), m_view->selEndCol() );
+ s.cursor = s.flags.backward ? s.selEnd : s.selBegin;
+ } else {
+ s.cursor = getCursor( searchFlags );
+ }
+
+ s.wrappedEnd = s.cursor;
+ s.wrapped = false;
+
+ search( searchFlags );
+}
+
+void KateSearch::findAgain( bool reverseDirection )
+{
+ SearchFlags searchFlags;
+ searchFlags.caseSensitive = KateViewConfig::global()->searchFlags() & KFindDialog::CaseSensitive;
+ searchFlags.wholeWords = KateViewConfig::global()->searchFlags() & KFindDialog::WholeWordsOnly;
+ searchFlags.fromBeginning = !(KateViewConfig::global()->searchFlags() & KFindDialog::FromCursor)
+ && !(KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText);
+ searchFlags.backward = KateViewConfig::global()->searchFlags() & KFindDialog::FindBackwards;
+ searchFlags.selected = KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText;
+ searchFlags.prompt = KateViewConfig::global()->searchFlags() & KReplaceDialog::PromptOnReplace;
+ searchFlags.replace = false;
+ searchFlags.finished = false;
+ searchFlags.regExp = KateViewConfig::global()->searchFlags() & KFindDialog::RegularExpression;
+ searchFlags.useBackRefs = KateViewConfig::global()->searchFlags() & KReplaceDialog::BackReference;
+
+ if (reverseDirection)
+ searchFlags.backward = !searchFlags.backward;
+
+ searchFlags.fromBeginning = false;
+ searchFlags.prompt = true; // ### why is the above assignment there?
+
+ s.cursor = getCursor( searchFlags );
+ search( searchFlags );
+}
+
+void KateSearch::search( SearchFlags flags )
+{
+ s.flags = flags;
+
+ if( s.flags.fromBeginning ) {
+ if( !s.flags.backward ) {
+ s.cursor.setPos(0, 0);
+ } else {
+ s.cursor.setLine(doc()->numLines() - 1);
+ s.cursor.setCol(doc()->lineLength( s.cursor.line() ));
+ }
+ }
+
+ if((!s.flags.backward &&
+ s.cursor.col() == 0 &&
+ s.cursor.line() == 0 ) ||
+ ( s.flags.backward &&
+ s.cursor.col() == doc()->lineLength( s.cursor.line() ) &&
+ s.cursor.line() == (((int)doc()->numLines()) - 1) ) ) {
+ s.flags.finished = true;
+ }
+
+ if( s.flags.replace ) {
+ replaces = 0;
+ if( s.flags.prompt )
+ promptReplace();
+ else
+ replaceAll();
+ } else {
+ findAgain();
+ }
+}
+
+void KateSearch::wrapSearch()
+{
+ if( s.flags.selected )
+ {
+ KateTextCursor start (s.selBegin);
+ KateTextCursor end (s.selEnd);
+
+ // recalc for block sel, to have start with lowest col, end with highest
+ if (m_view->blockSelectionMode())
+ {
+ start.setCol (kMin(s.selBegin.col(), s.selEnd.col()));
+ end.setCol (kMax(s.selBegin.col(), s.selEnd.col()));
+ }
+
+ s.cursor = s.flags.backward ? end : start;
+ }
+ else
+ {
+ if( !s.flags.backward ) {
+ s.cursor.setPos(0, 0);
+ } else {
+ s.cursor.setLine(doc()->numLines() - 1);
+ s.cursor.setCol(doc()->lineLength( s.cursor.line() ) );
+ }
+ }
+
+ // oh, we wrapped around one time allready now !
+ // only check that on replace
+ s.wrapped = s.flags.replace;
+
+ replaces = 0;
+ s.flags.finished = true;
+}
+
+void KateSearch::findAgain()
+{
+ if( s_pattern.isEmpty() ) {
+ find();
+ return;
+ }
+
+ if ( doSearch( s_pattern ) ) {
+ exposeFound( s.cursor, s.matchedLength );
+ } else if( !s.flags.finished ) {
+ if( askContinue() ) {
+ wrapSearch();
+ findAgain();
+ } else {
+ if (arbitraryHLExample) m_arbitraryHLList->clear();
+ }
+ } else {
+ if (arbitraryHLExample) m_arbitraryHLList->clear();
+ if ( s.showNotFound )
+ KMessageBox::sorry( view(),
+ i18n("Search string '%1' not found!")
+ .arg( KStringHandler::csqueeze( s_pattern ) ),
+ i18n("Find"));
+ }
+}
+
+void KateSearch::replaceAll()
+{
+ doc()->editStart ();
+
+ while( doSearch( s_pattern ) )
+ replaceOne();
+
+ doc()->editEnd ();
+
+ if( !s.flags.finished ) {
+ if( askContinue() ) {
+ wrapSearch();
+ replaceAll();
+ }
+ } else {
+ KMessageBox::information( view(),
+ i18n("%n replacement made.","%n replacements made.",replaces),
+ i18n("Replace") );
+ }
+}
+
+void KateSearch::promptReplace()
+{
+ if ( doSearch( s_pattern ) ) {
+ exposeFound( s.cursor, s.matchedLength );
+ replacePrompt->show();
+ replacePrompt->setFocus ();
+ } else if( !s.flags.finished && askContinue() ) {
+ wrapSearch();
+ promptReplace();
+ } else {
+ if (arbitraryHLExample) m_arbitraryHLList->clear();
+ replacePrompt->hide();
+ KMessageBox::information( view(),
+ i18n("%n replacement made.","%n replacements made.",replaces),
+ i18n("Replace") );
+ }
+}
+
+void KateSearch::replaceOne()
+{
+ QString replaceWith = m_replacement;
+ if ( s.flags.regExp && s.flags.useBackRefs ) {
+ // replace each "(?!\)\d+" with the corresponding capture
+ QRegExp br("\\\\(\\d+)");
+ int pos = br.search( replaceWith );
+ int ncaps = m_re.numCaptures();
+ while ( pos >= 0 ) {
+ QString sc;
+ if ( !pos || replaceWith.at( pos-1) != '\\' ) {
+ int ccap = br.cap(1).toInt();
+ if (ccap <= ncaps ) {
+ sc = m_re.cap( ccap );
+ replaceWith.replace( pos, br.matchedLength(), sc );
+ }
+ else {
+ kdDebug()<<"KateSearch::replaceOne(): you don't have "<<ccap<<" backreferences in regexp '"<<m_re.pattern()<<"'"<<endl;
+ }
+ }
+ pos = br.search( replaceWith, pos + (int)sc.length() );
+ }
+ }
+
+ doc()->editStart();
+ doc()->removeText( s.cursor.line(), s.cursor.col(),
+ s.cursor.line(), s.cursor.col() + s.matchedLength );
+ doc()->insertText( s.cursor.line(), s.cursor.col(), replaceWith );
+ doc()->editEnd(),
+
+ replaces++;
+
+ // if we inserted newlines, we better adjust.
+ uint newlines = replaceWith.contains('\n');
+ if ( newlines )
+ {
+ if ( ! s.flags.backward )
+ {
+ s.cursor.setLine( s.cursor.line() + newlines );
+ s.cursor.setCol( replaceWith.length() - replaceWith.findRev('\n') );
+ }
+ // selection?
+ if ( s.flags.selected )
+ s.selEnd.setLine( s.selEnd.line() + newlines );
+ }
+
+
+ // adjust selection endcursor if needed
+ if( s.flags.selected && s.cursor.line() == s.selEnd.line() )
+ {
+ s.selEnd.setCol(s.selEnd.col() + replaceWith.length() - s.matchedLength );
+ }
+
+ // adjust wrap cursor if needed
+ if( s.cursor.line() == s.wrappedEnd.line() && s.cursor.col() <= s.wrappedEnd.col())
+ {
+ s.wrappedEnd.setCol(s.wrappedEnd.col() + replaceWith.length() - s.matchedLength );
+ }
+
+ if( !s.flags.backward ) {
+ s.cursor.setCol(s.cursor.col() + replaceWith.length());
+ } else if( s.cursor.col() > 0 ) {
+ s.cursor.setCol(s.cursor.col() - 1);
+ } else {
+ s.cursor.setLine(s.cursor.line() - 1);
+ if( s.cursor.line() >= 0 ) {
+ s.cursor.setCol(doc()->lineLength( s.cursor.line() ));
+ }
+ }
+}
+
+void KateSearch::skipOne()
+{
+ if( !s.flags.backward ) {
+ s.cursor.setCol(s.cursor.col() + s.matchedLength);
+ } else if( s.cursor.col() > 0 ) {
+ s.cursor.setCol(s.cursor.col() - 1);
+ } else {
+ s.cursor.setLine(s.cursor.line() - 1);
+ if( s.cursor.line() >= 0 ) {
+ s.cursor.setCol(doc()->lineLength(s.cursor.line()));
+ }
+ }
+}
+
+void KateSearch::replaceSlot() {
+ switch( (Dialog_results)replacePrompt->result() ) {
+ case srCancel: replacePrompt->hide(); break;
+ case srAll: replacePrompt->hide(); replaceAll(); break;
+ case srYes: replaceOne(); promptReplace(); break;
+ case srLast: replacePrompt->hide(), replaceOne(); break;
+ case srNo: skipOne(); promptReplace(); break;
+ }
+}
+
+bool KateSearch::askContinue()
+{
+ QString made =
+ i18n( "%n replacement made.",
+ "%n replacements made.",
+ replaces );
+
+ QString reached = !s.flags.backward ?
+ i18n( "End of document reached." ) :
+ i18n( "Beginning of document reached." );
+
+ if (KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText)
+ {
+ reached = !s.flags.backward ?
+ i18n( "End of selection reached." ) :
+ i18n( "Beginning of selection reached." );
+ }
+
+ QString question = !s.flags.backward ?
+ i18n( "Continue from the beginning?" ) :
+ i18n( "Continue from the end?" );
+
+ QString text = s.flags.replace ?
+ made + "\n" + reached + "\n" + question :
+ reached + "\n" + question;
+
+ return KMessageBox::Yes == KMessageBox::questionYesNo(
+ view(), text, s.flags.replace ? i18n("Replace") : i18n("Find"),
+ KStdGuiItem::cont(), i18n("&Stop") );
+}
+
+QString KateSearch::getSearchText()
+{
+ // SelectionOnly: use selection
+ // WordOnly: use word under cursor
+ // SelectionWord: use selection if available, else use word under cursor
+ // WordSelection: use word if available, else use selection
+ QString str;
+
+ int getFrom = view()->config()->textToSearchMode();
+ switch (getFrom)
+ {
+ case KateViewConfig::SelectionOnly: // (Windows)
+ //kdDebug() << "getSearchText(): SelectionOnly" << endl;
+ if( m_view->hasSelection() )
+ str = m_view->selection();
+ break;
+
+ case KateViewConfig::SelectionWord: // (classic Kate behavior)
+ //kdDebug() << "getSearchText(): SelectionWord" << endl;
+ if( m_view->hasSelection() )
+ str = m_view->selection();
+ else
+ str = view()->currentWord();
+ break;
+
+ case KateViewConfig::WordOnly: // (weird?)
+ //kdDebug() << "getSearchText(): WordOnly" << endl;
+ str = view()->currentWord();
+ break;
+
+ case KateViewConfig::WordSelection: // (persistent selection lover)
+ //kdDebug() << "getSearchText(): WordSelection" << endl;
+ str = view()->currentWord();
+ if (str.isEmpty() && m_view->hasSelection() )
+ str = m_view->selection();
+ break;
+
+ default: // (nowhere)
+ //kdDebug() << "getSearchText(): Nowhere" << endl;
+ break;
+ }
+
+ str.replace( QRegExp("^\\n"), "" );
+ str.replace( QRegExp("\\n.*"), "" );
+
+ return str;
+}
+
+KateTextCursor KateSearch::getCursor( SearchFlags flags )
+{
+ if (flags.backward && !flags.selected && view()->hasSelection())
+ {
+ // We're heading backwards (and not within a selection),
+ // the selection might start before the cursor.
+ return kMin( KateTextCursor(view()->selStartLine(), view()->selStartCol()),
+ KateTextCursor(view()->cursorLine(), view()->cursorColumnReal()));
+ }
+ return KateTextCursor(view()->cursorLine(), view()->cursorColumnReal());
+}
+
+bool KateSearch::doSearch( const QString& text )
+{
+/*
+ rodda: Still Working on this... :)
+
+ bool result = false;
+
+ if (m_searchResults.count()) {
+ m_resultIndex++;
+ if (m_resultIndex < (int)m_searchResults.count()) {
+ s = m_searchResults[m_resultIndex];
+ result = true;
+ }
+
+ } else {
+ int temp = 0;
+ do {*/
+
+#if 0
+ static int oldLine = -1;
+ static int oldCol = -1;
+#endif
+
+ uint line = s.cursor.line();
+ uint col = s.cursor.col();// + (result ? s.matchedLength : 0);
+ bool backward = s.flags.backward;
+ bool caseSensitive = s.flags.caseSensitive;
+ bool regExp = s.flags.regExp;
+ bool wholeWords = s.flags.wholeWords;
+ uint foundLine, foundCol, matchLen;
+ bool found = false;
+ //kdDebug() << "Searching at " << line << ", " << col << endl;
+// kdDebug()<<"KateSearch::doSearch: "<<line<<", "<<col<<", "<<backward<<endl;
+
+ if (backward)
+ {
+ KateDocCursor docCursor(line, col, doc());
+
+ // If we're at the top of the document, we're not gonna find anything, so bail.
+ if (docCursor.line() == 0 && docCursor.col() == 0)
+ return false;
+
+ // Move one step backward before searching, if this is a "find again", we don't
+ // want to find the same match.
+ docCursor.moveBackward(1);
+ line = docCursor.line();
+ col = docCursor.col();
+ }
+
+ do {
+ if( regExp ) {
+ m_re = QRegExp( text, caseSensitive );
+ found = doc()->searchText( line, col, m_re,
+ &foundLine, &foundCol,
+ &matchLen, backward );
+ }
+ else if ( wholeWords )
+ {
+ bool maybefound = false;
+ do
+ {
+ maybefound = doc()->searchText( line, col, text,
+ &foundLine, &foundCol,
+ &matchLen, caseSensitive, backward );
+ if ( maybefound )
+ {
+ found = (
+ ( foundCol == 0 ||
+ ! doc()->highlight()->isInWord( doc()->textLine( foundLine ).at( foundCol - 1 ) ) ) &&
+ ( foundCol + matchLen == doc()->lineLength( foundLine ) ||
+ ! doc()->highlight()->isInWord( doc()->textLine( foundLine ).at( foundCol + matchLen ) ) )
+ );
+ if ( found )
+ {
+ break;
+ }
+ else if ( backward && foundCol == 0 ) // we are done on this line and want to avoid endless loops like in #137312
+ {
+ if ( line == 0 ) // we are completely done...
+ break;
+ else
+ line--;
+ }
+ else
+ {
+ line = foundLine;
+ col = foundCol + 1;
+ }
+ }
+ } while ( maybefound );
+ }
+ else {
+ found = doc()->searchText( line, col, text,
+ &foundLine, &foundCol,
+ &matchLen, caseSensitive, backward );
+ }
+
+ if ( found && s.flags.selected )
+ {
+ KateTextCursor start (s.selBegin);
+ KateTextCursor end (s.selEnd);
+
+ // recalc for block sel, to have start with lowest col, end with highest
+ if (m_view->blockSelectionMode())
+ {
+ start.setCol (kMin(s.selBegin.col(), s.selEnd.col()));
+ end.setCol (kMax(s.selBegin.col(), s.selEnd.col()));
+ }
+
+ if ( !s.flags.backward && KateTextCursor( foundLine, foundCol ) >= end
+ || s.flags.backward && KateTextCursor( foundLine, foundCol ) < start )
+ {
+ found = false;
+ }
+ else if (m_view->blockSelectionMode())
+ {
+ if ((int)foundCol >= start.col() && (int)foundCol < end.col())
+ break;
+ }
+ }
+
+ line = foundLine;
+ col = foundCol+1;
+ }
+ while (s.flags.selected && m_view->blockSelectionMode() && found);
+ // in the case we want to search in selection + blockselection we need to loop
+
+ if( !found ) return false;
+
+ // save the search result
+ s.cursor.setPos(foundLine, foundCol);
+ s.matchedLength = matchLen;
+
+ // we allready wrapped around one time
+ if (s.wrapped)
+ {
+ if (s.flags.backward)
+ {
+ if ( (s.cursor.line() < s.wrappedEnd.line())
+ || ( (s.cursor.line() == s.wrappedEnd.line()) && ((s.cursor.col()+matchLen) <= uint(s.wrappedEnd.col())) ) )
+ return false;
+ }
+ else
+ {
+ if ( (s.cursor.line() > s.wrappedEnd.line())
+ || ( (s.cursor.line() == s.wrappedEnd.line()) && (s.cursor.col() > s.wrappedEnd.col()) ) )
+ return false;
+ }
+ }
+
+// kdDebug() << "Found at " << s.cursor.line() << ", " << s.cursor.col() << endl;
+
+
+ //m_searchResults.append(s);
+
+ if (arbitraryHLExample) {
+ KateArbitraryHighlightRange* hl = new KateArbitraryHighlightRange(new KateSuperCursor(m_doc, true, s.cursor), new KateSuperCursor(m_doc, true, s.cursor.line(), s.cursor.col() + s.matchedLength), this);
+ hl->setBold();
+ hl->setTextColor(Qt::white);
+ hl->setBGColor(Qt::black);
+ // destroy the highlight upon change
+ connect(hl, SIGNAL(contentsChanged()), hl, SIGNAL(eliminated()));
+ m_arbitraryHLList->append(hl);
+ }
+
+ return true;
+
+ /* rodda: more of my search highlighting work
+
+ } while (++temp < 100);
+
+ if (result) {
+ s = m_searchResults.first();
+ m_resultIndex = 0;
+ }
+ }
+
+ return result;*/
+}
+
+void KateSearch::exposeFound( KateTextCursor &cursor, int slen )
+{
+ view()->setCursorPositionInternal ( cursor.line(), cursor.col() + slen, 1 );
+ view()->setSelection( cursor.line(), cursor.col(), cursor.line(), cursor.col() + slen );
+ view()->syncSelectionCache();
+}
+//END KateSearch
+
+//BEGIN KateReplacePrompt
+// this dialog is not modal
+KateReplacePrompt::KateReplacePrompt ( QWidget *parent )
+ : KDialogBase ( parent, 0L, false, i18n( "Replace Confirmation" ),
+ User3 | User2 | User1 | Close | Ok , Ok, true,
+ i18n("Replace &All"), i18n("Re&place && Close"), i18n("&Replace") )
+{
+ setButtonOK( i18n("&Find Next") );
+ QWidget *page = new QWidget(this);
+ setMainWidget(page);
+
+ QBoxLayout *topLayout = new QVBoxLayout( page, 0, spacingHint() );
+ QLabel *label = new QLabel(i18n("Found an occurrence of your search term. What do you want to do?"),page);
+ topLayout->addWidget(label );
+}
+
+void KateReplacePrompt::slotOk ()
+{ // Search Next
+ done(KateSearch::srNo);
+ actionButton(Ok)->setFocus();
+}
+
+void KateReplacePrompt::slotClose ()
+{ // Close
+ done(KateSearch::srCancel);
+ actionButton(Close)->setFocus();
+}
+
+void KateReplacePrompt::slotUser1 ()
+{ // Replace All
+ done(KateSearch::srAll);
+ actionButton(User1)->setFocus();
+}
+
+void KateReplacePrompt::slotUser2 ()
+{ // Replace & Close
+ done(KateSearch::srLast);
+ actionButton(User2)->setFocus();
+}
+
+void KateReplacePrompt::slotUser3 ()
+{ // Replace
+ done(KateSearch::srYes);
+ actionButton(User3)->setFocus();
+}
+
+void KateReplacePrompt::done (int result)
+{
+ setResult(result);
+
+ emit clicked();
+}
+//END KateReplacePrompt
+
+//BEGIN SearchCommand
+bool SearchCommand::exec(class Kate::View *view, const QString &cmd, QString &msg)
+{
+ QString flags, pattern, replacement;
+ if ( cmd.startsWith( "find" ) )
+ {
+
+ static QRegExp re_find("find(?::([bcersw]*))?\\s+(.+)");
+ if ( re_find.search( cmd ) < 0 )
+ {
+ msg = i18n("Usage: find[:[bcersw]] PATTERN");
+ return false;
+ }
+ flags = re_find.cap( 1 );
+ pattern = re_find.cap( 2 );
+ }
+
+ else if ( cmd.startsWith( "ifind" ) )
+ {
+ static QRegExp re_ifind("ifind(?::([bcrs]*))?\\s+(.*)");
+ if ( re_ifind.search( cmd ) < 0 )
+ {
+ msg = i18n("Usage: ifind[:[bcrs]] PATTERN");
+ return false;
+ }
+ ifindClear();
+ return true;
+ }
+
+ else if ( cmd.startsWith( "replace" ) )
+ {
+ // Try if the pattern and replacement is quoted, using a quote character ["']
+ static QRegExp re_rep("replace(?::([bceprsw]*))?\\s+([\"'])((?:[^\\\\\\\\2]|\\\\.)*)\\2\\s+\\2((?:[^\\\\\\\\2]|\\\\.)*)\\2\\s*$");
+ // Or one quoted argument
+ QRegExp re_rep1("replace(?::([bceprsw]*))?\\s+([\"'])((?:[^\\\\\\\\2]|\\\\.)*)\\2\\s*$");
+ // Else, it's just one or two (space separated) words
+ QRegExp re_rep2("replace(?::([bceprsw]*))?\\s+(\\S+)(.*)");
+#define unbackslash(s) p=0;\
+while ( (p = pattern.find( '\\' + delim, p )) > -1 )\
+{\
+ if ( !p || pattern[p-1] != '\\' )\
+ pattern.remove( p, 1 );\
+ p++;\
+}
+
+ if ( re_rep.search( cmd ) >= 0 )
+ {
+ flags = re_rep.cap(1);
+ pattern = re_rep.cap( 3 );
+ replacement = re_rep.cap( 4 );
+
+ int p(0);
+ // unbackslash backslashed delimiter strings
+ // in pattern ..
+ QString delim = re_rep.cap( 2 );
+ unbackslash(pattern);
+ // .. and in replacement
+ unbackslash(replacement);
+ }
+ else if ( re_rep1.search( cmd ) >= 0 )
+ {
+ flags = re_rep1.cap(1);
+ pattern = re_rep1.cap( 3 );
+
+ int p(0);
+ QString delim = re_rep1.cap( 2 );
+ unbackslash(pattern);
+ }
+ else if ( re_rep2.search( cmd ) >= 0 )
+ {
+ flags = re_rep2.cap( 1 );
+ pattern = re_rep2.cap( 2 );
+ replacement = re_rep2.cap( 3 ).stripWhiteSpace();
+ }
+ else
+ {
+ msg = i18n("Usage: replace[:[bceprsw]] PATTERN [REPLACEMENT]");
+ return false;
+ }
+ kdDebug()<<"replace '"<<pattern<<"' with '"<<replacement<<"'"<<endl;
+#undef unbackslash
+ }
+
+ long f = 0;
+ if ( flags.contains( 'b' ) ) f |= KFindDialog::FindBackwards;
+ if ( flags.contains( 'c' ) ) f |= KFindDialog::FromCursor;
+ if ( flags.contains( 'e' ) ) f |= KFindDialog::SelectedText;
+ if ( flags.contains( 'r' ) ) f |= KFindDialog::RegularExpression;
+ if ( flags.contains( 'p' ) ) f |= KReplaceDialog::PromptOnReplace;
+ if ( flags.contains( 's' ) ) f |= KFindDialog::CaseSensitive;
+ if ( flags.contains( 'w' ) ) f |= KFindDialog::WholeWordsOnly;
+
+ if ( cmd.startsWith( "find" ) )
+ {
+ ((KateView*)view)->find( pattern, f );
+ return true;
+ }
+ else if ( cmd.startsWith( "replace" ) )
+ {
+ f |= KReplaceDialog::BackReference; // mandatory here?
+ ((KateView*)view)->replace( pattern, replacement, f );
+ return true;
+ }
+
+ return false;
+}
+
+bool SearchCommand::help(class Kate::View *, const QString &cmd, QString &msg)
+{
+ if ( cmd == "find" )
+ msg = i18n("<p>Usage: <code>find[:bcersw] PATTERN</code></p>");
+
+ else if ( cmd == "ifind" )
+ msg = i18n("<p>Usage: <code>ifind:[:bcrs] PATTERN</code>"
+ "<br>ifind does incremental or 'as-you-type' search</p>");
+
+ else
+ msg = i18n("<p>Usage: <code>replace[:bceprsw] PATTERN REPLACEMENT</code></p>");
+
+ msg += i18n(
+ "<h4><caption>Options</h4><p>"
+ "<b>b</b> - Search backward"
+ "<br><b>c</b> - Search from cursor"
+ "<br><b>r</b> - Pattern is a regular expression"
+ "<br><b>s</b> - Case sensitive search"
+ );
+
+ if ( cmd == "find" )
+ msg += i18n(
+ "<br><b>e</b> - Search in selected text only"
+ "<br><b>w</b> - Search whole words only"
+ );
+
+ if ( cmd == "replace" )
+ msg += i18n(
+ "<br><b>p</b> - Prompt for replace</p>"
+ "<p>If REPLACEMENT is not present, an empty string is used.</p>"
+ "<p>If you want to have whitespace in your PATTERN, you need to "
+ "quote both PATTERN and REPLACEMENT with either single or double "
+ "quotes. To have the quote characters in the strings, prepend them "
+ "with a backslash.");
+
+ msg += "</p>";
+ return true;
+}
+
+QStringList SearchCommand::cmds()
+{
+ QStringList l;
+ l << "find" << "replace" << "ifind";
+ return l;
+}
+
+bool SearchCommand::wantsToProcessText( const QString &cmdname )
+{
+ return cmdname == "ifind";
+}
+
+void SearchCommand::processText( Kate::View *view, const QString &cmd )
+{
+ static QRegExp re_ifind("ifind(?::([bcrs]*))?\\s(.*)");
+ if ( re_ifind.search( cmd ) > -1 )
+ {
+ QString flags = re_ifind.cap( 1 );
+ QString pattern = re_ifind.cap( 2 );
+
+
+ // if there is no setup, or the text length is 0, set up the properties
+ if ( ! m_ifindFlags || pattern.isEmpty() )
+ ifindInit( flags );
+ // if there is no fromCursor, add it if this is not the first character
+ else if ( ! ( m_ifindFlags & KFindDialog::FromCursor ) && ! pattern.isEmpty() )
+ m_ifindFlags |= KFindDialog::FromCursor;
+
+ // search..
+ if ( ! pattern.isEmpty() )
+ {
+ KateView *v = (KateView*)view;
+
+ // If it *looks like* we are continuing, place the cursor
+ // at the beginning of the selection, so that the search continues.
+ // ### check more carefully, like is the cursor currently at the end
+ // of the selection.
+ if ( pattern.startsWith( v->selection() ) &&
+ v->selection().length() + 1 == pattern.length() )
+ v->setCursorPositionInternal( v->selStartLine(), v->selStartCol() );
+
+ v->find( pattern, m_ifindFlags, false );
+ }
+ }
+}
+
+void SearchCommand::ifindInit( const QString &flags )
+{
+ long f = 0;
+ if ( flags.contains( 'b' ) ) f |= KFindDialog::FindBackwards;
+ if ( flags.contains( 'c' ) ) f |= KFindDialog::FromCursor;
+ if ( flags.contains( 'r' ) ) f |= KFindDialog::RegularExpression;
+ if ( flags.contains( 's' ) ) f |= KFindDialog::CaseSensitive;
+ m_ifindFlags = f;
+}
+
+void SearchCommand::ifindClear()
+{
+ m_ifindFlags = 0;
+}
+//END SearchCommand
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katesearch.h b/kate/part/katesearch.h
new file mode 100644
index 000000000..3f2ce2ec6
--- /dev/null
+++ b/kate/part/katesearch.h
@@ -0,0 +1,243 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2004-2005 Anders Lund <anders@alweb.dk>
+ Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org>
+ Copyright (C) 2001-2004 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
+ Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KATE_SEARCH_H__
+#define __KATE_SEARCH_H__
+
+#include "katecursor.h"
+#include "../interfaces/document.h"
+
+#include <kdialogbase.h>
+
+#include <qstring.h>
+#include <qregexp.h>
+#include <qstringlist.h>
+#include <qvaluelist.h>
+
+class KateView;
+class KateDocument;
+class KateSuperRangeList;
+
+class KActionCollection;
+
+class KateSearch : public QObject
+{
+ Q_OBJECT
+
+ friend class KateDocument;
+
+ private:
+ class SearchFlags
+ {
+ public:
+ bool caseSensitive :1;
+ bool wholeWords :1;
+ bool fromBeginning :1;
+ bool backward :1;
+ bool selected :1;
+ bool prompt :1;
+ bool replace :1;
+ bool finished :1;
+ bool regExp :1;
+ bool useBackRefs :1;
+ };
+
+ class SConfig
+ {
+ public:
+ SearchFlags flags;
+ KateTextCursor cursor;
+ KateTextCursor wrappedEnd; // after wraping around, search/replace until here
+ bool wrapped; // have we allready wrapped around ?
+ bool showNotFound; // pop up annoying dialogs?
+ uint matchedLength;
+ KateTextCursor selBegin;
+ KateTextCursor selEnd;
+ };
+
+ public:
+ enum Dialog_results {
+ srCancel = KDialogBase::Cancel,
+ srAll = KDialogBase::User1,
+ srLast = KDialogBase::User2,
+ srNo = KDialogBase::User3,
+ srYes = KDialogBase::Ok
+ };
+
+ public:
+ KateSearch( KateView* );
+ ~KateSearch();
+
+ void createActions( KActionCollection* );
+
+ public slots:
+ void find();
+ /**
+ * Search for @p pattern given @p flags
+ * This is for the commandline "find", and is forwarded by
+ * KateView.
+ * @param pattern string or regex pattern to search for.
+ * @param flags a OR'ed combination of KFindDialog::Options
+ * @param add wether this string should be added to the recent search list
+ * @param shownotfound wether to pop up "Not round: PATTERN" when that happens.
+ * That must now be explicitly required -- the find dialog does, but the commandline
+ * incremental search does not.
+ */
+ void find( const QString &pattern, long flags, bool add=true, bool shownotfound=false );
+ void replace();
+ /**
+ * Replace @p pattern with @p replacement given @p flags.
+ * This is for the commandline "replace" and is forwarded
+ * by KateView.
+ * @param pattern string or regular expression to search for
+ * @param replacement Replacement string.
+ * @param flags OR'd combination of KFindDialog::Options
+ */
+ void replace( const QString &pattern, const QString &replacement, long flags );
+ void findAgain( bool reverseDirection );
+
+ private slots:
+ void replaceSlot();
+ void slotFindNext() { findAgain( false ); }
+ void slotFindPrev() { findAgain( true ); }
+
+ private:
+ static void addToList( QStringList&, const QString& );
+ static void addToSearchList( const QString& s ) { addToList( s_searchList, s ); }
+ static void addToReplaceList( const QString& s ) { addToList( s_replaceList, s ); }
+ static QStringList s_searchList; ///< recent patterns
+ static QStringList s_replaceList; ///< recent replacement strings
+ static QString s_pattern; ///< the string to search for
+
+ void search( SearchFlags flags );
+ void wrapSearch();
+ bool askContinue();
+
+ void findAgain();
+ void promptReplace();
+ void replaceAll();
+ void replaceOne();
+ void skipOne();
+
+ QString getSearchText();
+ KateTextCursor getCursor( SearchFlags flags );
+ bool doSearch( const QString& text );
+ void exposeFound( KateTextCursor &cursor, int slen );
+
+ inline KateView* view() { return m_view; }
+ inline KateDocument* doc() { return m_doc; }
+
+ KateView* m_view;
+ KateDocument* m_doc;
+
+ KateSuperRangeList* m_arbitraryHLList;
+
+ SConfig s;
+
+ QValueList<SConfig> m_searchResults;
+ int m_resultIndex;
+
+ int replaces;
+ QDialog* replacePrompt;
+ QString m_replacement;
+ QRegExp m_re;
+};
+
+/**
+ * simple replace prompt dialog
+ */
+class KateReplacePrompt : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * Constructor
+ * @param parent parent widget for the dialog
+ */
+ KateReplacePrompt(QWidget *parent);
+
+ signals:
+ /**
+ * button clicked
+ */
+ void clicked();
+
+ protected slots:
+ /**
+ * ok pressed
+ */
+ void slotOk ();
+
+ /**
+ * close pressed
+ */
+ void slotClose ();
+
+ /**
+ * replace all pressed
+ */
+ void slotUser1 ();
+
+ /**
+ * last pressed
+ */
+ void slotUser2 ();
+
+ /**
+ * Yes pressed
+ */
+ void slotUser3 ();
+
+ /**
+ * dialog done
+ * @param result dialog result
+ */
+ void done (int result);
+};
+
+class SearchCommand : public Kate::Command, public Kate::CommandExtension
+{
+ public:
+ SearchCommand() : m_ifindFlags(0) {;}
+ bool exec(class Kate::View *view, const QString &cmd, QString &errorMsg);
+ bool help(class Kate::View *, const QString &, QString &);
+ QStringList cmds();
+ bool wantsToProcessText( const QString &/*cmdname*/ );
+ void processText( Kate::View *view, const QString& text );
+
+ private:
+ /**
+ * set up properties for incremental find
+ */
+ void ifindInit( const QString &cmd );
+ /**
+ * clear properties for incremental find
+ */
+ void ifindClear();
+
+ long m_ifindFlags;
+};
+
+#endif
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katespell.cpp b/kate/part/katespell.cpp
new file mode 100644
index 000000000..1afd8d53f
--- /dev/null
+++ b/kate/part/katespell.cpp
@@ -0,0 +1,221 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2004-2005 Anders Lund <anders@alweb.dk>
+ Copyright (C) 2003 Clarence Dang <dang@kde.org>
+ Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org>
+ Copyright (C) 2001-2004 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
+ Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "katespell.h"
+#include "katespell.moc"
+
+#include "kateview.h"
+
+#include <kaction.h>
+#include <kstdaction.h>
+#include <kspell.h>
+#include <ksconfig.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+
+KateSpell::KateSpell( KateView* view )
+ : QObject( view )
+ , m_view (view)
+ , m_kspell (0)
+{
+}
+
+KateSpell::~KateSpell()
+{
+ // kspell stuff
+ if( m_kspell )
+ {
+ m_kspell->setAutoDelete(true);
+ m_kspell->cleanUp(); // need a way to wait for this to complete
+ delete m_kspell;
+ }
+}
+
+void KateSpell::createActions( KActionCollection* ac )
+{
+ KStdAction::spelling( this, SLOT(spellcheck()), ac );
+ KAction *a = new KAction( i18n("Spelling (from cursor)..."), "spellcheck", 0, this, SLOT(spellcheckFromCursor()), ac, "tools_spelling_from_cursor" );
+ a->setWhatsThis(i18n("Check the document's spelling from the cursor and forward"));
+
+ m_spellcheckSelection = new KAction( i18n("Spellcheck Selection..."), "spellcheck", 0, this, SLOT(spellcheckSelection()), ac, "tools_spelling_selection" );
+ m_spellcheckSelection->setWhatsThis(i18n("Check spelling of the selected text"));
+}
+
+void KateSpell::updateActions ()
+{
+ m_spellcheckSelection->setEnabled (m_view->hasSelection ());
+}
+
+void KateSpell::spellcheckFromCursor()
+{
+ spellcheck( KateTextCursor(m_view->cursorLine(), m_view->cursorColumnReal()) );
+}
+
+void KateSpell::spellcheckSelection()
+{
+ KateTextCursor from( m_view->selStartLine(), m_view->selStartCol() );
+ KateTextCursor to( m_view->selEndLine(), m_view->selEndCol() );
+ spellcheck( from, to );
+}
+
+void KateSpell::spellcheck()
+{
+ spellcheck( KateTextCursor( 0, 0 ) );
+}
+
+void KateSpell::spellcheck( const KateTextCursor &from, const KateTextCursor &to )
+{
+ m_spellStart = from;
+ m_spellEnd = to;
+
+ if ( to.line() == 0 && to.col() == 0 )
+ {
+ int lln = m_view->doc()->lastLine();
+ m_spellEnd.setLine( lln );
+ m_spellEnd.setCol( m_view->doc()->lineLength( lln ) );
+ }
+
+ m_spellPosCursor = from;
+ m_spellLastPos = 0;
+
+ QString mt = m_view->doc()->mimeType()/*->name()*/;
+
+ KSpell::SpellerType type = KSpell::Text;
+ if ( mt == "text/x-tex" || mt == "text/x-latex" )
+ type = KSpell::TeX;
+ else if ( mt == "text/html" || mt == "text/xml" || mt == "text/docbook" || mt == "application/x-php")
+ type = KSpell::HTML;
+
+ KSpellConfig *ksc = new KSpellConfig;
+ QStringList ksEncodings;
+ ksEncodings << "US-ASCII" << "ISO 8859-1" << "ISO 8859-2" << "ISO 8859-3"
+ << "ISO 8859-4" << "ISO 8859-5" << "ISO 8859-7" << "ISO 8859-8"
+ << "ISO 8859-9" << "ISO 8859-13" << "ISO 8859-15" << "UTF-8"
+ << "KOI8-R" << "KOI8-U" << "CP1251" << "CP1255";
+
+ int enc = ksEncodings.findIndex( m_view->doc()->encoding() );
+ if ( enc > -1 )
+ {
+ ksc->setEncoding( enc );
+ kdDebug(13020)<<"KateSpell::spellCheck(): using encoding: "<<enc<<" ("<<ksEncodings[enc]<<") and KSpell::Type "<<type<<" (for '"<<mt<<"')"<<endl;
+ }
+ else
+ kdDebug(13020)<<"KateSpell::spellCheck(): using encoding: "<<enc<<" and KSpell::Type "<<type<<" (for '"<<mt<<"')"<<endl;
+
+ m_kspell = new KSpell( m_view, i18n("Spellcheck"),
+ this, SLOT(ready(KSpell *)), ksc, true, true, type );
+
+ connect( m_kspell, SIGNAL(death()),
+ this, SLOT(spellCleanDone()) );
+
+ connect( m_kspell, SIGNAL(misspelling(const QString&, const QStringList&, unsigned int)),
+ this, SLOT(misspelling(const QString&, const QStringList&, unsigned int)) );
+ connect( m_kspell, SIGNAL(corrected(const QString&, const QString&, unsigned int)),
+ this, SLOT(corrected(const QString&, const QString&, unsigned int)) );
+ connect( m_kspell, SIGNAL(done(const QString&)),
+ this, SLOT(spellResult(const QString&)) );
+}
+
+void KateSpell::ready(KSpell *)
+{
+ m_kspell->setProgressResolution( 1 );
+
+ m_kspell->check( m_view->doc()->text( m_spellStart.line(), m_spellStart.col(), m_spellEnd.line(), m_spellEnd.col() ) );
+
+ kdDebug (13020) << "SPELLING READY STATUS: " << m_kspell->status () << endl;
+}
+
+void KateSpell::locatePosition( uint pos, uint& line, uint& col )
+{
+ uint remains;
+
+ while ( m_spellLastPos < pos )
+ {
+ remains = pos - m_spellLastPos;
+ uint l = m_view->doc()->lineLength( m_spellPosCursor.line() ) - m_spellPosCursor.col();
+ if ( l > remains )
+ {
+ m_spellPosCursor.setCol( m_spellPosCursor.col() + remains );
+ m_spellLastPos = pos;
+ }
+ else
+ {
+ m_spellPosCursor.setLine( m_spellPosCursor.line() + 1 );
+ m_spellPosCursor.setCol(0);
+ m_spellLastPos += l + 1;
+ }
+ }
+
+ line = m_spellPosCursor.line();
+ col = m_spellPosCursor.col();
+}
+
+void KateSpell::misspelling( const QString& origword, const QStringList&, unsigned int pos )
+{
+ uint line, col;
+
+ locatePosition( pos, line, col );
+
+ m_view->setCursorPositionInternal (line, col, 1);
+ m_view->setSelection( line, col, line, col + origword.length() );
+}
+
+void KateSpell::corrected( const QString& originalword, const QString& newword, unsigned int pos )
+{
+ uint line, col;
+
+ locatePosition( pos, line, col );
+
+ m_view->doc()->removeText( line, col, line, col + originalword.length() );
+ m_view->doc()->insertText( line, col, newword );
+}
+
+void KateSpell::spellResult( const QString& )
+{
+ m_view->clearSelection();
+ m_kspell->cleanUp();
+}
+
+void KateSpell::spellCleanDone()
+{
+ KSpell::spellStatus status = m_kspell->status();
+
+ if( status == KSpell::Error ) {
+ KMessageBox::sorry( 0,
+ i18n("The spelling program could not be started. "
+ "Please make sure you have set the correct spelling program "
+ "and that it is properly configured and in your PATH."));
+ } else if( status == KSpell::Crashed ) {
+ KMessageBox::sorry( 0,
+ i18n("The spelling program seems to have crashed."));
+ }
+
+ delete m_kspell;
+ m_kspell = 0;
+
+ kdDebug (13020) << "SPELLING END" << endl;
+}
+//END
+
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katespell.h b/kate/part/katespell.h
new file mode 100644
index 000000000..db7dfe0ed
--- /dev/null
+++ b/kate/part/katespell.h
@@ -0,0 +1,86 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2004-2005 Anders Lund <anders@alweb.dk>
+ Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org>
+ Copyright (C) 2001-2004 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
+ Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KATE_SPELL_H__
+#define __KATE_SPELL_H__
+
+#include "katecursor.h"
+
+class KateView;
+
+class KAction;
+class KSpell;
+
+class KateSpell : public QObject
+{
+ Q_OBJECT
+
+ public:
+ KateSpell( KateView* );
+ ~KateSpell();
+
+ void createActions( KActionCollection* );
+
+ void updateActions ();
+
+ // spellcheck from cursor, selection
+ private slots:
+ void spellcheckFromCursor();
+
+ // defined here in anticipation of pr view selections ;)
+ void spellcheckSelection();
+
+ void spellcheck();
+
+ /**
+ * Spellcheck a defined portion of the text.
+ *
+ * @param from Where to start the check
+ * @param to Where to end. If this is (0,0), it will be set to the end of the document.
+ */
+ void spellcheck( const KateTextCursor &from, const KateTextCursor &to=KateTextCursor() );
+
+ void ready(KSpell *);
+ void misspelling( const QString&, const QStringList&, unsigned int );
+ void corrected ( const QString&, const QString&, unsigned int);
+ void spellResult( const QString& );
+ void spellCleanDone();
+
+ void locatePosition( uint pos, uint& line, uint& col );
+
+ private:
+ KateView *m_view;
+ KAction *m_spellcheckSelection;
+
+ KSpell *m_kspell;
+
+ // define the part of the text to check
+ KateTextCursor m_spellStart, m_spellEnd;
+
+ // keep track of where we are.
+ KateTextCursor m_spellPosCursor;
+ uint m_spellLastPos;
+};
+
+#endif
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katesupercursor.cpp b/kate/part/katesupercursor.cpp
new file mode 100644
index 000000000..774b695db
--- /dev/null
+++ b/kate/part/katesupercursor.cpp
@@ -0,0 +1,746 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2003 Hamish Rodda <rodda@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "katesupercursor.h"
+#include "katesupercursor.moc"
+
+#include "katedocument.h"
+
+#include <kdebug.h>
+
+#include <qobjectlist.h>
+
+KateSuperCursor::KateSuperCursor(KateDocument* doc, bool privateC, const KateTextCursor& cursor, QObject* parent, const char* name)
+ : QObject(parent, name)
+ , KateDocCursor(cursor.line(), cursor.col(), doc)
+ , Kate::Cursor ()
+ , m_doc (doc)
+{
+ m_moveOnInsert = false;
+ m_lineRemoved = false;
+ m_privateCursor = privateC;
+
+ m_doc->addSuperCursor (this, privateC);
+}
+
+KateSuperCursor::KateSuperCursor(KateDocument* doc, bool privateC, int lineNum, int col, QObject* parent, const char* name)
+ : QObject(parent, name)
+ , KateDocCursor(lineNum, col, doc)
+ , Kate::Cursor ()
+ , m_doc (doc)
+{
+ m_moveOnInsert = false;
+ m_lineRemoved = false;
+ m_privateCursor = privateC;
+
+ m_doc->addSuperCursor (this, privateC);
+}
+
+KateSuperCursor::~KateSuperCursor ()
+{
+ m_doc->removeSuperCursor (this, m_privateCursor);
+}
+
+void KateSuperCursor::position(uint *pline, uint *pcol) const
+{
+ KateDocCursor::position(pline, pcol);
+}
+
+bool KateSuperCursor::setPosition(uint line, uint col)
+{
+ if (line == uint(-2) && col == uint(-2)) { delete this; return true; }
+ return KateDocCursor::setPosition(line, col);
+}
+
+bool KateSuperCursor::insertText(const QString& s)
+{
+ return KateDocCursor::insertText(s);
+}
+
+bool KateSuperCursor::removeText(uint nbChar)
+{
+ return KateDocCursor::removeText(nbChar);
+}
+
+QChar KateSuperCursor::currentChar() const
+{
+ return KateDocCursor::currentChar();
+}
+
+bool KateSuperCursor::atStartOfLine() const
+{
+ return col() == 0;
+}
+
+bool KateSuperCursor::atEndOfLine() const
+{
+ return col() >= (int)m_doc->kateTextLine(line())->length();
+}
+
+bool KateSuperCursor::moveOnInsert() const
+{
+ return m_moveOnInsert;
+}
+
+void KateSuperCursor::setMoveOnInsert(bool moveOnInsert)
+{
+ m_moveOnInsert = moveOnInsert;
+}
+
+void KateSuperCursor::setLine(int lineNum)
+{
+ int tempLine = line(), tempcol = col();
+ KateDocCursor::setLine(lineNum);
+
+ if (tempLine != line() || tempcol != col())
+ emit positionDirectlyChanged();
+}
+
+void KateSuperCursor::setCol(int colNum)
+{
+ KateDocCursor::setCol(colNum);
+}
+
+void KateSuperCursor::setPos(const KateTextCursor& pos)
+{
+ KateDocCursor::setPos(pos);
+}
+
+void KateSuperCursor::setPos(int lineNum, int colNum)
+{
+ KateDocCursor::setPos(lineNum, colNum);
+}
+
+void KateSuperCursor::editTextInserted(uint line, uint col, uint len)
+{
+ if (m_line == int(line))
+ {
+ if ((m_col > int(col)) || (m_moveOnInsert && (m_col == int(col))))
+ {
+ bool insertedAt = m_col == int(col);
+
+ m_col += len;
+
+ if (insertedAt)
+ emit charInsertedAt();
+
+ emit positionChanged();
+ return;
+ }
+ }
+
+ emit positionUnChanged();
+}
+
+void KateSuperCursor::editTextRemoved(uint line, uint col, uint len)
+{
+ if (m_line == int(line))
+ {
+ if (m_col > int(col))
+ {
+ if (m_col > int(col + len))
+ {
+ m_col -= len;
+ }
+ else
+ {
+ bool prevCharDeleted = m_col == int(col + len);
+
+ m_col = col;
+
+ if (prevCharDeleted)
+ emit charDeletedBefore();
+ else
+ emit positionDeleted();
+ }
+
+ emit positionChanged();
+ return;
+
+ }
+ else if (m_col == int(col))
+ {
+ emit charDeletedAfter();
+ }
+ }
+
+ emit positionUnChanged();
+}
+
+void KateSuperCursor::editLineWrapped(uint line, uint col, bool newLine)
+{
+ if (newLine)
+ {
+ if (m_line > int(line) || (m_line == int(line) && m_col >= int(col)))
+ {
+ if(m_line == int(line))
+ m_col -= col;
+ m_line++;
+
+ emit positionChanged();
+ return;
+ }
+ }
+ else if ( (m_line == int(line)) && (m_col > int(col)) || (m_moveOnInsert && (m_col == int(col))) )
+ {
+ m_line++;
+ m_col -= col;
+
+ emit positionChanged();
+ return;
+ }
+
+ emit positionUnChanged();
+}
+
+void KateSuperCursor::editLineUnWrapped(uint line, uint col, bool removeLine, uint length)
+{
+ if (removeLine && (m_line > int(line+1)))
+ {
+ m_line--;
+
+ emit positionChanged();
+ return;
+ }
+ else if ( (m_line == int(line+1)) && (removeLine || (m_col < int(length))) )
+ {
+ m_line = line;
+ m_col += col;
+
+ emit positionChanged();
+ return;
+ }
+ else if ( (m_line == int(line+1)) && (m_col >= int(length)) )
+ {
+ m_col -= length;
+
+ emit positionChanged();
+ return;
+ }
+
+ emit positionUnChanged();
+}
+
+void KateSuperCursor::editLineInserted (uint line)
+{
+ if (m_line >= int(line))
+ {
+ m_line++;
+
+ emit positionChanged();
+ return;
+ }
+
+ emit positionUnChanged();
+}
+
+void KateSuperCursor::editLineRemoved(uint line)
+{
+ if (m_line > int(line))
+ {
+ m_line--;
+
+ emit positionChanged();
+ return;
+ }
+ else if (m_line == int(line))
+ {
+ m_line = (line <= m_doc->lastLine()) ? line : (line - 1);
+ m_col = 0;
+
+ emit positionDeleted();
+
+ emit positionChanged();
+ return;
+ }
+
+ emit positionUnChanged();
+}
+
+KateSuperCursor::operator QString()
+{
+ return QString("[%1,%1]").arg(line()).arg(col());
+}
+
+KateSuperRange::KateSuperRange(KateSuperCursor* start, KateSuperCursor* end, QObject* parent, const char* name)
+ : QObject(parent, name)
+ , m_start(start)
+ , m_end(end)
+ , m_evaluate(false)
+ , m_startChanged(false)
+ , m_endChanged(false)
+ , m_deleteCursors(false)
+ , m_allowZeroLength(false)
+{
+ init();
+}
+
+KateSuperRange::KateSuperRange(KateDocument* doc, const KateRange& range, QObject* parent, const char* name)
+ : QObject(parent, name)
+ , m_start(new KateSuperCursor(doc, true, range.start()))
+ , m_end(new KateSuperCursor(doc, true, range.end()))
+ , m_evaluate(false)
+ , m_startChanged(false)
+ , m_endChanged(false)
+ , m_deleteCursors(true)
+ , m_allowZeroLength(false)
+{
+ init();
+}
+
+KateSuperRange::KateSuperRange(KateDocument* doc, const KateTextCursor& start, const KateTextCursor& end, QObject* parent, const char* name)
+ : QObject(parent, name)
+ , m_start(new KateSuperCursor(doc, true, start))
+ , m_end(new KateSuperCursor(doc, true, end))
+ , m_evaluate(false)
+ , m_startChanged(false)
+ , m_endChanged(false)
+ , m_deleteCursors(true)
+ , m_allowZeroLength(false)
+{
+ init();
+}
+
+void KateSuperRange::init()
+{
+ Q_ASSERT(isValid());
+ if (!isValid())
+ kdDebug(13020) << superStart() << " " << superEnd() << endl;
+
+ insertChild(m_start);
+ insertChild(m_end);
+
+ setBehaviour(DoNotExpand);
+
+ // Not necessarily the best implementation
+ connect(m_start, SIGNAL(positionDirectlyChanged()), SIGNAL(contentsChanged()));
+ connect(m_end, SIGNAL(positionDirectlyChanged()), SIGNAL(contentsChanged()));
+
+ connect(m_start, SIGNAL(positionChanged()), SLOT(slotEvaluateChanged()));
+ connect(m_end, SIGNAL(positionChanged()), SLOT(slotEvaluateChanged()));
+ connect(m_start, SIGNAL(positionUnChanged()), SLOT(slotEvaluateUnChanged()));
+ connect(m_end, SIGNAL(positionUnChanged()), SLOT(slotEvaluateUnChanged()));
+ connect(m_start, SIGNAL(positionDeleted()), SIGNAL(boundaryDeleted()));
+ connect(m_end, SIGNAL(positionDeleted()), SIGNAL(boundaryDeleted()));
+}
+
+KateSuperRange::~KateSuperRange()
+{
+ if (m_deleteCursors)
+ {
+ //insertChild(m_start);
+ //insertChild(m_end);
+ delete m_start;
+ delete m_end;
+ }
+}
+
+KateTextCursor& KateSuperRange::start()
+{
+ return *m_start;
+}
+
+const KateTextCursor& KateSuperRange::start() const
+{
+ return *m_start;
+}
+
+KateTextCursor& KateSuperRange::end()
+{
+ return *m_end;
+}
+
+const KateTextCursor& KateSuperRange::end() const
+{
+ return *m_end;
+}
+
+KateSuperCursor& KateSuperRange::superStart()
+{
+ return *m_start;
+}
+
+const KateSuperCursor& KateSuperRange::superStart() const
+{
+ return *m_start;
+}
+
+KateSuperCursor& KateSuperRange::superEnd()
+{
+ return *m_end;
+}
+
+const KateSuperCursor& KateSuperRange::superEnd() const
+{
+ return *m_end;
+}
+
+int KateSuperRange::behaviour() const
+{
+ return (m_start->moveOnInsert() ? DoNotExpand : ExpandLeft) | (m_end->moveOnInsert() ? ExpandRight : DoNotExpand);
+}
+
+void KateSuperRange::setBehaviour(int behaviour)
+{
+ m_start->setMoveOnInsert(behaviour & ExpandLeft);
+ m_end->setMoveOnInsert(!(behaviour & ExpandRight));
+}
+
+bool KateSuperRange::isValid() const
+{
+ return superStart() <= superEnd();
+}
+
+bool KateSuperRange::owns(const KateTextCursor& cursor) const
+{
+ if (!includes(cursor)) return false;
+
+ if (children())
+ for (QObjectListIt it(*children()); *it; ++it)
+ if ((*it)->inherits("KateSuperRange"))
+ if (static_cast<KateSuperRange*>(*it)->owns(cursor))
+ return false;
+
+ return true;
+}
+
+bool KateSuperRange::includes(const KateTextCursor& cursor) const
+{
+ return isValid() && cursor >= superStart() && cursor < superEnd();
+}
+
+bool KateSuperRange::includes(uint lineNum) const
+{
+ return isValid() && (int)lineNum >= superStart().line() && (int)lineNum <= superEnd().line();
+}
+
+bool KateSuperRange::includesWholeLine(uint lineNum) const
+{
+ return isValid() && ((int)lineNum > superStart().line() || ((int)lineNum == superStart().line() && superStart().atStartOfLine())) && ((int)lineNum < superEnd().line() || ((int)lineNum == superEnd().line() && superEnd().atEndOfLine()));
+}
+
+bool KateSuperRange::boundaryAt(const KateTextCursor& cursor) const
+{
+ return isValid() && (cursor == superStart() || cursor == superEnd());
+}
+
+bool KateSuperRange::boundaryOn(uint lineNum) const
+{
+ return isValid() && (superStart().line() == (int)lineNum || superEnd().line() == (int)lineNum);
+}
+
+void KateSuperRange::slotEvaluateChanged()
+{
+ if (sender() == static_cast<QObject*>(m_start)) {
+ if (m_evaluate) {
+ if (!m_endChanged) {
+ // Only one was changed
+ evaluateEliminated();
+
+ } else {
+ // Both were changed
+ evaluatePositionChanged();
+ m_endChanged = false;
+ }
+
+ } else {
+ m_startChanged = true;
+ }
+
+ } else {
+ if (m_evaluate) {
+ if (!m_startChanged) {
+ // Only one was changed
+ evaluateEliminated();
+
+ } else {
+ // Both were changed
+ evaluatePositionChanged();
+ m_startChanged = false;
+ }
+
+ } else {
+ m_endChanged = true;
+ }
+ }
+
+ m_evaluate = !m_evaluate;
+}
+
+void KateSuperRange::slotEvaluateUnChanged()
+{
+ if (sender() == static_cast<QObject*>(m_start)) {
+ if (m_evaluate) {
+ if (m_endChanged) {
+ // Only one changed
+ evaluateEliminated();
+ m_endChanged = false;
+
+ } else {
+ // Neither changed
+ emit positionUnChanged();
+ }
+ }
+
+ } else {
+ if (m_evaluate) {
+ if (m_startChanged) {
+ // Only one changed
+ evaluateEliminated();
+ m_startChanged = false;
+
+ } else {
+ // Neither changed
+ emit positionUnChanged();
+ }
+ }
+ }
+
+ m_evaluate = !m_evaluate;
+}
+
+void KateSuperRange::slotTagRange()
+{
+ emit tagRange(this);
+}
+
+void KateSuperRange::evaluateEliminated()
+{
+ if (superStart() == superEnd()) {
+ if (!m_allowZeroLength) emit eliminated();
+ }
+ else
+ emit contentsChanged();
+}
+
+void KateSuperRange::evaluatePositionChanged()
+{
+ if (superStart() == superEnd())
+ emit eliminated();
+ else
+ emit positionChanged();
+}
+
+int KateSuperCursorList::compareItems(QPtrCollection::Item item1, QPtrCollection::Item item2)
+{
+ if (*(static_cast<KateSuperCursor*>(item1)) == *(static_cast<KateSuperCursor*>(item2)))
+ return 0;
+
+ return *(static_cast<KateSuperCursor*>(item1)) < *(static_cast<KateSuperCursor*>(item2)) ? -1 : 1;
+}
+
+KateSuperRangeList::KateSuperRangeList(bool autoManage, QObject* parent, const char* name)
+ : QObject(parent, name)
+ , m_autoManage(autoManage)
+ , m_connect(true)
+ , m_trackingBoundaries(false)
+{
+ setAutoManage(autoManage);
+}
+
+KateSuperRangeList::KateSuperRangeList(const QPtrList<KateSuperRange>& rangeList, QObject* parent, const char* name)
+ : QObject(parent, name)
+ , m_autoManage(false)
+ , m_connect(false)
+ , m_trackingBoundaries(false)
+{
+ appendList(rangeList);
+}
+
+void KateSuperRangeList::appendList(const QPtrList<KateSuperRange>& rangeList)
+{
+ for (QPtrListIterator<KateSuperRange> it = rangeList; *it; ++it)
+ append(*it);
+}
+
+void KateSuperRangeList::clear()
+{
+ for (KateSuperRange* range = first(); range; range = next())
+ emit rangeEliminated(range);
+
+ QPtrList<KateSuperRange>::clear();
+}
+
+void KateSuperRangeList::connectAll()
+{
+ if (!m_connect) {
+ m_connect = true;
+ for (KateSuperRange* range = first(); range; range = next()) {
+ connect(range, SIGNAL(destroyed(QObject*)), SLOT(slotDeleted(QObject*)));
+ connect(range, SIGNAL(eliminated()), SLOT(slotEliminated()));
+ }
+ }
+}
+
+bool KateSuperRangeList::autoManage() const
+{
+ return m_autoManage;
+}
+
+void KateSuperRangeList::setAutoManage(bool autoManage)
+{
+ m_autoManage = autoManage;
+ setAutoDelete(m_autoManage);
+}
+
+QPtrList<KateSuperRange> KateSuperRangeList::rangesIncluding(const KateTextCursor& cursor)
+{
+ sort();
+
+ QPtrList<KateSuperRange> ret;
+
+ for (KateSuperRange* r = first(); r; r = next())
+ if (r->includes(cursor))
+ ret.append(r);
+
+ return ret;
+}
+
+QPtrList<KateSuperRange> KateSuperRangeList::rangesIncluding(uint line)
+{
+ sort();
+
+ QPtrList<KateSuperRange> ret;
+
+ for (KateSuperRange* r = first(); r; r = next())
+ if (r->includes(line))
+ ret.append(r);
+
+ return ret;
+}
+
+bool KateSuperRangeList::rangesInclude(const KateTextCursor& cursor)
+{
+ for (KateSuperRange* r = first(); r; r = next())
+ if (r->includes(cursor))
+ return true;
+
+ return false;
+}
+
+void KateSuperRangeList::slotEliminated()
+{
+ if (sender()) {
+ KateSuperRange* range = static_cast<KateSuperRange*>(const_cast<QObject*>(sender()));
+ emit rangeEliminated(range);
+
+ if (m_trackingBoundaries) {
+ m_columnBoundaries.removeRef(range->m_start);
+ m_columnBoundaries.removeRef(range->m_end);
+ }
+
+ if (m_autoManage)
+ removeRef(range);
+
+ if (!count())
+ emit listEmpty();
+ }
+}
+
+void KateSuperRangeList::slotDeleted(QObject* range)
+{
+ //kdDebug(13020)<<"KateSuperRangeList::slotDeleted"<<endl;
+ KateSuperRange* r = static_cast<KateSuperRange*>(range);
+
+ if (m_trackingBoundaries) {
+ m_columnBoundaries.removeRef(r->m_start);
+ m_columnBoundaries.removeRef(r->m_end);
+ }
+
+ int index = findRef(r);
+ if (index != -1)
+ take(index);
+ //else kdDebug(13020)<<"Range not found in list"<<endl;
+
+ if (!count())
+ emit listEmpty();
+}
+
+KateSuperCursor* KateSuperRangeList::firstBoundary(const KateTextCursor* start)
+{
+ if (!m_trackingBoundaries) {
+ m_trackingBoundaries = true;
+
+ for (KateSuperRange* r = first(); r; r = next()) {
+ m_columnBoundaries.append(&(r->superStart()));
+ m_columnBoundaries.append(&(r->superEnd()));
+ }
+ }
+
+ m_columnBoundaries.sort();
+
+ if (start)
+ // OPTIMISE: QMap with QPtrList for each line? (==> sorting issues :( )
+ for (KateSuperCursor* c = m_columnBoundaries.first(); c; c = m_columnBoundaries.next())
+ if (*start <= *c)
+ break;
+
+ return m_columnBoundaries.current();
+}
+
+KateSuperCursor* KateSuperRangeList::nextBoundary()
+{
+ KateSuperCursor* current = m_columnBoundaries.current();
+
+ // make sure the new cursor is after the current cursor; multiple cursors with the same position can be in the list.
+ if (current)
+ while (m_columnBoundaries.next())
+ if (*(m_columnBoundaries.current()) != *current)
+ break;
+
+ return m_columnBoundaries.current();
+}
+
+KateSuperCursor* KateSuperRangeList::currentBoundary()
+{
+ return m_columnBoundaries.current();
+}
+
+int KateSuperRangeList::compareItems(QPtrCollection::Item item1, QPtrCollection::Item item2)
+{
+ if (static_cast<KateSuperRange*>(item1)->superStart() == static_cast<KateSuperRange*>(item2)->superStart()) {
+ if (static_cast<KateSuperRange*>(item1)->superEnd() == static_cast<KateSuperRange*>(item2)->superEnd()) {
+ return 0;
+ } else {
+ return static_cast<KateSuperRange*>(item1)->superEnd() < static_cast<KateSuperRange*>(item2)->superEnd() ? -1 : 1;
+ }
+ }
+
+ return static_cast<KateSuperRange*>(item1)->superStart() < static_cast<KateSuperRange*>(item2)->superStart() ? -1 : 1;
+}
+
+QPtrCollection::Item KateSuperRangeList::newItem(QPtrCollection::Item d)
+{
+ if (m_connect) {
+ connect(static_cast<KateSuperRange*>(d), SIGNAL(destroyed(QObject*)), SLOT(slotDeleted(QObject*)));
+ connect(static_cast<KateSuperRange*>(d), SIGNAL(eliminated()), SLOT(slotEliminated()));
+ connect(static_cast<KateSuperRange*>(d), SIGNAL(tagRange(KateSuperRange*)), SIGNAL(tagRange(KateSuperRange*)));
+
+ // HACK HACK
+ static_cast<KateSuperRange*>(d)->slotTagRange();
+ }
+
+ if (m_trackingBoundaries) {
+ m_columnBoundaries.append(&(static_cast<KateSuperRange*>(d)->superStart()));
+ m_columnBoundaries.append(&(static_cast<KateSuperRange*>(d)->superEnd()));
+ }
+
+ return QPtrList<KateSuperRange>::newItem(d);
+}
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katesupercursor.h b/kate/part/katesupercursor.h
new file mode 100644
index 000000000..e6b16baa8
--- /dev/null
+++ b/kate/part/katesupercursor.h
@@ -0,0 +1,463 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2003 Hamish Rodda <rodda@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KATESUPERCURSOR_H
+#define KATESUPERCURSOR_H
+
+#include "katecursor.h"
+
+class KateDocument;
+class KateView;
+
+/**
+ * Possible additional features:
+ * - Notification when a cursor enters or exits a view
+ * - suggest something :)
+ *
+ * Unresolved issues:
+ * - testing, testing, testing
+ * - ie. everything which hasn't already been tested, you can see that which has inside the #ifdefs
+ * - api niceness
+ */
+
+/**
+ * A cursor which updates and gives off various interesting signals.
+ *
+ * This aims to be a working version of what KateCursor was originally intended to be.
+ *
+ * @author Hamish Rodda
+ **/
+class KateSuperCursor : public QObject, public KateDocCursor, public Kate::Cursor
+{
+ Q_OBJECT
+
+public:
+ /**
+ * bool privateC says: if private, than don't show to apps using the cursorinterface in the list,
+ * all internally only used SuperCursors should be private or people could modify them from the
+ * outside breaking kate's internals
+ */
+ KateSuperCursor(KateDocument* doc, bool privateC, const KateTextCursor& cursor, QObject* parent = 0L, const char* name = 0L);
+ KateSuperCursor(KateDocument* doc, bool privateC, int lineNum = 0, int col = 0, QObject* parent = 0L, const char* name = 0L);
+
+ ~KateSuperCursor ();
+
+ public:
+ // KTextEditor::Cursor interface
+ void position(uint *line, uint *col) const;
+ bool setPosition(uint line, uint col);
+ bool insertText(const QString& text);
+ bool removeText(uint numberOfCharacters);
+ QChar currentChar() const;
+
+ /**
+ * @returns true if the cursor is situated at the start of the line, false if it isn't.
+ */
+ bool atStartOfLine() const;
+
+ /**
+ * @returns true if the cursor is situated at the end of the line, false if it isn't.
+ */
+ bool atEndOfLine() const;
+
+ /**
+ * Returns how this cursor behaves when text is inserted at the cursor.
+ * Defaults to not moving on insert.
+ */
+ bool moveOnInsert() const;
+
+ /**
+ * Change the behavior of the cursor when text is inserted at the cursor.
+ *
+ * If @p moveOnInsert is true, the cursor will end up at the end of the insert.
+ */
+ void setMoveOnInsert(bool moveOnInsert);
+
+ /**
+ * Debug: output the position.
+ */
+ operator QString();
+
+ // Reimplementations;
+ virtual void setLine(int lineNum);
+ virtual void setCol(int colNum);
+ virtual void setPos(const KateTextCursor& pos);
+ virtual void setPos(int lineNum, int colNum);
+
+ signals:
+ /**
+ * The cursor's position was directly changed by the program.
+ */
+ void positionDirectlyChanged();
+
+ /**
+ * The cursor's position was changed.
+ */
+ void positionChanged();
+
+ /**
+ * Athough an edit took place, the cursor's position was unchanged.
+ */
+ void positionUnChanged();
+
+ /**
+ * The cursor's surrounding characters were both deleted simultaneously.
+ * The cursor is automatically placed at the start of the deleted region.
+ */
+ void positionDeleted();
+
+ /**
+ * A character was inserted immediately before the cursor.
+ *
+ * Whether the char was inserted before or after this cursor depends on
+ * moveOnInsert():
+ * @li true -> the char was inserted before
+ * @li false -> the char was inserted after
+ */
+ void charInsertedAt();
+
+ /**
+ * The character immediately before the cursor was deleted.
+ */
+ void charDeletedBefore();
+
+ /**
+ * The character immediately after the cursor was deleted.
+ */
+ void charDeletedAfter();
+
+ //BEGIN METHODES TO CALL FROM KATE DOCUMENT TO KEEP CURSOR UP TO DATE
+ public:
+ void editTextInserted ( uint line, uint col, uint len);
+ void editTextRemoved ( uint line, uint col, uint len);
+
+ void editLineWrapped ( uint line, uint col, bool newLine = true );
+ void editLineUnWrapped ( uint line, uint col, bool removeLine = true, uint length = 0 );
+
+ void editLineInserted ( uint line );
+ void editLineRemoved ( uint line );
+ //END
+
+ private:
+ KateDocument *m_doc;
+ bool m_moveOnInsert : 1;
+ bool m_lineRemoved : 1;
+ bool m_privateCursor : 1;
+};
+
+/**
+ * Represents a range of text, from the start() to the end().
+ *
+ * Also tracks its position and emits useful signals.
+ */
+class KateSuperRange : public QObject, public KateRange
+{
+ friend class KateSuperRangeList;
+
+ Q_OBJECT
+
+public:
+ /// Determine how the range reacts to characters inserted immediately outside the range.
+ enum InsertBehaviour {
+ /// Don't expand to encapsulate new characters in either direction. This is the default.
+ DoNotExpand = 0,
+ /// Expand to encapsulate new characters to the left of the range.
+ ExpandLeft = 0x1,
+ /// Expand to encapsulate new characters to the right of the range.
+ ExpandRight = 0x2
+ };
+
+ /**
+ * Constructor. Takes posession of @p start and @p end.
+ */
+ KateSuperRange(KateSuperCursor* start, KateSuperCursor* end, QObject* parent = 0L, const char* name = 0L);
+ KateSuperRange(KateDocument* doc, const KateRange& range, QObject* parent = 0L, const char* name = 0L);
+ KateSuperRange(KateDocument* doc, const KateTextCursor& start, const KateTextCursor& end, QObject* parent = 0L, const char* name = 0L);
+
+ virtual ~KateSuperRange();
+
+ // fulfill KateRange requirements
+ virtual KateTextCursor& start();
+ virtual KateTextCursor& end();
+ virtual const KateTextCursor& start() const;
+ virtual const KateTextCursor& end() const;
+
+ void allowZeroLength(bool yes=true){m_allowZeroLength=yes;}
+ /**
+ * Returns the super start cursor.
+ */
+ KateSuperCursor& superStart();
+ const KateSuperCursor& superStart() const;
+
+ /**
+ * Returns the super end cursor.
+ */
+ KateSuperCursor& superEnd();
+ const KateSuperCursor& superEnd() const;
+
+ /**
+ * Returns how this range reacts to characters inserted immediately outside the range.
+ */
+ int behaviour() const;
+
+ /**
+ * Determine how the range should react to characters inserted immediately outside the range.
+ *
+ * TODO does this need a custom function to enable determining of the behavior based on the
+ * text that is inserted / deleted?
+ *
+ * @sa InsertBehaviour
+ */
+ void setBehaviour(int behaviour);
+
+ /**
+ * Start and end must be valid and start <= end.
+ */
+ virtual bool isValid() const;
+
+ /**
+ * This is for use where the ranges are used in a heirachy,
+ * ie. their parents are KateSuperRanges which completely
+ * encapsulate them.
+ *
+ * @todo constrain children when their position changes deliberately;
+ * eliminate() children when they are equivalent to their parents
+ *
+ * @returns true if the range contains the cursor and no children
+ * also contain it; false otherwise.
+ */
+ bool owns(const KateTextCursor& cursor) const;
+
+ /**
+ * Returns true if the range includes @p cursor 's character.
+ * Returns false if @p cursor == end().
+ */
+ bool includes(const KateTextCursor& cursor) const;
+
+ /**
+ * Returns true if the range includes @p line
+ */
+ bool includes(uint lineNum) const;
+
+ /**
+ * Returns true if the range totally encompasses @p line
+ */
+ bool includesWholeLine(uint lineNum) const;
+
+ /**
+ * Returns whether @p cursor is the site of a boundary of this range.
+ */
+ bool boundaryAt(const KateTextCursor& cursor) const;
+
+ /**
+ * Returns whether there is a boundary of this range on @p line.
+ */
+ bool boundaryOn(uint lineNum) const;
+
+signals:
+ /**
+ * More interesting signals that aren't worth implementing here:
+ * firstCharDeleted: start()::charDeleted()
+ * lastCharDeleted: end()::previousCharDeleted()
+ */
+
+ /**
+ * The range's position changed.
+ */
+ void positionChanged();
+
+ /**
+ * The range's position was unchanged.
+ */
+ void positionUnChanged();
+
+ /**
+ * The contents of the range changed.
+ */
+ void contentsChanged();
+
+ /**
+ * Either cursor's surrounding characters were both deleted.
+ */
+ void boundaryDeleted();
+
+ /**
+ * The range now contains no characters (ie. the start and end cursors are the same).
+ *
+ * To eliminate this range under different conditions, connect the other signal directly
+ * to this signal.
+ */
+ void eliminated();
+
+ /**
+ * Indicates the region needs re-drawing.
+ */
+ void tagRange(KateSuperRange* range);
+
+public slots:
+ void slotTagRange();
+
+private slots:
+ void slotEvaluateChanged();
+ void slotEvaluateUnChanged();
+
+private:
+ void init();
+ void evaluateEliminated();
+ void evaluatePositionChanged();
+
+ KateSuperCursor* m_start;
+ KateSuperCursor* m_end;
+ bool m_evaluate;
+ bool m_startChanged;
+ bool m_endChanged;
+ bool m_deleteCursors;
+ bool m_allowZeroLength;
+};
+
+class KateSuperCursorList : public QPtrList<KateSuperCursor>
+{
+protected:
+ virtual int compareItems(QPtrCollection::Item item1, QPtrCollection::Item item2);
+};
+
+class KateSuperRangeList : public QObject, public QPtrList<KateSuperRange>
+{
+ Q_OBJECT
+
+public:
+ /**
+ * @sa autoManage()
+ */
+ KateSuperRangeList(bool autoManage = true, QObject* parent = 0L, const char* name = 0L);
+
+ /**
+ * Semi-copy constructor.
+ *
+ * Does not copy auto-manage value, as that would make it too easy to perform
+ * double-deletions.
+ *
+ * Also, does not connect signals and slots to save time, as this is mainly
+ * used by the document itself while drawing (call connectAll() to re-constitute)
+ */
+ KateSuperRangeList(const QPtrList<KateSuperRange>& rangeList, QObject* parent = 0L, const char* name = 0L);
+
+ virtual ~KateSuperRangeList() {}
+ /**
+ * Append another list.
+ * If this object was created by the semi-copy constructor, it may not connect items
+ * (unless connectAll() has already been called), call connectAll().
+ */
+ void appendList(const QPtrList<KateSuperRange>& rangeList);
+
+ /**
+ * Connect items that are not connected. This only needs to be called once,
+ * and only if this was created with the semi-copy constructor.
+ */
+ void connectAll();
+
+ /**
+ * Override to emit rangeEliminated() signals.
+ */
+ virtual void clear();
+
+ /**
+ * Automanage is a combination of autodeleting of the objects and
+ * removing of any eliminated() ranges.
+ */
+ bool autoManage() const;
+
+ /**
+ * @sa autoManage()
+ */
+ void setAutoManage(bool autoManage);
+
+ /**
+ * This is just a straight-forward list so that there is no confusion about whether
+ * this list should be auto-managed (ie. it shouldn't, to avoid double deletions).
+ */
+ QPtrList<KateSuperRange> rangesIncluding(const KateTextCursor& cursor);
+ QPtrList<KateSuperRange> rangesIncluding(uint line);
+
+ /**
+ * @retval true if one of the ranges in the list includes @p cursor
+ * @retval false otherwise
+ */
+ bool rangesInclude(const KateTextCursor& cursor);
+
+ /**
+ * Construct a list of boundaries, and return the first, or 0L if there are none.
+ * If @p start is defined, the first boundary returned will be at or after @p start.
+ *
+ * @returns the first boundary location
+ */
+ KateSuperCursor* firstBoundary(const KateTextCursor* start = 0L);
+
+ /**
+ * @returns the next boundary, or 0L if there are no more.
+ */
+ KateSuperCursor* nextBoundary();
+
+ /**
+ * @returns the current boundary
+ */
+ KateSuperCursor* currentBoundary();
+
+signals:
+ /**
+ * The range now contains no characters (ie. the start and end cursors are the same).
+ * If autoManage() is true, the range will be deleted after the signal has processed.
+ */
+ void rangeEliminated(KateSuperRange* range);
+
+ /**
+ * There are no ranges left.
+ */
+ void listEmpty();
+
+ /**
+ * Connected to all ranges if connect()ed.
+ */
+ void tagRange(KateSuperRange* range);
+
+protected:
+ /**
+ * internal reimplementation
+ */
+ virtual int compareItems(QPtrCollection::Item item1, QPtrCollection::Item item2);
+
+ /**
+ * internal reimplementation
+ */
+ virtual QPtrCollection::Item newItem(QPtrCollection::Item d);
+
+private slots:
+ void slotEliminated();
+ void slotDeleted(QObject* range);
+
+private:
+ bool m_autoManage;
+ bool m_connect;
+
+ KateSuperCursorList m_columnBoundaries;
+ bool m_trackingBoundaries;
+};
+
+#endif
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katesyntaxdocument.cpp b/kate/part/katesyntaxdocument.cpp
new file mode 100644
index 000000000..e5c18c8ef
--- /dev/null
+++ b/kate/part/katesyntaxdocument.cpp
@@ -0,0 +1,475 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
+ Copyright (C) 2000 Scott Manson <sdmanson@alltel.net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "katesyntaxdocument.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <kdebug.h>
+#include <kstandarddirs.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kconfig.h>
+
+#include <qfile.h>
+
+KateSyntaxDocument::KateSyntaxDocument(bool force)
+ : QDomDocument()
+{
+ // Let's build the Mode List (katesyntaxhighlightingrc)
+ setupModeList(force);
+}
+
+KateSyntaxDocument::~KateSyntaxDocument()
+{
+ for (uint i=0; i < myModeList.size(); i++)
+ delete myModeList[i];
+}
+
+/** If the open hl file is different from the one needed, it opens
+ the new one and assign some other things.
+ identifier = File name and path of the new xml needed
+*/
+bool KateSyntaxDocument::setIdentifier(const QString& identifier)
+{
+ // if the current file is the same as the new one don't do anything.
+ if(currentFile != identifier)
+ {
+ // let's open the new file
+ QFile f( identifier );
+
+ if ( f.open(IO_ReadOnly) )
+ {
+ // Let's parse the contets of the xml file
+ /* The result of this function should be check for robustness,
+ a false returned means a parse error */
+ QString errorMsg;
+ int line, col;
+ bool success=setContent(&f,&errorMsg,&line,&col);
+
+ // Ok, now the current file is the pretended one (identifier)
+ currentFile = identifier;
+
+ // Close the file, is not longer needed
+ f.close();
+
+ if (!success)
+ {
+ KMessageBox::error(0L,i18n("<qt>The error <b>%4</b><br> has been detected in the file %1 at %2/%3</qt>").arg(identifier)
+ .arg(line).arg(col).arg(i18n("QXml",errorMsg.utf8())));
+ return false;
+ }
+ }
+ else
+ {
+ // Oh o, we couldn't open the file.
+ KMessageBox::error( 0L, i18n("Unable to open %1").arg(identifier) );
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * Jump to the next group, KateSyntaxContextData::currentGroup will point to the next group
+ */
+bool KateSyntaxDocument::nextGroup( KateSyntaxContextData* data)
+{
+ if(!data)
+ return false;
+
+ // No group yet so go to first child
+ if (data->currentGroup.isNull())
+ {
+ // Skip over non-elements. So far non-elements are just comments
+ QDomNode node = data->parent.firstChild();
+ while (node.isComment())
+ node = node.nextSibling();
+
+ data->currentGroup = node.toElement();
+ }
+ else
+ {
+ // common case, iterate over siblings, skipping comments as we go
+ QDomNode node = data->currentGroup.nextSibling();
+ while (node.isComment())
+ node = node.nextSibling();
+
+ data->currentGroup = node.toElement();
+ }
+
+ return !data->currentGroup.isNull();
+}
+
+/**
+ * Jump to the next item, KateSyntaxContextData::item will point to the next item
+ */
+bool KateSyntaxDocument::nextItem( KateSyntaxContextData* data)
+{
+ if(!data)
+ return false;
+
+ if (data->item.isNull())
+ {
+ QDomNode node = data->currentGroup.firstChild();
+ while (node.isComment())
+ node = node.nextSibling();
+
+ data->item = node.toElement();
+ }
+ else
+ {
+ QDomNode node = data->item.nextSibling();
+ while (node.isComment())
+ node = node.nextSibling();
+
+ data->item = node.toElement();
+ }
+
+ return !data->item.isNull();
+}
+
+/**
+ * This function is used to fetch the atributes of the tags of the item in a KateSyntaxContextData.
+ */
+QString KateSyntaxDocument::groupItemData( const KateSyntaxContextData* data, const QString& name){
+ if(!data)
+ return QString::null;
+
+ // If there's no name just return the tag name of data->item
+ if ( (!data->item.isNull()) && (name.isEmpty()))
+ {
+ return data->item.tagName();
+ }
+
+ // if name is not empty return the value of the attribute name
+ if (!data->item.isNull())
+ {
+ return data->item.attribute(name);
+ }
+
+ return QString::null;
+
+}
+
+QString KateSyntaxDocument::groupData( const KateSyntaxContextData* data,const QString& name)
+{
+ if(!data)
+ return QString::null;
+
+ if (!data->currentGroup.isNull())
+ {
+ return data->currentGroup.attribute(name);
+ }
+ else
+ {
+ return QString::null;
+ }
+}
+
+void KateSyntaxDocument::freeGroupInfo( KateSyntaxContextData* data)
+{
+ if (data)
+ delete data;
+}
+
+KateSyntaxContextData* KateSyntaxDocument::getSubItems(KateSyntaxContextData* data)
+{
+ KateSyntaxContextData *retval = new KateSyntaxContextData;
+
+ if (data != 0)
+ {
+ retval->parent = data->currentGroup;
+ retval->currentGroup = data->item;
+ }
+
+ return retval;
+}
+
+bool KateSyntaxDocument::getElement (QDomElement &element, const QString &mainGroupName, const QString &config)
+{
+ kdDebug(13010) << "Looking for \"" << mainGroupName << "\" -> \"" << config << "\"." << endl;
+
+ QDomNodeList nodes = documentElement().childNodes();
+
+ // Loop over all these child nodes looking for mainGroupName
+ for (unsigned int i=0; i<nodes.count(); i++)
+ {
+ QDomElement elem = nodes.item(i).toElement();
+ if (elem.tagName() == mainGroupName)
+ {
+ // Found mainGroupName ...
+ QDomNodeList subNodes = elem.childNodes();
+
+ // ... so now loop looking for config
+ for (unsigned int j=0; j<subNodes.count(); j++)
+ {
+ QDomElement subElem = subNodes.item(j).toElement();
+ if (subElem.tagName() == config)
+ {
+ // Found it!
+ element = subElem;
+ return true;
+ }
+ }
+
+ kdDebug(13010) << "WARNING: \""<< config <<"\" wasn't found!" << endl;
+ return false;
+ }
+ }
+
+ kdDebug(13010) << "WARNING: \""<< mainGroupName <<"\" wasn't found!" << endl;
+ return false;
+}
+
+/**
+ * Get the KateSyntaxContextData of the QDomElement Config inside mainGroupName
+ * KateSyntaxContextData::item will contain the QDomElement found
+ */
+KateSyntaxContextData* KateSyntaxDocument::getConfig(const QString& mainGroupName, const QString &config)
+{
+ QDomElement element;
+ if (getElement(element, mainGroupName, config))
+ {
+ KateSyntaxContextData *data = new KateSyntaxContextData;
+ data->item = element;
+ return data;
+ }
+ return 0;
+}
+
+/**
+ * Get the KateSyntaxContextData of the QDomElement Config inside mainGroupName
+ * KateSyntaxContextData::parent will contain the QDomElement found
+ */
+KateSyntaxContextData* KateSyntaxDocument::getGroupInfo(const QString& mainGroupName, const QString &group)
+{
+ QDomElement element;
+ if (getElement(element, mainGroupName, group+"s"))
+ {
+ KateSyntaxContextData *data = new KateSyntaxContextData;
+ data->parent = element;
+ return data;
+ }
+ return 0;
+}
+
+/**
+ * Returns a list with all the keywords inside the list type
+ */
+QStringList& KateSyntaxDocument::finddata(const QString& mainGroup, const QString& type, bool clearList)
+{
+ kdDebug(13010)<<"Create a list of keywords \""<<type<<"\" from \""<<mainGroup<<"\"."<<endl;
+ if (clearList)
+ m_data.clear();
+
+ for(QDomNode node = documentElement().firstChild(); !node.isNull(); node = node.nextSibling())
+ {
+ QDomElement elem = node.toElement();
+ if (elem.tagName() == mainGroup)
+ {
+ kdDebug(13010)<<"\""<<mainGroup<<"\" found."<<endl;
+ QDomNodeList nodelist1 = elem.elementsByTagName("list");
+
+ for (uint l=0; l<nodelist1.count(); l++)
+ {
+ if (nodelist1.item(l).toElement().attribute("name") == type)
+ {
+ kdDebug(13010)<<"List with attribute name=\""<<type<<"\" found."<<endl;
+ QDomNodeList childlist = nodelist1.item(l).toElement().childNodes();
+
+ for (uint i=0; i<childlist.count(); i++)
+ {
+ QString element = childlist.item(i).toElement().text().stripWhiteSpace();
+ if (element.isEmpty())
+ continue;
+#ifndef NDEBUG
+ if (i<6)
+ {
+ kdDebug(13010)<<"\""<<element<<"\" added to the list \""<<type<<"\""<<endl;
+ }
+ else if(i==6)
+ {
+ kdDebug(13010)<<"... The list continues ..."<<endl;
+ }
+#endif
+ m_data += element;
+ }
+
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ return m_data;
+}
+
+// Private
+/** Generate the list of hl modes, store them in myModeList
+ force: if true forces to rebuild the Mode List from the xml files (instead of katesyntax...rc)
+*/
+void KateSyntaxDocument::setupModeList (bool force)
+{
+ // If there's something in myModeList the Mode List was already built so, don't do it again
+ if (!myModeList.isEmpty())
+ return;
+
+ // We'll store the ModeList in katesyntaxhighlightingrc
+ KConfig config("katesyntaxhighlightingrc", false, false);
+
+ // figure our if the kate install is too new
+ config.setGroup ("General");
+ if (config.readNumEntry ("Version") > config.readNumEntry ("CachedVersion"))
+ {
+ config.writeEntry ("CachedVersion", config.readNumEntry ("Version"));
+ force = true;
+ }
+
+ // Let's get a list of all the xml files for hl
+ QStringList list = KGlobal::dirs()->findAllResources("data","katepart/syntax/*.xml",false,true);
+
+ // Let's iterate through the list and build the Mode List
+ for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it )
+ {
+ // Each file has a group called:
+ QString Group="Cache "+ *it;
+
+ // Let's go to this group
+ config.setGroup(Group);
+
+ // stat the file
+ struct stat sbuf;
+ memset (&sbuf, 0, sizeof(sbuf));
+ stat(QFile::encodeName(*it), &sbuf);
+
+ // If the group exist and we're not forced to read the xml file, let's build myModeList for katesyntax..rc
+ if (!force && config.hasGroup(Group) && (sbuf.st_mtime == config.readNumEntry("lastModified")))
+ {
+ // Let's make a new KateSyntaxModeListItem to instert in myModeList from the information in katesyntax..rc
+ KateSyntaxModeListItem *mli=new KateSyntaxModeListItem;
+ mli->name = config.readEntry("name");
+ mli->nameTranslated = i18n("Language",mli->name.utf8());
+ mli->section = i18n("Language Section",config.readEntry("section").utf8());
+ mli->mimetype = config.readEntry("mimetype");
+ mli->extension = config.readEntry("extension");
+ mli->version = config.readEntry("version");
+ mli->priority = config.readEntry("priority");
+ mli->author = config.readEntry("author");
+ mli->license = config.readEntry("license");
+ mli->hidden = config.readBoolEntry("hidden");
+ mli->identifier = *it;
+
+ // Apend the item to the list
+ myModeList.append(mli);
+ }
+ else
+ {
+ kdDebug (13010) << "UPDATE hl cache for: " << *it << endl;
+
+ // We're forced to read the xml files or the mode doesn't exist in the katesyntax...rc
+ QFile f(*it);
+
+ if (f.open(IO_ReadOnly))
+ {
+ // Ok we opened the file, let's read the contents and close the file
+ /* the return of setContent should be checked because a false return shows a parsing error */
+ QString errMsg;
+ int line, col;
+
+ bool success = setContent(&f,&errMsg,&line,&col);
+
+ f.close();
+
+ if (success)
+ {
+ QDomElement root = documentElement();
+
+ if (!root.isNull())
+ {
+ // If the 'first' tag is language, go on
+ if (root.tagName()=="language")
+ {
+ // let's make the mode list item.
+ KateSyntaxModeListItem *mli = new KateSyntaxModeListItem;
+
+ mli->name = root.attribute("name");
+ mli->section = root.attribute("section");
+ mli->mimetype = root.attribute("mimetype");
+ mli->extension = root.attribute("extensions");
+ mli->version = root.attribute("version");
+ mli->priority = root.attribute("priority");
+ mli->author = root.attribute("author");
+ mli->license = root.attribute("license");
+
+ QString hidden = root.attribute("hidden");
+ mli->hidden = (hidden == "true" || hidden == "TRUE");
+
+ mli->identifier = *it;
+
+ // Now let's write or overwrite (if force==true) the entry in katesyntax...rc
+ config.setGroup(Group);
+ config.writeEntry("name",mli->name);
+ config.writeEntry("section",mli->section);
+ config.writeEntry("mimetype",mli->mimetype);
+ config.writeEntry("extension",mli->extension);
+ config.writeEntry("version",mli->version);
+ config.writeEntry("priority",mli->priority);
+ config.writeEntry("author",mli->author);
+ config.writeEntry("license",mli->license);
+ config.writeEntry("hidden",mli->hidden);
+
+ // modified time to keep cache in sync
+ config.writeEntry("lastModified", sbuf.st_mtime);
+
+ // Now that the data is in the config file, translate section
+ mli->section = i18n("Language Section",mli->section.utf8());
+ mli->nameTranslated = i18n("Language",mli->name.utf8());
+
+ // Append the new item to the list.
+ myModeList.append(mli);
+ }
+ }
+ }
+ else
+ {
+ KateSyntaxModeListItem *emli=new KateSyntaxModeListItem;
+
+ emli->section=i18n("Errors!");
+ emli->mimetype="invalid_file/invalid_file";
+ emli->extension="invalid_file.invalid_file";
+ emli->version="1.";
+ emli->name=QString ("Error: %1").arg(*it); // internal
+ emli->nameTranslated=i18n("Error: %1").arg(*it); // translated
+ emli->identifier=(*it);
+
+ myModeList.append(emli);
+ }
+ }
+ }
+ }
+
+ // Syncronize with the file katesyntax...rc
+ config.sync();
+}
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katesyntaxdocument.h b/kate/part/katesyntaxdocument.h
new file mode 100644
index 000000000..1e5171390
--- /dev/null
+++ b/kate/part/katesyntaxdocument.h
@@ -0,0 +1,164 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
+ Copyright (C) 2000 Scott Manson <sdmanson@alltel.net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KATE_SYNTAXDOCUMENT_H__
+#define __KATE_SYNTAXDOCUMENT_H__
+
+#include <qdom.h>
+#include <qstringlist.h>
+
+/**
+ * Information about each syntax hl Mode
+ */
+class KateSyntaxModeListItem
+{
+ public:
+ QString name;
+ QString nameTranslated;
+ QString section;
+ QString mimetype;
+ QString extension;
+ QString identifier;
+ QString version;
+ QString priority;
+ QString author;
+ QString license;
+ bool hidden;
+};
+
+/**
+ * List of the KateSyntaxModeListItems holding all the syntax mode list items
+ */
+typedef QValueList<KateSyntaxModeListItem*> KateSyntaxModeList;
+
+/**
+ * Class holding the data around the current QDomElement
+ */
+class KateSyntaxContextData
+{
+ public:
+ QDomElement parent;
+ QDomElement currentGroup;
+ QDomElement item;
+};
+
+/**
+ * Store and manage the information about Syntax Highlighting.
+ */
+class KateSyntaxDocument : public QDomDocument
+{
+ public:
+ /**
+ * Constructor
+ * Sets the current file to nothing and build the ModeList (katesyntaxhighlightingrc)
+ * @param force fore the update of the hl cache
+ */
+ KateSyntaxDocument(bool force = false);
+
+ /**
+ * Desctructor
+ */
+ ~KateSyntaxDocument();
+
+ /**
+ * If the open hl file is different from the one needed, it opens
+ * the new one and assign some other things.
+ * @param identifier file name and path of the new xml needed
+ * @return success
+ */
+ bool setIdentifier(const QString& identifier);
+
+ /**
+ * Get the mode list
+ * @return mode list
+ */
+ const KateSyntaxModeList &modeList() { return myModeList; }
+
+ /**
+ * Jump to the next group, KateSyntaxContextData::currentGroup will point to the next group
+ * @param data context
+ * @return success
+ */
+ bool nextGroup(KateSyntaxContextData* data);
+
+ /**
+ * Jump to the next item, KateSyntaxContextData::item will point to the next item
+ * @param data context
+ * @return success
+ */
+ bool nextItem(KateSyntaxContextData* data);
+
+ /**
+ * This function is used to fetch the atributes of the tags.
+ */
+ QString groupItemData(const KateSyntaxContextData* data,const QString& name);
+ QString groupData(const KateSyntaxContextData* data,const QString& name);
+
+ void freeGroupInfo(KateSyntaxContextData* data);
+ KateSyntaxContextData* getSubItems(KateSyntaxContextData* data);
+
+ /**
+ * Get the KateSyntaxContextData of the DomElement Config inside mainGroupName
+ * It just fills KateSyntaxContextData::item
+ */
+ KateSyntaxContextData* getConfig(const QString& mainGroupName, const QString &config);
+
+ /**
+ * Get the KateSyntaxContextData of the QDomElement Config inside mainGroupName
+ * KateSyntaxContextData::parent will contain the QDomElement found
+ */
+ KateSyntaxContextData* getGroupInfo(const QString& mainGroupName, const QString &group);
+
+ /**
+ * Returns a list with all the keywords inside the list type
+ */
+ QStringList& finddata(const QString& mainGroup,const QString& type,bool clearList=true);
+
+ private:
+ /**
+ * Generate the list of hl modes, store them in myModeList
+ * @param force if true forces to rebuild the Mode List from the xml files (instead of katesyntax...rc)
+ */
+ void setupModeList(bool force);
+
+ /**
+ * Used by getConfig and getGroupInfo to traverse the xml nodes and
+ * evenually return the found element
+ */
+ bool getElement (QDomElement &element, const QString &mainGroupName, const QString &config);
+
+ /**
+ * List of mode items
+ */
+ KateSyntaxModeList myModeList;
+
+ /**
+ * current parsed filename
+ */
+ QString currentFile;
+
+ /**
+ * last found data out of the xml
+ */
+ QStringList m_data;
+};
+
+#endif
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katetemplatehandler.cpp b/kate/part/katetemplatehandler.cpp
new file mode 100644
index 000000000..3ca86ff70
--- /dev/null
+++ b/kate/part/katetemplatehandler.cpp
@@ -0,0 +1,342 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2004 Joseph Wenninger <jowenn@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#include "katetemplatehandler.h"
+#include "katetemplatehandler.moc"
+#include "katedocument.h"
+#include "katesupercursor.h"
+#include "katearbitraryhighlight.h"
+#include "kateview.h"
+#include <qregexp.h>
+#include <kdebug.h>
+#include <qvaluelist.h>
+
+KateTemplateHandler::KateTemplateHandler(
+ KateDocument *doc,
+ uint line, uint column,
+ const QString &templateString,
+ const QMap<QString, QString> &initialValues )
+ : QObject( doc )
+ , KateKeyInterceptorFunctor()
+ , m_doc( doc )
+ , m_currentTabStop( -1 )
+ , m_currentRange( 0 )
+ , m_initOk( false )
+ , m_recursion( false )
+{
+ connect( m_doc, SIGNAL( destroyed() ), this, SLOT( slotDocumentDestroyed() ) );
+ m_ranges = new KateSuperRangeList( false, this ); //false/*,this*/);
+
+ if ( !m_doc->setTabInterceptor( this ) )
+ {
+ deleteLater();
+ return ;
+ }
+
+ KateArbitraryHighlight *kah = doc->arbitraryHL();
+ /*KateArbitraryHighlightRange *hlr=new KateArbitraryHighlightRange(doc,KateTextCursor(line,column),
+ KateTextCursor(line,column+3));
+ hlr->setUnderline(true);
+ hlr->setOverline(true);
+ l->append(hlr);*/
+ QValueList<KateTemplateHandlerPlaceHolderInfo> buildList;
+ QRegExp rx( "([$%])\\{([^}\\s]+)\\}" );
+ rx.setMinimal( true );
+ int pos = 0;
+ int opos = 0;
+ QString insertString = templateString;
+
+ while ( pos >= 0 )
+ {
+ pos = rx.search( insertString, pos );
+
+ if ( pos > -1 )
+ {
+ if ( ( pos - opos ) > 0 )
+ {
+ if ( insertString[ pos - 1 ] == '\\' )
+ {
+ insertString.remove( pos - 1, 1 );
+ opos = pos;
+ continue;
+ }
+ }
+
+ QString placeholder = rx.cap( 2 );
+ QString value = initialValues[ placeholder ];
+
+ // don't add %{MACRO} to the tab navigation, unless there was not value
+ if ( rx.cap( 1 ) != "%" || placeholder == value )
+ buildList.append( KateTemplateHandlerPlaceHolderInfo( pos, value.length(), placeholder ) );
+
+ insertString.replace( pos, rx.matchedLength(), value );
+ pos += value.length();
+ opos = pos;
+ }
+ }
+
+ doc->editStart();
+
+ if ( !doc->insertText( line, column, insertString ) )
+ {
+ deleteLater();
+ doc->editEnd();
+ return ;
+ }
+
+ if ( buildList.isEmpty() )
+ {
+ m_initOk = true;
+ deleteLater();
+ doc->editEnd();
+ return ;
+ }
+
+ doc->undoSafePoint();
+ doc->editEnd();
+ generateRangeTable( line, column, insertString, buildList );
+ kah->addHighlightToDocument( m_ranges );
+
+ for ( KateSuperRangeList::const_iterator it = m_ranges->begin();it != m_ranges->end();++it )
+ {
+ m_doc->tagLines( ( *it ) ->start().line(), ( *it ) ->end().line() );
+ }
+
+ /* connect(doc,SIGNAL(charactersInteractivelyInserted(int ,int ,const QString&)),this,
+ SLOT(slotCharactersInteractivlyInserted(int,int,const QString&)));
+ connect(doc,SIGNAL(charactersSemiInteractivelyInserted(int ,int ,const QString&)),this,
+ SLOT(slotCharactersInteractivlyInserted(int,int,const QString&)));*/
+ connect( doc, SIGNAL( textInserted( int, int ) ), this, SLOT( slotTextInserted( int, int ) ) );
+ connect( doc, SIGNAL( aboutToRemoveText( const KateTextRange& ) ), this, SLOT( slotAboutToRemoveText( const KateTextRange& ) ) );
+ connect( doc, SIGNAL( textRemoved() ), this, SLOT( slotTextRemoved() ) );
+
+ ( *this ) ( Qt::Key_Tab );
+}
+
+KateTemplateHandler::~KateTemplateHandler()
+{
+ m_ranges->setAutoManage( true );
+
+ if ( m_doc )
+ {
+ m_doc->removeTabInterceptor( this );
+
+ for ( KateSuperRangeList::const_iterator it = m_ranges->begin();it != m_ranges->end();++it )
+ {
+ m_doc->tagLines( ( *it ) ->start().line(), ( *it ) ->end().line() );
+ }
+ }
+
+ m_ranges->clear();
+}
+
+void KateTemplateHandler::slotDocumentDestroyed() {m_doc = 0;}
+
+void KateTemplateHandler::generateRangeTable( uint insertLine, uint insertCol, const QString& insertString, const QValueList<KateTemplateHandlerPlaceHolderInfo> &buildList )
+{
+ uint line = insertLine;
+ uint col = insertCol;
+ uint colInText = 0;
+
+ for ( QValueList<KateTemplateHandlerPlaceHolderInfo>::const_iterator it = buildList.begin();it != buildList.end();++it )
+ {
+ KateTemplatePlaceHolder *ph = m_dict[ ( *it ).placeholder ];
+
+ if ( !ph )
+ {
+ ph = new KateTemplatePlaceHolder;
+ ph->isInitialValue = true;
+ ph->isCursor = ( ( *it ).placeholder == "cursor" );
+ m_dict.insert( ( *it ).placeholder, ph );
+
+ if ( !ph->isCursor ) m_tabOrder.append( ph );
+
+ ph->ranges.setAutoManage( false );
+ }
+
+ // FIXME handle space/tab replacement correctly make it use of the indenter
+ while ( colInText < ( *it ).begin )
+ {
+ ++col;
+
+ if ( insertString.at( colInText ) == '\n' )
+ {
+ col = 0;
+ line++;
+ }
+
+ ++colInText;
+ }
+
+ KateArbitraryHighlightRange *hlr = new KateArbitraryHighlightRange( m_doc, KateTextCursor( line, col ),
+ KateTextCursor( line, ( *it ).len + col ) );
+ colInText += ( *it ).len;
+ col += ( *it ).len;
+ hlr->allowZeroLength();
+ hlr->setUnderline( true );
+ hlr->setOverline( true );
+ //hlr->setBehaviour(KateSuperRange::ExpandRight);
+ ph->ranges.append( hlr );
+ m_ranges->append( hlr );
+ }
+
+ KateTemplatePlaceHolder *cursor = m_dict[ "cursor" ];
+
+ if ( cursor ) m_tabOrder.append( cursor );
+}
+
+void KateTemplateHandler::slotTextInserted( int line, int col )
+{
+#ifdef __GNUC__
+#warning FIXME undo/redo detection
+#endif
+
+ if ( m_recursion ) return ;
+
+ //if (m_editSessionNumber!=0) return; // assume that this is due an udno/redo operation right now
+ KateTextCursor cur( line, col );
+
+ if ( ( !m_currentRange ) ||
+ ( ( !m_currentRange->includes( cur ) ) && ( ! ( ( m_currentRange->start() == m_currentRange->end() ) && m_currentRange->end() == cur ) )
+ ) ) locateRange( cur );
+
+ if ( !m_currentRange ) return ;
+
+ KateTemplatePlaceHolder *ph = m_tabOrder.at( m_currentTabStop );
+
+ QString sourceText = m_doc->text ( m_currentRange->start().line(), m_currentRange->start().col(),
+ m_currentRange->end().line(), m_currentRange->end().col(), false );
+
+ ph->isInitialValue = false;
+ bool undoDontMerge = m_doc->m_undoDontMerge;
+ Q_ASSERT( m_doc->editSessionNumber == 0 );
+ m_recursion = true;
+
+ m_doc->editStart( /*false*/ );
+
+ for ( KateSuperRangeList::const_iterator it = ph->ranges.begin();it != ph->ranges.end();++it )
+ {
+ if ( ( *it ) == m_currentRange ) continue;
+
+ KateTextCursor start = ( *it ) ->start();
+ KateTextCursor end = ( *it ) ->end();
+ m_doc->removeText( start.line(), start.col(), end.line(), end.col(), false );
+ m_doc->insertText( start.line(), start.col(), sourceText );
+ }
+
+ m_doc->m_undoDontMerge = false;
+ m_doc->m_undoComplexMerge = true;
+ m_doc->undoSafePoint();
+ m_doc->editEnd();
+ m_doc->m_undoDontMerge = undoDontMerge;
+ m_recursion = false;
+
+ if ( ph->isCursor ) deleteLater();
+}
+
+void KateTemplateHandler::locateRange( const KateTextCursor& cursor )
+{
+ /* if (m_currentRange) {
+ m_doc->tagLines(m_currentRange->start().line(),m_currentRange->end().line());
+
+ }*/
+
+ for ( uint i = 0;i < m_tabOrder.count();i++ )
+ {
+ KateTemplatePlaceHolder *ph = m_tabOrder.at( i );
+
+ for ( KateSuperRangeList::const_iterator it = ph->ranges.begin();it != ph->ranges.end();++it )
+ {
+ if ( ( *it ) ->includes( cursor ) )
+ {
+ m_currentTabStop = i;
+ m_currentRange = ( *it );
+ //m_doc->tagLines(m_currentRange->start().line(),m_currentRange->end().line());
+ return ;
+ }
+ }
+
+ }
+
+ m_currentRange = 0;
+ /*while (m_ranges->count()>0)
+ delete (m_ranges->take(0));
+ disconnect(m_ranges,0,0,0);
+ delete m_ranges;*/
+ deleteLater();
+}
+
+
+bool KateTemplateHandler::operator() ( KKey key )
+{
+ if ( key==Qt::Key_Tab )
+ {
+ m_currentTabStop++;
+
+ if ( m_currentTabStop >= ( int ) m_tabOrder.count() )
+ m_currentTabStop = 0;
+ }
+ else
+ {
+ m_currentTabStop--;
+
+ if ( m_currentTabStop < 0 ) m_currentTabStop = m_tabOrder.count() - 1;
+ }
+
+ m_currentRange = m_tabOrder.at( m_currentTabStop ) ->ranges.at( 0 );
+
+ if ( m_tabOrder.at( m_currentTabStop ) ->isInitialValue )
+ {
+ m_doc->activeView()->setSelection( m_currentRange->start(), m_currentRange->end() );
+ }
+ else m_doc->activeView()->setSelection( m_currentRange->end(), m_currentRange->end() );
+
+ m_doc->activeView() ->setCursorPositionReal( m_currentRange->end().line(), m_currentRange->end().col() );
+ m_doc->activeView() ->tagLine( m_currentRange->end() );
+
+ return true;
+}
+
+void KateTemplateHandler::slotAboutToRemoveText( const KateTextRange &range )
+{
+ if ( m_recursion ) return ;
+
+ if ( m_currentRange && ( !m_currentRange->includes( range.start() ) ) ) locateRange( range.start() );
+
+ if ( m_currentRange != 0 )
+ {
+ if ( m_currentRange->end() <= range.end() ) return ;
+ }
+
+ if ( m_doc )
+ {
+ disconnect( m_doc, SIGNAL( textInserted( int, int ) ), this, SLOT( slotTextInserted( int, int ) ) );
+ disconnect( m_doc, SIGNAL( aboutToRemoveText( const KateTextRange& ) ), this, SLOT( slotAboutToRemoveText( const KateTextRange& ) ) );
+ disconnect( m_doc, SIGNAL( textRemoved() ), this, SLOT( slotTextRemoved() ) );
+ }
+
+ deleteLater();
+}
+
+void KateTemplateHandler::slotTextRemoved()
+{
+ if ( m_recursion ) return ;
+ if ( !m_currentRange ) return ;
+
+ slotTextInserted( m_currentRange->start().line(), m_currentRange->start().col() );
+}
+
diff --git a/kate/part/katetemplatehandler.h b/kate/part/katetemplatehandler.h
new file mode 100644
index 000000000..a2f41f518
--- /dev/null
+++ b/kate/part/katetemplatehandler.h
@@ -0,0 +1,68 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2004 Joseph Wenninger <jowenn@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef _KATE_TEMPLATE_HANDLER_H_
+#define _KATE_TEMPLATE_HANDLER_H_
+
+#include "katesupercursor.h"
+#include "katekeyinterceptorfunctor.h"
+#include <qobject.h>
+#include <qmap.h>
+#include <qdict.h>
+#include <qptrlist.h>
+#include <qstring.h>
+
+class KateDocument;
+
+class KateTemplateHandler: public QObject, public KateKeyInterceptorFunctor {
+ Q_OBJECT
+ public:
+ KateTemplateHandler(KateDocument *doc,uint line,uint column, const QString &templateString, const QMap<QString,QString> &initialValues);
+ virtual ~KateTemplateHandler();
+ inline bool initOk() {return m_initOk;}
+ virtual bool operator()(KKey key);
+ private:
+ struct KateTemplatePlaceHolder {
+ KateSuperRangeList ranges;
+ bool isCursor;
+ bool isInitialValue;
+ };
+ class KateTemplateHandlerPlaceHolderInfo{
+ public:
+ KateTemplateHandlerPlaceHolderInfo():begin(0),len(0),placeholder(""){};
+ KateTemplateHandlerPlaceHolderInfo(uint begin_,uint len_,const QString& placeholder_):begin(begin_),len(len_),placeholder(placeholder_){}
+ uint begin;
+ uint len;
+ QString placeholder;
+ };
+ class KateSuperRangeList *m_ranges;
+ class KateDocument *m_doc;
+ QPtrList<KateTemplatePlaceHolder> m_tabOrder;
+ QDict<KateTemplatePlaceHolder> m_dict;
+ void generateRangeTable(uint insertLine,uint insertCol, const QString& insertString, const QValueList<KateTemplateHandlerPlaceHolderInfo> &buildList);
+ int m_currentTabStop;
+ KateSuperRange *m_currentRange;
+ void locateRange(const KateTextCursor &cursor );
+ bool m_initOk;
+ bool m_recursion;
+ private slots:
+ void slotTextInserted(int,int);
+ void slotDocumentDestroyed();
+ void slotAboutToRemoveText(const KateTextRange &range);
+ void slotTextRemoved();
+};
+#endif
diff --git a/kate/part/katetextline.cpp b/kate/part/katetextline.cpp
new file mode 100644
index 000000000..250be5236
--- /dev/null
+++ b/kate/part/katetextline.cpp
@@ -0,0 +1,443 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2001-2003 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2002 Joseph Wenninger <jowenn@kde.org>
+
+ Based on:
+ KateTextLine : Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "katetextline.h"
+#include "katerenderer.h"
+
+#include <kglobal.h>
+
+#include <qregexp.h>
+
+KateTextLine::KateTextLine ()
+ : m_flags(0)
+{
+}
+
+KateTextLine::~KateTextLine()
+{
+}
+
+void KateTextLine::insertText (uint pos, uint insLen, const QChar *insText, uchar *insAttribs)
+{
+ // nothing to do
+ if (insLen == 0)
+ return;
+
+ // calc new textLen, store old
+ uint oldTextLen = m_text.length();
+ m_text.insert (pos, insText, insLen);
+ uint textLen = m_text.length();
+
+ // resize the array
+ m_attributes.resize (textLen);
+
+ // HA, insert behind text end, fill with spaces
+ if (pos >= oldTextLen)
+ {
+ for (uint z = oldTextLen; z < pos; z++)
+ m_attributes[z] = 0;
+ }
+ // HA, insert in text, move the old text behind pos
+ else if (oldTextLen > 0)
+ {
+ for (int z = oldTextLen -1; z >= (int) pos; z--)
+ m_attributes[z+insLen] = m_attributes[z];
+ }
+
+ // BUH, actually insert the new text
+ for (uint z = 0; z < insLen; z++)
+ {
+ if (insAttribs == 0)
+ m_attributes[z+pos] = 0;
+ else
+ m_attributes[z+pos] = insAttribs[z];
+ }
+}
+
+void KateTextLine::removeText (uint pos, uint delLen)
+{
+ // nothing to do
+ if (delLen == 0)
+ return;
+
+ uint textLen = m_text.length();
+
+ if (textLen == 0)
+ return; // uh, again nothing real to do ;)
+
+ if (pos >= textLen)
+ return;
+
+ if ((pos + delLen) > textLen)
+ delLen = textLen - pos;
+
+ // BU, MOVE THE OLD TEXT AROUND
+ for (uint z = pos; z < textLen - delLen; z++)
+ m_attributes[z] = m_attributes[z+delLen];
+
+ m_text.remove (pos, delLen);
+ m_attributes.resize (m_text.length ());
+}
+
+void KateTextLine::truncate(uint newLen)
+{
+ if (newLen < m_text.length())
+ {
+ m_text.truncate (newLen);
+ m_attributes.truncate (newLen);
+ }
+}
+
+int KateTextLine::nextNonSpaceChar(uint pos) const
+{
+ const uint len = m_text.length();
+ const QChar *unicode = m_text.unicode();
+
+ for(uint i = pos; i < len; i++)
+ {
+ if(!unicode[i].isSpace())
+ return i;
+ }
+
+ return -1;
+}
+
+int KateTextLine::previousNonSpaceChar(uint pos) const
+{
+ const int len = m_text.length();
+
+ if (pos >= (uint)len)
+ pos = len - 1;
+
+ const QChar *unicode = m_text.unicode();
+
+ for(int i = pos; i >= 0; i--)
+ {
+ if(!unicode[i].isSpace())
+ return i;
+ }
+
+ return -1;
+}
+
+int KateTextLine::firstChar() const
+{
+ return nextNonSpaceChar(0);
+}
+
+int KateTextLine::lastChar() const
+{
+ return previousNonSpaceChar(m_text.length() - 1);
+}
+
+const QChar *KateTextLine::firstNonSpace() const
+{
+ int first = firstChar();
+ return (first > -1) ? ((QChar*)m_text.unicode())+first : m_text.unicode();
+}
+
+uint KateTextLine::indentDepth (uint tabwidth) const
+{
+ uint d = 0;
+ const uint len = m_text.length();
+ const QChar *unicode = m_text.unicode();
+
+ for(uint i = 0; i < len; i++)
+ {
+ if(unicode[i].isSpace())
+ {
+ if (unicode[i] == QChar('\t'))
+ d += tabwidth - (d % tabwidth);
+ else
+ d++;
+ }
+ else
+ return d;
+ }
+
+ return d;
+}
+
+bool KateTextLine::stringAtPos(uint pos, const QString& match) const
+{
+ const uint len = m_text.length();
+ const uint matchlen = match.length();
+
+ if ((pos+matchlen) > len)
+ return false;
+
+ // (pos > len) in case the uint pos was assigned a signed -1, pos+matchlen can
+ // overflow again which (pos+matchlen > len) does not catch; see bugs #129263 and #129580
+ Q_ASSERT(pos < len);
+
+ const QChar *unicode = m_text.unicode();
+ const QChar *matchUnicode = match.unicode();
+
+ for (uint i=0; i < matchlen; i++)
+ if (unicode[i+pos] != matchUnicode[i])
+ return false;
+
+ return true;
+}
+
+bool KateTextLine::startingWith(const QString& match) const
+{
+ const uint matchlen = match.length();
+
+ if (matchlen > m_text.length())
+ return false;
+
+ const QChar *unicode = m_text.unicode();
+ const QChar *matchUnicode = match.unicode();
+
+ for (uint i=0; i < matchlen; i++)
+ if (unicode[i] != matchUnicode[i])
+ return false;
+
+ return true;
+}
+
+bool KateTextLine::endingWith(const QString& match) const
+{
+ const uint matchlen = match.length();
+
+ if (matchlen > m_text.length())
+ return false;
+
+ const QChar *unicode = m_text.unicode();
+ const QChar *matchUnicode = match.unicode();
+
+ uint start = m_text.length() - matchlen;
+ for (uint i=0; i < matchlen; i++)
+ if (unicode[start+i] != matchUnicode[i])
+ return false;
+
+ return true;
+}
+
+int KateTextLine::cursorX(uint pos, uint tabChars) const
+{
+ uint x = 0;
+
+ const uint n = kMin (pos, m_text.length());
+ const QChar *unicode = m_text.unicode();
+
+ for ( uint z = 0; z < n; z++)
+ {
+ if (unicode[z] == QChar('\t'))
+ x += tabChars - (x % tabChars);
+ else
+ x++;
+ }
+
+ return x;
+}
+
+
+uint KateTextLine::lengthWithTabs (uint tabChars) const
+{
+ uint x = 0;
+ const uint len = m_text.length();
+ const QChar *unicode = m_text.unicode();
+
+ for ( uint z = 0; z < len; z++)
+ {
+ if (unicode[z] == QChar('\t'))
+ x += tabChars - (x % tabChars);
+ else
+ x++;
+ }
+
+ return x;
+}
+
+bool KateTextLine::searchText (uint startCol, const QString &text, uint *foundAtCol, uint *matchLen, bool casesensitive, bool backwards)
+{
+ int index;
+
+ if (backwards)
+ {
+ int col = startCol;
+ uint l = text.length();
+ // allow finding the string ending at eol
+ if ( col == (int) m_text.length() ) ++startCol;
+
+ do {
+ index = m_text.findRev( text, col, casesensitive );
+ col--;
+ } while ( col >= 0 && l + index >= startCol );
+ }
+ else
+ index = m_text.find (text, startCol, casesensitive);
+
+ if (index > -1)
+ {
+ if (foundAtCol)
+ (*foundAtCol) = index;
+ if (matchLen)
+ (*matchLen)=text.length();
+ return true;
+ }
+
+ return false;
+}
+
+bool KateTextLine::searchText (uint startCol, const QRegExp &regexp, uint *foundAtCol, uint *matchLen, bool backwards)
+{
+ int index;
+
+ if (backwards)
+ {
+ int col = startCol;
+
+ // allow finding the string ending at eol
+ if ( col == (int) m_text.length() ) ++startCol;
+ do {
+ index = regexp.searchRev (m_text, col);
+ col--;
+ } while ( col >= 0 && regexp.matchedLength() + index >= (int)startCol );
+ }
+ else
+ index = regexp.search (m_text, startCol);
+
+ if (index > -1)
+ {
+ if (foundAtCol)
+ (*foundAtCol) = index;
+
+ if (matchLen)
+ (*matchLen)=regexp.matchedLength();
+ return true;
+ }
+
+ return false;
+}
+
+char *KateTextLine::dump (char *buf, bool withHighlighting) const
+{
+ uint l = m_text.length();
+ char f = m_flags;
+
+ if (!withHighlighting)
+ f = f | KateTextLine::flagNoOtherData;
+
+ memcpy(buf, (char *) &f, 1);
+ buf += 1;
+
+ memcpy(buf, &l, sizeof(uint));
+ buf += sizeof(uint);
+
+ memcpy(buf, (char *) m_text.unicode(), sizeof(QChar)*l);
+ buf += sizeof(QChar) * l;
+
+ if (!withHighlighting)
+ return buf;
+
+ memcpy(buf, (char *)m_attributes.data(), sizeof(uchar) * l);
+ buf += sizeof (uchar) * l;
+
+ uint lctx = m_ctx.size();
+ uint lfold = m_foldingList.size();
+ uint lind = m_indentationDepth.size();
+
+ memcpy(buf, &lctx, sizeof(uint));
+ buf += sizeof(uint);
+
+ memcpy(buf, &lfold, sizeof(uint));
+ buf += sizeof(uint);
+
+ memcpy(buf, &lind, sizeof(uint));
+ buf += sizeof(uint);
+
+ memcpy(buf, (char *)m_ctx.data(), sizeof(short) * lctx);
+ buf += sizeof (short) * lctx;
+
+ memcpy(buf, (char *)m_foldingList.data(), sizeof(uint)*lfold);
+ buf += sizeof (uint) * lfold;
+
+ memcpy(buf, (char *)m_indentationDepth.data(), sizeof(unsigned short) * lind);
+ buf += sizeof (unsigned short) * lind;
+
+ return buf;
+}
+
+char *KateTextLine::restore (char *buf)
+{
+ uint l = 0;
+ char f = 0;
+
+ memcpy((char *) &f, buf, 1);
+ buf += 1;
+
+ // text + context length read
+ memcpy((char *) &l, buf, sizeof(uint));
+ buf += sizeof(uint);
+
+ // text + attributes
+ m_text.setUnicode ((QChar *) buf, l);
+ buf += sizeof(QChar) * l;
+
+ // we just restore a KateTextLine from a buffer first time
+ if (f & KateTextLine::flagNoOtherData)
+ {
+ m_flags = 0;
+
+ if (f & KateTextLine::flagAutoWrapped)
+ m_flags = m_flags | KateTextLine::flagAutoWrapped;
+
+ // fill with clean empty attribs !
+ m_attributes.fill (0, l);
+
+ return buf;
+ }
+ else
+ m_flags = f;
+
+ m_attributes.duplicate ((uchar *) buf, l);
+ buf += sizeof(uchar) * l;
+
+ uint lctx = 0;
+ uint lfold = 0;
+ uint lind = 0;
+
+ memcpy((char *) &lctx, buf, sizeof(uint));
+ buf += sizeof(uint);
+
+ memcpy((char *) &lfold, buf, sizeof(uint));
+ buf += sizeof(uint);
+
+ memcpy((char *) &lind, buf, sizeof(uint));
+ buf += sizeof(uint);
+
+ m_ctx.duplicate ((short *) buf, lctx);
+ buf += sizeof(short) * lctx;
+
+ m_foldingList.duplicate ((uint *) buf, lfold);
+ buf += sizeof(uint)*lfold;
+
+ m_indentationDepth.duplicate ((unsigned short *) buf, lind);
+ buf += sizeof(unsigned short) * lind;
+
+ return buf;
+}
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/katetextline.h b/kate/part/katetextline.h
new file mode 100644
index 000000000..9a6ed5e4d
--- /dev/null
+++ b/kate/part/katetextline.h
@@ -0,0 +1,456 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2001-2003 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2002 Joseph Wenninger <jowenn@kde.org>
+
+ Based on:
+ KateTextLine : Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KATE_TEXTLINE_H__
+#define __KATE_TEXTLINE_H__
+
+#include <ksharedptr.h>
+
+#include <qmemarray.h>
+#include <qstring.h>
+
+class KateRenderer;
+class QTextStream;
+
+/**
+ * The KateTextLine represents a line of text. A text line that contains the
+ * text, an attribute for each character, an attribute for the free space
+ * behind the last character and a context number for the syntax highlight.
+ * The attribute stores the index to a table that contains fonts and colors
+ * and also if a character is selected.
+ */
+class KateTextLine : public KShared
+{
+ public:
+ /**
+ * Define a Shared-Pointer type
+ */
+ typedef KSharedPtr<KateTextLine> Ptr;
+
+ public:
+ /**
+ * Used Flags
+ */
+ enum Flags
+ {
+ flagNoOtherData = 1, // ONLY INTERNAL USE, NEVER EVER SET THAT !!!!
+ flagHlContinue = 2,
+ flagAutoWrapped = 4,
+ flagFoldingColumnsOutdated = 8,
+ flagNoIndentationBasedFolding = 16,
+ flagNoIndentationBasedFoldingAtStart = 32
+ };
+
+ public:
+ /**
+ * Constructor
+ * Creates an empty text line with given attribute and syntax highlight
+ * context
+ */
+ KateTextLine ();
+
+ /**
+ * Destructor
+ */
+ ~KateTextLine ();
+
+ /**
+ * Methods to get data
+ */
+ public:
+ /**
+ * Set the flag that only positions have changed, not folding region begins/ends themselve
+ */
+ inline void setFoldingColumnsOutdated(bool set) { if (set) m_flags |= KateTextLine::flagFoldingColumnsOutdated; else m_flags&=
+ (~KateTextLine::flagFoldingColumnsOutdated);}
+
+ /**
+ * folding columns outdated ?
+ * @return folding columns outdated?
+ */
+ inline bool foldingColumnsOutdated() { return m_flags & KateTextLine::flagFoldingColumnsOutdated; }
+
+
+ /**
+ * Returns the length
+ * @return length of text in line
+ */
+ inline uint length() const { return m_text.length(); }
+
+ /**
+ * has the line the hl continue flag set
+ * @return hl continue set?
+ */
+ inline bool hlLineContinue () const { return m_flags & KateTextLine::flagHlContinue; }
+
+ /**
+ * was this line automagically wrapped
+ * @return line auto-wrapped
+ */
+ inline bool isAutoWrapped () const { return m_flags & KateTextLine::flagAutoWrapped; }
+
+ /**
+ * Returns the position of the first non-whitespace character
+ * @return position of first non-whitespace char or -1 if there is none
+ */
+ int firstChar() const;
+
+ /**
+ * Returns the position of the last non-whitespace character
+ * @return position of last non-whitespace char or -1 if there is none
+ */
+ int lastChar() const;
+
+ /**
+ * Find the position of the next char that is not a space.
+ * @param pos Column of the character which is examined first.
+ * @return True if the specified or a following character is not a space
+ * Otherwise false.
+ */
+ int nextNonSpaceChar(uint pos) const;
+
+ /**
+ * Find the position of the previous char that is not a space.
+ * @param pos Column of the character which is examined first.
+ * @return The position of the first none-whitespace character preceeding pos,
+ * or -1 if none is found.
+ */
+ int previousNonSpaceChar(uint pos) const;
+
+ /**
+ * Gets the char at the given position
+ * @param pos position
+ * @return character at the given position or QChar::null if position is
+ * beyond the length of the string
+ */
+ inline QChar getChar (uint pos) const { return m_text[pos]; }
+
+ /**
+ * Gets the text as a unicode representation
+ * @return text of this line as QChar array
+ */
+ inline const QChar *text() const { return m_text.unicode(); }
+
+ /**
+ * Highlighting array
+ * The size of this is string().length()
+ *
+ * This contains the index for the attributes (so you can only
+ * have a maximum of 2^8 different highlighting styles in a document)
+ *
+ * To turn this into actual attributes (bold, green, etc),
+ * you need to feed these values into KRenderer::attributes
+ *
+ * e.g: m_renderer->attributes(attributes[3]);
+ *
+ * @return hl-attributes array
+ */
+ inline uchar *attributes () const { return m_attributes.data(); }
+
+ /**
+ * Gets a QString
+ * @return text of line as QString reference
+ */
+ inline const QString& string() const { return m_text; }
+
+ /**
+ * Gets a substring.
+ * @param startCol start column of substring
+ * @param length length of substring
+ * @return wanted substring
+ */
+ inline QString string(uint startCol, uint length) const
+ { return m_text.mid(startCol, length); }
+
+ /**
+ * Gets a null terminated pointer to first non space char
+ * @return array of QChars starting at first non-whitespace char
+ */
+ const QChar *firstNonSpace() const;
+
+ /**
+ * indentation depth of this line
+ * @param tabwidth width of the tabulators
+ * @return indentation width
+ */
+ uint indentDepth (uint tabwidth) const;
+
+ /**
+ * Returns the x position of the cursor at the given position, which
+ * depends on the number of tab characters
+ * @param pos position in chars
+ * @param tabChars tabulator width in chars
+ * @return position with tabulators calculated
+ */
+ int cursorX(uint pos, uint tabChars) const;
+
+ /**
+ * Returns the text length with tabs calced in
+ * @param tabChars tabulator width in chars
+ * @return text length
+ */
+ uint lengthWithTabs (uint tabChars) const;
+
+ /**
+ * Can we find the given string at the given position
+ * @param pos startpostion of given string
+ * @param match string to match at given pos
+ * @return did the string match?
+ */
+ bool stringAtPos(uint pos, const QString& match) const;
+
+ /**
+ * Is the line starting with the given string
+ * @param match string to test
+ * @return does line start with given string?
+ */
+ bool startingWith(const QString& match) const;
+
+ /**
+ * Is the line ending with the given string
+ * @param match string to test
+ * @return does the line end with given string?
+ */
+ bool endingWith(const QString& match) const;
+
+ /**
+ * search given string
+ * @param startCol column to start search
+ * @param text string to search for
+ * @param foundAtCol column where text was found
+ * @param matchLen length of matching
+ * @param casesensitive should search be case-sensitive
+ * @param backwards search backwards?
+ * @return string found?
+ */
+ bool searchText (uint startCol, const QString &text,
+ uint *foundAtCol, uint *matchLen,
+ bool casesensitive = true,
+ bool backwards = false);
+
+ /**
+ * search given regexp
+ * @param startCol column to start search
+ * @param regexp regex to search for
+ * @param foundAtCol column where text was found
+ * @param matchLen length of matching
+ * @param backwards search backwards?
+ * @return regexp found?
+ */
+ bool searchText (uint startCol, const QRegExp &regexp,
+ uint *foundAtCol, uint *matchLen,
+ bool backwards = false);
+
+ /**
+ * Gets the attribute at the given position
+ * use KRenderer::attributes to get the KTextAttribute for this.
+ *
+ * @param pos position of attribute requested
+ * @return value of attribute
+ * @see attributes
+ */
+ inline uchar attribute (uint pos) const
+ {
+ if (pos < m_attributes.size()) return m_attributes[pos];
+ return 0;
+ }
+
+ /**
+ * context stack
+ * @return context stack
+ */
+ inline const QMemArray<short> &ctxArray () const { return m_ctx; };
+
+ /**
+ * @return true if any context at the line end has the noIndentBasedFolding flag set
+ */
+ inline const bool noIndentBasedFolding() const { return m_flags & KateTextLine::flagNoIndentationBasedFolding; };
+ inline const bool noIndentBasedFoldingAtStart() const { return m_flags & KateTextLine::flagNoIndentationBasedFoldingAtStart; };
+ /**
+ * folding list
+ * @return folding array
+ */
+ inline const QMemArray<uint> &foldingListArray () const { return m_foldingList; };
+
+ /**
+ * indentation stack
+ * @return indentation array
+ */
+ inline const QMemArray<unsigned short> &indentationDepthArray () const { return m_indentationDepth; };
+
+ /**
+ * insert text into line
+ * @param pos insert position
+ * @param insLen insert length
+ * @param insText text to insert
+ * @param insAttribs attributes for the insert text
+ */
+ void insertText (uint pos, uint insLen, const QChar *insText, uchar *insAttribs = 0);
+
+ /**
+ * remove text at given position
+ * @param pos start position of remove
+ * @param delLen length to remove
+ */
+ void removeText (uint pos, uint delLen);
+
+ /**
+ * Truncates the textline to the new length
+ * @param newLen new length of line
+ */
+ void truncate(uint newLen);
+
+ /**
+ * set hl continue flag
+ * @param cont continue flag?
+ */
+ inline void setHlLineContinue (bool cont)
+ {
+ if (cont) m_flags = m_flags | KateTextLine::flagHlContinue;
+ else m_flags = m_flags & ~ KateTextLine::flagHlContinue;
+ }
+
+ /**
+ * auto-wrapped
+ * @param wrapped line was wrapped?
+ */
+ inline void setAutoWrapped (bool wrapped)
+ {
+ if (wrapped) m_flags = m_flags | KateTextLine::flagAutoWrapped;
+ else m_flags = m_flags & ~ KateTextLine::flagAutoWrapped;
+ }
+
+ /**
+ * Sets the syntax highlight context number
+ * @param val new context array
+ */
+ inline void setContext (QMemArray<short> &val) { m_ctx.assign (val); }
+
+ /**
+ * sets if for the next line indent based folding should be disabled
+ */
+ inline void setNoIndentBasedFolding(bool val)
+ {
+ if (val) m_flags = m_flags | KateTextLine::flagNoIndentationBasedFolding;
+ else m_flags = m_flags & ~ KateTextLine::flagNoIndentationBasedFolding;
+ }
+
+ inline void setNoIndentBasedFoldingAtStart(bool val)
+ {
+ if (val) m_flags = m_flags | KateTextLine::flagNoIndentationBasedFoldingAtStart;
+ else m_flags = m_flags & ~ KateTextLine::flagNoIndentationBasedFoldingAtStart;
+ }
+
+ /**
+ * update folding list
+ * @param val new folding list
+ */
+ inline void setFoldingList (QMemArray<uint> &val) { m_foldingList.assign (val); m_foldingList.detach(); }
+
+ /**
+ * update indentation stack
+ * @param val new indentation stack
+ */
+ inline void setIndentationDepth (QMemArray<unsigned short> &val) { m_indentationDepth.assign (val); }
+
+ /**
+ * Methodes for dump/restore of the line in the buffer
+ */
+ public:
+ /**
+ * Dumpsize in bytes
+ * @param withHighlighting should we dump the hl, too?
+ * @return size of line for dumping
+ */
+ inline uint dumpSize (bool withHighlighting) const
+ {
+ return ( 1
+ + sizeof(uint)
+ + (m_text.length() * sizeof(QChar))
+ + ( withHighlighting ?
+ ( (3 * sizeof(uint))
+ + (m_text.length() * sizeof(uchar))
+ + (m_ctx.size() * sizeof(short))
+ + (m_foldingList.size() * sizeof(uint))
+ + (m_indentationDepth.size() * sizeof(unsigned short))
+ ) : 0
+ )
+ );
+ }
+
+ /**
+ * Dumps the line to *buf and counts buff dumpSize bytes up
+ * as return value
+ * @param buf buffer to dump to
+ * @param withHighlighting dump hl data, too?
+ * @return buffer index after dumping
+ */
+ char *dump (char *buf, bool withHighlighting) const;
+
+ /**
+ * Restores the line from *buf and counts buff dumpSize bytes up
+ * as return value
+ * @param buf buffer to restore from
+ * @return buffer index after restoring
+ */
+ char *restore (char *buf);
+
+ /**
+ * REALLY PRIVATE ;) please no new friend classes
+ */
+ private:
+ /**
+ * text of line as unicode
+ */
+ QString m_text;
+
+ /**
+ * array of highlighting attributes
+ * This is exactly the same size as m_text.length()
+ * Each letter in m_text has a uchar attribute
+ */
+ QMemArray<uchar> m_attributes;
+
+ /**
+ * context stack
+ */
+ QMemArray<short> m_ctx;
+
+ /**
+ * list of folding starts/ends
+ */
+ QMemArray<uint> m_foldingList;
+
+ /**
+ * indentation stack
+ */
+ QMemArray<unsigned short> m_indentationDepth;
+
+ /**
+ * flags
+ */
+ uchar m_flags;
+};
+
+#endif
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/kateundo.cpp b/kate/part/kateundo.cpp
new file mode 100644
index 000000000..0f8d3599f
--- /dev/null
+++ b/kate/part/kateundo.cpp
@@ -0,0 +1,392 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org>
+ Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kateundo.h"
+
+#include "katedocument.h"
+#include "kateview.h"
+#include "katecursor.h"
+
+/**
+ * Private class, only for KateUndoGroup, no need to use it elsewhere
+ */
+ class KateUndo
+{
+ public:
+ /**
+ * Constructor
+ * @param type undo item type
+ * @param line line affected
+ * @param col start column
+ * @param len lenght of change
+ * @param text text removed/inserted
+ */
+ KateUndo (KateUndoGroup::UndoType type, uint line, uint col, uint len, const QString &text);
+
+ /**
+ * Destructor
+ */
+ ~KateUndo ();
+
+ public:
+ /**
+ * Invalid examples: insert / remove 0 length text
+ * I could probably fix this in KateDocument, but it's more work there
+ * (and probably better here too)
+ * @return validity
+ */
+ bool isValid();
+
+ /**
+ * merge an undo item
+ * Saves a bit of memory and potentially many calls when undo/redoing.
+ * @param u undo item to merge
+ * @return success
+ */
+ bool merge(KateUndo* u);
+
+ /**
+ * undo this item at given doc
+ * @param doc document
+ */
+ void undo (KateDocument *doc);
+
+ /**
+ * redo this item at given doc
+ * @param doc document
+ */
+ void redo (KateDocument *doc);
+
+ /**
+ * The cursor before the action took place
+ */
+ KateTextCursor cursorBefore() const;
+
+ /**
+ * The cursor after the action took place
+ */
+ KateTextCursor cursorAfter() const;
+
+ /**
+ * type of item
+ * @return type
+ */
+ inline KateUndoGroup::UndoType type() const { return m_type; }
+
+ /**
+ * line of changes
+ * @return line
+ */
+ inline uint line () const { return m_line; }
+
+ /**
+ * startcol of changes
+ * @return column
+ */
+ inline uint col () const { return m_col; }
+
+ /**
+ * length of changes
+ * @return length
+ */
+ inline uint len() const { return m_len; }
+
+ /**
+ * text inserted/removed
+ * @return text
+ */
+ inline const QString& text() const { return m_text; };
+
+ private:
+ /**
+ * type
+ */
+ KateUndoGroup::UndoType m_type;
+
+ /**
+ * line
+ */
+ uint m_line;
+
+ /**
+ * column
+ */
+ uint m_col;
+
+ /**
+ * length
+ */
+ uint m_len;
+
+ /**
+ * text
+ */
+ QString m_text;
+};
+
+KateUndo::KateUndo (KateUndoGroup::UndoType type, uint line, uint col, uint len, const QString &text)
+: m_type (type),
+ m_line (line),
+ m_col (col),
+ m_len (len),
+ m_text (text)
+{
+}
+
+KateUndo::~KateUndo ()
+{
+}
+
+bool KateUndo::isValid()
+{
+ if (m_type == KateUndoGroup::editInsertText || m_type == KateUndoGroup::editRemoveText)
+ if (len() == 0)
+ return false;
+
+ return true;
+}
+
+bool KateUndo::merge(KateUndo* u)
+{
+ if (m_type != u->type())
+ return false;
+
+ if (m_type == KateUndoGroup::editInsertText
+ && m_line == u->line()
+ && (m_col + m_len) == u->col())
+ {
+ m_text += u->text();
+ m_len += u->len();
+ return true;
+ }
+ else if (m_type == KateUndoGroup::editRemoveText
+ && m_line == u->line()
+ && m_col == (u->col() + u->len()))
+ {
+ m_text.prepend(u->text());
+ m_col = u->col();
+ m_len += u->len();
+ return true;
+ }
+
+ return false;
+}
+
+void KateUndo::undo (KateDocument *doc)
+{
+ if (m_type == KateUndoGroup::editInsertText)
+ {
+ doc->editRemoveText (m_line, m_col, m_len);
+ }
+ else if (m_type == KateUndoGroup::editRemoveText)
+ {
+ doc->editInsertText (m_line, m_col, m_text);
+ }
+ else if (m_type == KateUndoGroup::editWrapLine)
+ {
+ doc->editUnWrapLine (m_line, (m_text == "1"), m_len);
+ }
+ else if (m_type == KateUndoGroup::editUnWrapLine)
+ {
+ doc->editWrapLine (m_line, m_col, (m_text == "1"));
+ }
+ else if (m_type == KateUndoGroup::editInsertLine)
+ {
+ doc->editRemoveLine (m_line);
+ }
+ else if (m_type == KateUndoGroup::editRemoveLine)
+ {
+ doc->editInsertLine (m_line, m_text);
+ }
+ else if (m_type == KateUndoGroup::editMarkLineAutoWrapped)
+ {
+ doc->editMarkLineAutoWrapped (m_line, m_col == 0);
+ }
+}
+
+void KateUndo::redo (KateDocument *doc)
+{
+ if (m_type == KateUndoGroup::editRemoveText)
+ {
+ doc->editRemoveText (m_line, m_col, m_len);
+ }
+ else if (m_type == KateUndoGroup::editInsertText)
+ {
+ doc->editInsertText (m_line, m_col, m_text);
+ }
+ else if (m_type == KateUndoGroup::editUnWrapLine)
+ {
+ doc->editUnWrapLine (m_line, (m_text == "1"), m_len);
+ }
+ else if (m_type == KateUndoGroup::editWrapLine)
+ {
+ doc->editWrapLine (m_line, m_col, (m_text == "1"));
+ }
+ else if (m_type == KateUndoGroup::editRemoveLine)
+ {
+ doc->editRemoveLine (m_line);
+ }
+ else if (m_type == KateUndoGroup::editInsertLine)
+ {
+ doc->editInsertLine (m_line, m_text);
+ }
+ else if (m_type == KateUndoGroup::editMarkLineAutoWrapped)
+ {
+ doc->editMarkLineAutoWrapped (m_line, m_col == 1);
+ }
+}
+
+KateTextCursor KateUndo::cursorBefore() const
+{
+ if (m_type == KateUndoGroup::editInsertLine || m_type == KateUndoGroup::editUnWrapLine)
+ return KateTextCursor(m_line+1, m_col);
+ else if (m_type == KateUndoGroup::editRemoveText)
+ return KateTextCursor(m_line, m_col+m_len);
+
+ return KateTextCursor(m_line, m_col);
+}
+
+KateTextCursor KateUndo::cursorAfter() const
+{
+ if (m_type == KateUndoGroup::editRemoveLine || m_type == KateUndoGroup::editWrapLine)
+ return KateTextCursor(m_line+1, m_col);
+ else if (m_type == KateUndoGroup::editInsertText)
+ return KateTextCursor(m_line, m_col+m_len);
+
+ return KateTextCursor(m_line, m_col);
+}
+
+KateUndoGroup::KateUndoGroup (KateDocument *doc)
+: m_doc (doc),m_safePoint(false)
+{
+ m_items.setAutoDelete (true);
+}
+
+KateUndoGroup::~KateUndoGroup ()
+{
+}
+
+void KateUndoGroup::undo ()
+{
+ if (m_items.count() == 0)
+ return;
+
+ m_doc->editStart (false);
+
+ for (KateUndo* u = m_items.last(); u; u = m_items.prev())
+ u->undo(m_doc);
+
+ if (m_doc->activeView())
+ {
+ for (uint z=0; z < m_items.count(); z++)
+ if (m_items.at(z)->type() != KateUndoGroup::editMarkLineAutoWrapped)
+ {
+ m_doc->activeView()->editSetCursor (m_items.at(z)->cursorBefore());
+ break;
+ }
+ }
+
+ m_doc->editEnd ();
+}
+
+void KateUndoGroup::redo ()
+{
+ if (m_items.count() == 0)
+ return;
+
+ m_doc->editStart (false);
+
+ for (KateUndo* u = m_items.first(); u; u = m_items.next())
+ u->redo(m_doc);
+
+ if (m_doc->activeView())
+ {
+ for (uint z=0; z < m_items.count(); z++)
+ if (m_items.at(z)->type() != KateUndoGroup::editMarkLineAutoWrapped)
+ {
+ m_doc->activeView()->editSetCursor (m_items.at(z)->cursorAfter());
+ break;
+ }
+ }
+
+ m_doc->editEnd ();
+}
+
+void KateUndoGroup::addItem (KateUndoGroup::UndoType type, uint line, uint col, uint len, const QString &text)
+{
+ addItem(new KateUndo(type, line, col, len, text));
+}
+
+void KateUndoGroup::addItem(KateUndo* u)
+{
+ if (!u->isValid())
+ delete u;
+ else if (m_items.last() && m_items.last()->merge(u))
+ delete u;
+ else
+ m_items.append(u);
+}
+
+bool KateUndoGroup::merge(KateUndoGroup* newGroup,bool complex)
+{
+ if (m_safePoint) return false;
+ if (newGroup->isOnlyType(singleType()) || complex) {
+ // Take all of its items first -> last
+ KateUndo* u = newGroup->m_items.take(0);
+ while (u) {
+ addItem(u);
+ u = newGroup->m_items.take(0);
+ }
+ if (newGroup->m_safePoint) safePoint();
+ return true;
+ }
+ return false;
+}
+
+void KateUndoGroup::safePoint (bool safePoint) {
+ m_safePoint=safePoint;
+}
+
+KateUndoGroup::UndoType KateUndoGroup::singleType()
+{
+ KateUndoGroup::UndoType ret = editInvalid;
+
+ for (KateUndo* u = m_items.first(); u; u = m_items.next()) {
+ if (ret == editInvalid)
+ ret = u->type();
+ else if (ret != u->type())
+ return editInvalid;
+ }
+
+ return ret;
+}
+
+bool KateUndoGroup::isOnlyType(KateUndoGroup::UndoType type)
+{
+ if (type == editInvalid) return false;
+
+ for (KateUndo* u = m_items.first(); u; u = m_items.next())
+ if (u->type() != type)
+ return false;
+
+ return true;
+}
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/kateundo.h b/kate/part/kateundo.h
new file mode 100644
index 000000000..66905f5cd
--- /dev/null
+++ b/kate/part/kateundo.h
@@ -0,0 +1,141 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org>
+ Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef kate_undo_h
+#define kate_undo_h
+
+#include <qptrlist.h>
+#include <qstring.h>
+
+class KateDocument;
+class KateUndo;
+
+/**
+ * Class to manage a group of undo items
+ */
+class KateUndoGroup
+{
+ public:
+ /**
+ * Constructor
+ * @param doc document to belong to
+ */
+ KateUndoGroup (KateDocument *doc);
+
+ /**
+ * Destructor
+ */
+ ~KateUndoGroup ();
+
+ public:
+ /**
+ * Undo the contained undo items
+ */
+ void undo ();
+
+ /**
+ * Redo the contained undo items
+ */
+ void redo ();
+
+ public:
+ /**
+ * Types for undo items
+ */
+ enum UndoType
+ {
+ editInsertText,
+ editRemoveText,
+ editWrapLine,
+ editUnWrapLine,
+ editInsertLine,
+ editRemoveLine,
+ editMarkLineAutoWrapped,
+ editInvalid
+ };
+
+ /**
+ * add an item to the group
+ * @param type undo item type
+ * @param line line affected
+ * @param col start column
+ * @param len lenght of change
+ * @param text text removed/inserted
+ */
+ void addItem (KateUndoGroup::UndoType type, uint line, uint col, uint len, const QString &text);
+
+ /**
+ * merge this group with an other
+ * @param newGroup group to merge into this one
+ * @param complex set if a complex undo
+ * @return success
+ */
+ bool merge(KateUndoGroup* newGroup,bool complex);
+
+ /**
+ * set group as as savepoint. the next group will not merge with this one
+ */
+ void safePoint (bool safePoint=true);
+
+ /**
+ * is this undogroup empty?
+ */
+ bool isEmpty () const { return m_items.isEmpty(); }
+
+ private:
+ /**
+ * singleType
+ * @return the type if it's only one type, or editInvalid if it contains multiple types.
+ */
+ KateUndoGroup::UndoType singleType();
+
+ /**
+ * are we only of this type ?
+ * @param type type to query
+ * @return we contain only the given type
+ */
+ bool isOnlyType(KateUndoGroup::UndoType type);
+
+ /**
+ * add an undo item
+ * @param u item to add
+ */
+ void addItem (KateUndo *u);
+
+ private:
+ /**
+ * Document we belong to
+ */
+ KateDocument *m_doc;
+
+ /**
+ * list of items contained
+ */
+ QPtrList<KateUndo> m_items;
+
+ /**
+ * prohibit merging with the next group
+ */
+ bool m_safePoint;
+};
+
+#endif
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/kateview.cpp b/kate/part/kateview.cpp
new file mode 100644
index 000000000..6592bc8cc
--- /dev/null
+++ b/kate/part/kateview.cpp
@@ -0,0 +1,1920 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2003 Hamish Rodda <rodda@kde.org>
+ Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org>
+ Copyright (C) 2001-2004 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
+ Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#define DEBUGACCELS
+
+//BEGIN includes
+#include "kateview.h"
+#include "kateview.moc"
+
+#include "kateviewinternal.h"
+#include "kateviewhelpers.h"
+#include "katerenderer.h"
+#include "katedocument.h"
+#include "katedocumenthelpers.h"
+#include "katefactory.h"
+#include "katehighlight.h"
+#include "katedialogs.h"
+#include "katetextline.h"
+#include "katecodefoldinghelpers.h"
+#include "katecodecompletion.h"
+#include "katesearch.h"
+#include "kateschema.h"
+#include "katebookmarks.h"
+#include "katesearch.h"
+#include "kateconfig.h"
+#include "katefiletype.h"
+#include "kateautoindent.h"
+#include "katespell.h"
+
+#include <ktexteditor/plugin.h>
+
+#include <kparts/event.h>
+
+#include <kio/netaccess.h>
+
+#include <kconfig.h>
+#include <kurldrag.h>
+#include <kdebug.h>
+#include <kapplication.h>
+#include <kcursor.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <kcharsets.h>
+#include <kmessagebox.h>
+#include <kaction.h>
+#include <kstdaction.h>
+#include <kxmlguifactory.h>
+#include <kaccel.h>
+#include <klibloader.h>
+#include <kencodingfiledialog.h>
+#include <kmultipledrag.h>
+#include <ktempfile.h>
+#include <ksavefile.h>
+
+#include <qfont.h>
+#include <qfileinfo.h>
+#include <qstyle.h>
+#include <qevent.h>
+#include <qpopupmenu.h>
+#include <qlayout.h>
+#include <qclipboard.h>
+#include <qstylesheet.h>
+//END includes
+
+KateView::KateView( KateDocument *doc, QWidget *parent, const char * name )
+ : Kate::View( doc, parent, name )
+ , m_doc( doc )
+ , m_search( new KateSearch( this ) )
+ , m_spell( new KateSpell( this ) )
+ , m_bookmarks( new KateBookmarks( this ) )
+ , m_cmdLine (0)
+ , m_cmdLineOn (false)
+ , m_active( false )
+ , m_hasWrap( false )
+ , m_startingUp (true)
+ , m_updatingDocumentConfig (false)
+ , selectStart (m_doc, true)
+ , selectEnd (m_doc, true)
+ , blockSelect (false)
+ , m_imStartLine( 0 )
+ , m_imStart( 0 )
+ , m_imEnd( 0 )
+ , m_imSelStart( 0 )
+ , m_imSelEnd( 0 )
+ , m_imComposeEvent( false )
+{
+ KateFactory::self()->registerView( this );
+ m_config = new KateViewConfig (this);
+
+ m_renderer = new KateRenderer(doc, this);
+
+ m_grid = new QGridLayout (this, 3, 3);
+
+ m_grid->setRowStretch ( 0, 10 );
+ m_grid->setRowStretch ( 1, 0 );
+ m_grid->setColStretch ( 0, 0 );
+ m_grid->setColStretch ( 1, 10 );
+ m_grid->setColStretch ( 2, 0 );
+
+ m_viewInternal = new KateViewInternal( this, doc );
+ m_grid->addWidget (m_viewInternal, 0, 1);
+
+ setClipboardInterfaceDCOPSuffix (viewDCOPSuffix());
+ setCodeCompletionInterfaceDCOPSuffix (viewDCOPSuffix());
+ setDynWordWrapInterfaceDCOPSuffix (viewDCOPSuffix());
+ setPopupMenuInterfaceDCOPSuffix (viewDCOPSuffix());
+ setSessionConfigInterfaceDCOPSuffix (viewDCOPSuffix());
+ setViewCursorInterfaceDCOPSuffix (viewDCOPSuffix());
+ setViewStatusMsgInterfaceDCOPSuffix (viewDCOPSuffix());
+
+ setInstance( KateFactory::self()->instance() );
+ doc->addView( this );
+
+ setFocusProxy( m_viewInternal );
+ setFocusPolicy( StrongFocus );
+
+ if (!doc->singleViewMode()) {
+ setXMLFile( "katepartui.rc" );
+ } else {
+ if( doc->readOnly() )
+ setXMLFile( "katepartreadonlyui.rc" );
+ else
+ setXMLFile( "katepartui.rc" );
+ }
+
+ setupConnections();
+ setupActions();
+ setupEditActions();
+ setupCodeFolding();
+ setupCodeCompletion();
+
+ // enable the plugins of this view
+ m_doc->enableAllPluginsGUI (this);
+
+ // update the enabled state of the undo/redo actions...
+ slotNewUndo();
+
+ m_startingUp = false;
+ updateConfig ();
+
+ slotHlChanged();
+ /*test texthint
+ connect(this,SIGNAL(needTextHint(int, int, QString &)),
+ this,SLOT(slotNeedTextHint(int, int, QString &)));
+ enableTextHints(1000);
+ test texthint*/
+}
+
+KateView::~KateView()
+{
+ if (!m_doc->singleViewMode())
+ m_doc->disableAllPluginsGUI (this);
+
+ m_doc->removeView( this );
+
+ // its a QObject. don't double-delete
+ //delete m_viewInternal;
+ //delete m_codeCompletion;
+
+ delete m_renderer;
+ m_renderer = 0;
+
+ delete m_config;
+ m_config = 0;
+ KateFactory::self()->deregisterView (this);
+}
+
+void KateView::setupConnections()
+{
+ connect( m_doc, SIGNAL(undoChanged()),
+ this, SLOT(slotNewUndo()) );
+ connect( m_doc, SIGNAL(hlChanged()),
+ this, SLOT(slotHlChanged()) );
+ connect( m_doc, SIGNAL(canceled(const QString&)),
+ this, SLOT(slotSaveCanceled(const QString&)) );
+ connect( m_viewInternal, SIGNAL(dropEventPass(QDropEvent*)),
+ this, SIGNAL(dropEventPass(QDropEvent*)) );
+ connect(this,SIGNAL(cursorPositionChanged()),this,SLOT(slotStatusMsg()));
+ connect(this,SIGNAL(newStatus()),this,SLOT(slotStatusMsg()));
+ connect(m_doc, SIGNAL(undoChanged()), this, SLOT(slotStatusMsg()));
+
+ if ( m_doc->browserView() )
+ {
+ connect( this, SIGNAL(dropEventPass(QDropEvent*)),
+ this, SLOT(slotDropEventPass(QDropEvent*)) );
+ }
+}
+
+void KateView::setupActions()
+{
+ KActionCollection *ac = this->actionCollection ();
+ KAction *a;
+
+ m_toggleWriteLock = 0;
+
+ m_cut = a=KStdAction::cut(this, SLOT(cut()), ac);
+ a->setWhatsThis(i18n("Cut the selected text and move it to the clipboard"));
+
+ m_paste = a=KStdAction::pasteText(this, SLOT(paste()), ac);
+ a->setWhatsThis(i18n("Paste previously copied or cut clipboard contents"));
+
+ m_copy = a=KStdAction::copy(this, SLOT(copy()), ac);
+ a->setWhatsThis(i18n( "Use this command to copy the currently selected text to the system clipboard."));
+
+ m_copyHTML = a = new KAction(i18n("Copy as &HTML"), "editcopy", 0, this, SLOT(copyHTML()), ac, "edit_copy_html");
+ a->setWhatsThis(i18n( "Use this command to copy the currently selected text as HTML to the system clipboard."));
+
+ if (!m_doc->readOnly())
+ {
+ a=KStdAction::save(this, SLOT(save()), ac);
+ a->setWhatsThis(i18n("Save the current document"));
+
+ a=m_editUndo = KStdAction::undo(m_doc, SLOT(undo()), ac);
+ a->setWhatsThis(i18n("Revert the most recent editing actions"));
+
+ a=m_editRedo = KStdAction::redo(m_doc, SLOT(redo()), ac);
+ a->setWhatsThis(i18n("Revert the most recent undo operation"));
+
+ (new KAction(i18n("&Word Wrap Document"), "", 0, this, SLOT(applyWordWrap()), ac, "tools_apply_wordwrap"))->setWhatsThis(
+ i18n("Use this command to wrap all lines of the current document which are longer than the width of the"
+ " current view, to fit into this view.<br><br> This is a static word wrap, meaning it is not updated"
+ " when the view is resized."));
+
+ // setup Tools menu
+ a=new KAction(i18n("&Indent"), "indent", Qt::CTRL+Qt::Key_I, this, SLOT(indent()), ac, "tools_indent");
+ a->setWhatsThis(i18n("Use this to indent a selected block of text.<br><br>"
+ "You can configure whether tabs should be honored and used or replaced with spaces, in the configuration dialog."));
+ a=new KAction(i18n("&Unindent"), "unindent", Qt::CTRL+Qt::SHIFT+Qt::Key_I, this, SLOT(unIndent()), ac, "tools_unindent");
+ a->setWhatsThis(i18n("Use this to unindent a selected block of text."));
+
+ a=new KAction(i18n("&Clean Indentation"), 0, this, SLOT(cleanIndent()), ac, "tools_cleanIndent");
+ a->setWhatsThis(i18n("Use this to clean the indentation of a selected block of text (only tabs/only spaces)<br><br>"
+ "You can configure whether tabs should be honored and used or replaced with spaces, in the configuration dialog."));
+
+ a=new KAction(i18n("&Align"), 0, this, SLOT(align()), ac, "tools_align");
+ a->setWhatsThis(i18n("Use this to align the current line or block of text to its proper indent level."));
+
+ a=new KAction(i18n("C&omment"), CTRL+Qt::Key_D, this, SLOT(comment()),
+ ac, "tools_comment");
+ a->setWhatsThis(i18n("This command comments out the current line or a selected block of text.<BR><BR>"
+ "The characters for single/multiple line comments are defined within the language's highlighting."));
+
+ a=new KAction(i18n("Unco&mment"), CTRL+SHIFT+Qt::Key_D, this, SLOT(uncomment()),
+ ac, "tools_uncomment");
+ a->setWhatsThis(i18n("This command removes comments from the current line or a selected block of text.<BR><BR>"
+ "The characters for single/multiple line comments are defined within the language's highlighting."));
+ a = m_toggleWriteLock = new KToggleAction(
+ i18n("&Read Only Mode"), 0, 0,
+ this, SLOT( toggleWriteLock() ),
+ ac, "tools_toggle_write_lock" );
+ a->setWhatsThis( i18n("Lock/unlock the document for writing") );
+
+ a = new KAction( i18n("Uppercase"), CTRL + Qt::Key_U, this,
+ SLOT(uppercase()), ac, "tools_uppercase" );
+ a->setWhatsThis( i18n("Convert the selection to uppercase, or the character to the "
+ "right of the cursor if no text is selected.") );
+
+ a = new KAction( i18n("Lowercase"), CTRL + SHIFT + Qt::Key_U, this,
+ SLOT(lowercase()), ac, "tools_lowercase" );
+ a->setWhatsThis( i18n("Convert the selection to lowercase, or the character to the "
+ "right of the cursor if no text is selected.") );
+
+ a = new KAction( i18n("Capitalize"), CTRL + ALT + Qt::Key_U, this,
+ SLOT(capitalize()), ac, "tools_capitalize" );
+ a->setWhatsThis( i18n("Capitalize the selection, or the word under the "
+ "cursor if no text is selected.") );
+
+ a = new KAction( i18n("Join Lines"), CTRL + Qt::Key_J, this,
+ SLOT( joinLines() ), ac, "tools_join_lines" );
+ }
+ else
+ {
+ m_cut->setEnabled (false);
+ m_paste->setEnabled (false);
+ m_editUndo = 0;
+ m_editRedo = 0;
+ }
+
+ a=KStdAction::print( m_doc, SLOT(print()), ac );
+ a->setWhatsThis(i18n("Print the current document."));
+
+ a=new KAction(i18n("Reloa&d"), "reload", KStdAccel::reload(), this, SLOT(reloadFile()), ac, "file_reload");
+ a->setWhatsThis(i18n("Reload the current document from disk."));
+
+ a=KStdAction::saveAs(this, SLOT(saveAs()), ac);
+ a->setWhatsThis(i18n("Save the current document to disk, with a name of your choice."));
+
+ a=KStdAction::gotoLine(this, SLOT(gotoLine()), ac);
+ a->setWhatsThis(i18n("This command opens a dialog and lets you choose a line that you want the cursor to move to."));
+
+ a=new KAction(i18n("&Configure Editor..."), 0, m_doc, SLOT(configDialog()),ac, "set_confdlg");
+ a->setWhatsThis(i18n("Configure various aspects of this editor."));
+
+ KateViewHighlightAction *menu = new KateViewHighlightAction (i18n("&Highlighting"), ac, "set_highlight");
+ menu->setWhatsThis(i18n("Here you can choose how the current document should be highlighted."));
+ menu->updateMenu (m_doc);
+
+ KateViewFileTypeAction *ftm = new KateViewFileTypeAction (i18n("&Filetype"),ac,"set_filetype");
+ ftm->updateMenu (m_doc);
+
+ KateViewSchemaAction *schemaMenu = new KateViewSchemaAction (i18n("&Schema"),ac,"view_schemas");
+ schemaMenu->updateMenu (this);
+
+ // indentation menu
+ new KateViewIndentationAction (m_doc, i18n("&Indentation"),ac,"tools_indentation");
+
+ // html export
+ a = new KAction(i18n("E&xport as HTML..."), 0, 0, this, SLOT(exportAsHTML()), ac, "file_export_html");
+ a->setWhatsThis(i18n("This command allows you to export the current document"
+ " with all highlighting information into a HTML document."));
+
+ m_selectAll = a=KStdAction::selectAll(this, SLOT(selectAll()), ac);
+ a->setWhatsThis(i18n("Select the entire text of the current document."));
+
+ m_deSelect = a=KStdAction::deselect(this, SLOT(clearSelection()), ac);
+ a->setWhatsThis(i18n("If you have selected something within the current document, this will no longer be selected."));
+
+ a=new KAction(i18n("Enlarge Font"), "viewmag+", 0, m_viewInternal, SLOT(slotIncFontSizes()), ac, "incFontSizes");
+ a->setWhatsThis(i18n("This increases the display font size."));
+
+ a=new KAction(i18n("Shrink Font"), "viewmag-", 0, m_viewInternal, SLOT(slotDecFontSizes()), ac, "decFontSizes");
+ a->setWhatsThis(i18n("This decreases the display font size."));
+
+ a= m_toggleBlockSelection = new KToggleAction(
+ i18n("Bl&ock Selection Mode"), CTRL + SHIFT + Key_B,
+ this, SLOT(toggleBlockSelectionMode()),
+ ac, "set_verticalSelect");
+ a->setWhatsThis(i18n("This command allows switching between the normal (line based) selection mode and the block selection mode."));
+
+ a= m_toggleInsert = new KToggleAction(
+ i18n("Overwr&ite Mode"), Key_Insert,
+ this, SLOT(toggleInsert()),
+ ac, "set_insert" );
+ a->setWhatsThis(i18n("Choose whether you want the text you type to be inserted or to overwrite existing text."));
+
+ KToggleAction *toggleAction;
+ a= m_toggleDynWrap = toggleAction = new KToggleAction(
+ i18n("&Dynamic Word Wrap"), Key_F10,
+ this, SLOT(toggleDynWordWrap()),
+ ac, "view_dynamic_word_wrap" );
+ a->setWhatsThis(i18n("If this option is checked, the text lines will be wrapped at the view border on the screen."));
+
+ a= m_setDynWrapIndicators = new KSelectAction(i18n("Dynamic Word Wrap Indicators"), 0, ac, "dynamic_word_wrap_indicators");
+ a->setWhatsThis(i18n("Choose when the Dynamic Word Wrap Indicators should be displayed"));
+
+ connect(m_setDynWrapIndicators, SIGNAL(activated(int)), this, SLOT(setDynWrapIndicators(int)));
+ QStringList list2;
+ list2.append(i18n("&Off"));
+ list2.append(i18n("Follow &Line Numbers"));
+ list2.append(i18n("&Always On"));
+ m_setDynWrapIndicators->setItems(list2);
+
+ a= toggleAction=m_toggleFoldingMarkers = new KToggleAction(
+ i18n("Show Folding &Markers"), Key_F9,
+ this, SLOT(toggleFoldingMarkers()),
+ ac, "view_folding_markers" );
+ a->setWhatsThis(i18n("You can choose if the codefolding marks should be shown, if codefolding is possible."));
+ toggleAction->setCheckedState(i18n("Hide Folding &Markers"));
+
+ a= m_toggleIconBar = toggleAction = new KToggleAction(
+ i18n("Show &Icon Border"), Key_F6,
+ this, SLOT(toggleIconBorder()),
+ ac, "view_border");
+ a=toggleAction;
+ a->setWhatsThis(i18n("Show/hide the icon border.<BR><BR> The icon border shows bookmark symbols, for instance."));
+ toggleAction->setCheckedState(i18n("Hide &Icon Border"));
+
+ a= toggleAction=m_toggleLineNumbers = new KToggleAction(
+ i18n("Show &Line Numbers"), Key_F11,
+ this, SLOT(toggleLineNumbersOn()),
+ ac, "view_line_numbers" );
+ a->setWhatsThis(i18n("Show/hide the line numbers on the left hand side of the view."));
+ toggleAction->setCheckedState(i18n("Hide &Line Numbers"));
+
+ a= m_toggleScrollBarMarks = toggleAction = new KToggleAction(
+ i18n("Show Scroll&bar Marks"), 0,
+ this, SLOT(toggleScrollBarMarks()),
+ ac, "view_scrollbar_marks");
+ a->setWhatsThis(i18n("Show/hide the marks on the vertical scrollbar.<BR><BR>The marks, for instance, show bookmarks."));
+ toggleAction->setCheckedState(i18n("Hide Scroll&bar Marks"));
+
+ a = toggleAction = m_toggleWWMarker = new KToggleAction(
+ i18n("Show Static &Word Wrap Marker"), 0,
+ this, SLOT( toggleWWMarker() ),
+ ac, "view_word_wrap_marker" );
+ a->setWhatsThis( i18n(
+ "Show/hide the Word Wrap Marker, a vertical line drawn at the word "
+ "wrap column as defined in the editing properties" ));
+ toggleAction->setCheckedState(i18n("Hide Static &Word Wrap Marker"));
+
+ a= m_switchCmdLine = new KAction(
+ i18n("Switch to Command Line"), Key_F7,
+ this, SLOT(switchToCmdLine()),
+ ac, "switch_to_cmd_line" );
+ a->setWhatsThis(i18n("Show/hide the command line on the bottom of the view."));
+
+ a=m_setEndOfLine = new KSelectAction(i18n("&End of Line"), 0, ac, "set_eol");
+ a->setWhatsThis(i18n("Choose which line endings should be used, when you save the document"));
+ QStringList list;
+ list.append("&UNIX");
+ list.append("&Windows/DOS");
+ list.append("&Macintosh");
+ m_setEndOfLine->setItems(list);
+ m_setEndOfLine->setCurrentItem (m_doc->config()->eol());
+ connect(m_setEndOfLine, SIGNAL(activated(int)), this, SLOT(setEol(int)));
+
+ // encoding menu
+ new KateViewEncodingAction (m_doc, this, i18n("E&ncoding"), ac, "set_encoding");
+
+ m_search->createActions( ac );
+ m_spell->createActions( ac );
+ m_bookmarks->createActions( ac );
+
+ slotSelectionChanged ();
+
+ connect (this, SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged()));
+}
+
+void KateView::setupEditActions()
+{
+ m_editActions = new KActionCollection( m_viewInternal, this, "edit_actions" );
+ KActionCollection* ac = m_editActions;
+
+ new KAction(
+ i18n("Move Word Left"), CTRL + Key_Left,
+ this,SLOT(wordLeft()),
+ ac, "word_left" );
+ new KAction(
+ i18n("Select Character Left"), SHIFT + Key_Left,
+ this,SLOT(shiftCursorLeft()),
+ ac, "select_char_left" );
+ new KAction(
+ i18n("Select Word Left"), SHIFT + CTRL + Key_Left,
+ this, SLOT(shiftWordLeft()),
+ ac, "select_word_left" );
+
+ new KAction(
+ i18n("Move Word Right"), CTRL + Key_Right,
+ this, SLOT(wordRight()),
+ ac, "word_right" );
+ new KAction(
+ i18n("Select Character Right"), SHIFT + Key_Right,
+ this, SLOT(shiftCursorRight()),
+ ac, "select_char_right" );
+ new KAction(
+ i18n("Select Word Right"), SHIFT + CTRL + Key_Right,
+ this,SLOT(shiftWordRight()),
+ ac, "select_word_right" );
+
+ new KAction(
+ i18n("Move to Beginning of Line"), Key_Home,
+ this, SLOT(home()),
+ ac, "beginning_of_line" );
+ new KAction(
+ i18n("Move to Beginning of Document"), KStdAccel::home(),
+ this, SLOT(top()),
+ ac, "beginning_of_document" );
+ new KAction(
+ i18n("Select to Beginning of Line"), SHIFT + Key_Home,
+ this, SLOT(shiftHome()),
+ ac, "select_beginning_of_line" );
+ new KAction(
+ i18n("Select to Beginning of Document"), SHIFT + CTRL + Key_Home,
+ this, SLOT(shiftTop()),
+ ac, "select_beginning_of_document" );
+
+ new KAction(
+ i18n("Move to End of Line"), Key_End,
+ this, SLOT(end()),
+ ac, "end_of_line" );
+ new KAction(
+ i18n("Move to End of Document"), KStdAccel::end(),
+ this, SLOT(bottom()),
+ ac, "end_of_document" );
+ new KAction(
+ i18n("Select to End of Line"), SHIFT + Key_End,
+ this, SLOT(shiftEnd()),
+ ac, "select_end_of_line" );
+ new KAction(
+ i18n("Select to End of Document"), SHIFT + CTRL + Key_End,
+ this, SLOT(shiftBottom()),
+ ac, "select_end_of_document" );
+
+ new KAction(
+ i18n("Select to Previous Line"), SHIFT + Key_Up,
+ this, SLOT(shiftUp()),
+ ac, "select_line_up" );
+ new KAction(
+ i18n("Scroll Line Up"),"", CTRL + Key_Up,
+ this, SLOT(scrollUp()),
+ ac, "scroll_line_up" );
+
+ new KAction(i18n("Move to Next Line"), Key_Down, this, SLOT(down()),
+ ac, "move_line_down");
+
+ new KAction(i18n("Move to Previous Line"), Key_Up, this, SLOT(up()),
+ ac, "move_line_up");
+
+ new KAction(i18n("Move Character Right"), Key_Right, this,
+ SLOT(cursorRight()), ac, "move_cursor_right");
+
+ new KAction(i18n("Move Character Left"), Key_Left, this, SLOT(cursorLeft()),
+ ac, "move_cusor_left");
+
+ new KAction(
+ i18n("Select to Next Line"), SHIFT + Key_Down,
+ this, SLOT(shiftDown()),
+ ac, "select_line_down" );
+ new KAction(
+ i18n("Scroll Line Down"), CTRL + Key_Down,
+ this, SLOT(scrollDown()),
+ ac, "scroll_line_down" );
+
+ new KAction(
+ i18n("Scroll Page Up"), KStdAccel::prior(),
+ this, SLOT(pageUp()),
+ ac, "scroll_page_up" );
+ new KAction(
+ i18n("Select Page Up"), SHIFT + Key_PageUp,
+ this, SLOT(shiftPageUp()),
+ ac, "select_page_up" );
+ new KAction(
+ i18n("Move to Top of View"), CTRL + Key_PageUp,
+ this, SLOT(topOfView()),
+ ac, "move_top_of_view" );
+ new KAction(
+ i18n("Select to Top of View"), CTRL + SHIFT + Key_PageUp,
+ this, SLOT(shiftTopOfView()),
+ ac, "select_top_of_view" );
+
+ new KAction(
+ i18n("Scroll Page Down"), KStdAccel::next(),
+ this, SLOT(pageDown()),
+ ac, "scroll_page_down" );
+ new KAction(
+ i18n("Select Page Down"), SHIFT + Key_PageDown,
+ this, SLOT(shiftPageDown()),
+ ac, "select_page_down" );
+ new KAction(
+ i18n("Move to Bottom of View"), CTRL + Key_PageDown,
+ this, SLOT(bottomOfView()),
+ ac, "move_bottom_of_view" );
+ new KAction(
+ i18n("Select to Bottom of View"), CTRL + SHIFT + Key_PageDown,
+ this, SLOT(shiftBottomOfView()),
+ ac, "select_bottom_of_view" );
+ new KAction(
+ i18n("Move to Matching Bracket"), CTRL + Key_6,
+ this, SLOT(toMatchingBracket()),
+ ac, "to_matching_bracket" );
+ new KAction(
+ i18n("Select to Matching Bracket"), SHIFT + CTRL + Key_6,
+ this, SLOT(shiftToMatchingBracket()),
+ ac, "select_matching_bracket" );
+
+ // anders: shortcuts doing any changes should not be created in browserextension
+ if ( !m_doc->readOnly() )
+ {
+ new KAction(
+ i18n("Transpose Characters"), CTRL + Key_T,
+ this, SLOT(transpose()),
+ ac, "transpose_char" );
+
+ new KAction(
+ i18n("Delete Line"), CTRL + Key_K,
+ this, SLOT(killLine()),
+ ac, "delete_line" );
+
+ new KAction(
+ i18n("Delete Word Left"), KStdAccel::deleteWordBack(),
+ this, SLOT(deleteWordLeft()),
+ ac, "delete_word_left" );
+
+ new KAction(
+ i18n("Delete Word Right"), KStdAccel::deleteWordForward(),
+ this, SLOT(deleteWordRight()),
+ ac, "delete_word_right" );
+
+ new KAction(i18n("Delete Next Character"), Key_Delete,
+ this, SLOT(keyDelete()),
+ ac, "delete_next_character");
+
+ KAction *a = new KAction(i18n("Backspace"), Key_Backspace,
+ this, SLOT(backspace()),
+ ac, "backspace");
+ KShortcut cut = a->shortcut();
+ cut.append( KKey( SHIFT + Key_Backspace ) );
+ a->setShortcut( cut );
+ }
+
+ connect( this, SIGNAL(gotFocus(Kate::View*)),
+ this, SLOT(slotGotFocus()) );
+ connect( this, SIGNAL(lostFocus(Kate::View*)),
+ this, SLOT(slotLostFocus()) );
+
+ m_editActions->readShortcutSettings( "Katepart Shortcuts" );
+
+ if( hasFocus() )
+ slotGotFocus();
+ else
+ slotLostFocus();
+
+
+}
+
+void KateView::setupCodeFolding()
+{
+ KActionCollection *ac=this->actionCollection();
+ new KAction( i18n("Collapse Toplevel"), CTRL+SHIFT+Key_Minus,
+ m_doc->foldingTree(),SLOT(collapseToplevelNodes()),ac,"folding_toplevel");
+ new KAction( i18n("Expand Toplevel"), CTRL+SHIFT+Key_Plus,
+ this,SLOT(slotExpandToplevel()),ac,"folding_expandtoplevel");
+ new KAction( i18n("Collapse One Local Level"), CTRL+Key_Minus,
+ this,SLOT(slotCollapseLocal()),ac,"folding_collapselocal");
+ new KAction( i18n("Expand One Local Level"), CTRL+Key_Plus,
+ this,SLOT(slotExpandLocal()),ac,"folding_expandlocal");
+
+#ifdef DEBUGACCELS
+ KAccel* debugAccels = new KAccel(this,this);
+ debugAccels->insert("KATE_DUMP_REGION_TREE",i18n("Show the code folding region tree"),"","Ctrl+Shift+Alt+D",m_doc,SLOT(dumpRegionTree()));
+ debugAccels->insert("KATE_TEMPLATE_TEST",i18n("Basic template code test"),"","Ctrl+Shift+Alt+T",m_doc,SLOT(testTemplateCode()));
+ debugAccels->setEnabled(true);
+#endif
+}
+
+void KateView::slotExpandToplevel()
+{
+ m_doc->foldingTree()->expandToplevelNodes(m_doc->numLines());
+}
+
+void KateView::slotCollapseLocal()
+{
+ int realLine = m_doc->foldingTree()->collapseOne(cursorLine());
+ if (realLine != -1)
+ // TODO rodda: fix this to only set line and allow internal view to chose column
+ // Explicitly call internal because we want this to be registered as an internal call
+ setCursorPositionInternal(realLine, cursorColumn(), tabWidth(), false);
+}
+
+void KateView::slotExpandLocal()
+{
+ m_doc->foldingTree()->expandOne(cursorLine(), m_doc->numLines());
+}
+
+void KateView::setupCodeCompletion()
+{
+ m_codeCompletion = new KateCodeCompletion(this);
+ connect( m_codeCompletion, SIGNAL(completionAborted()),
+ this, SIGNAL(completionAborted()));
+ connect( m_codeCompletion, SIGNAL(completionDone()),
+ this, SIGNAL(completionDone()));
+ connect( m_codeCompletion, SIGNAL(argHintHidden()),
+ this, SIGNAL(argHintHidden()));
+ connect( m_codeCompletion, SIGNAL(completionDone(KTextEditor::CompletionEntry)),
+ this, SIGNAL(completionDone(KTextEditor::CompletionEntry)));
+ connect( m_codeCompletion, SIGNAL(filterInsertString(KTextEditor::CompletionEntry*,QString*)),
+ this, SIGNAL(filterInsertString(KTextEditor::CompletionEntry*,QString*)));
+}
+
+void KateView::slotGotFocus()
+{
+ m_editActions->accel()->setEnabled( true );
+
+ slotStatusMsg ();
+}
+
+void KateView::slotLostFocus()
+{
+ m_editActions->accel()->setEnabled( false );
+}
+
+void KateView::setDynWrapIndicators(int mode)
+{
+ config()->setDynWordWrapIndicators (mode);
+}
+
+void KateView::slotStatusMsg ()
+{
+ QString ovrstr;
+ if (m_doc->isReadWrite())
+ {
+ if (m_doc->config()->configFlags() & KateDocument::cfOvr)
+ ovrstr = i18n(" OVR ");
+ else
+ ovrstr = i18n(" INS ");
+ }
+ else
+ ovrstr = i18n(" R/O ");
+
+ uint r = cursorLine() + 1;
+ uint c = cursorColumn() + 1;
+
+ QString s1 = i18n(" Line: %1").arg(KGlobal::locale()->formatNumber(r, 0));
+ QString s2 = i18n(" Col: %1").arg(KGlobal::locale()->formatNumber(c, 0));
+
+ QString modstr = m_doc->isModified() ? QString (" * ") : QString (" ");
+ QString blockstr = blockSelectionMode() ? i18n(" BLK ") : i18n(" NORM ");
+
+ emit viewStatusMsg (s1 + s2 + " " + ovrstr + blockstr + modstr);
+}
+
+void KateView::slotSelectionTypeChanged()
+{
+ m_toggleBlockSelection->setChecked( blockSelectionMode() );
+
+ emit newStatus();
+}
+
+bool KateView::isOverwriteMode() const
+{
+ return m_doc->config()->configFlags() & KateDocument::cfOvr;
+}
+
+void KateView::reloadFile()
+{
+ m_doc->reloadFile();
+ emit newStatus();
+}
+
+void KateView::slotUpdate()
+{
+ emit newStatus();
+
+ slotNewUndo();
+}
+
+void KateView::slotReadWriteChanged ()
+{
+ if ( m_toggleWriteLock )
+ m_toggleWriteLock->setChecked( ! m_doc->isReadWrite() );
+
+ m_cut->setEnabled (m_doc->isReadWrite());
+ m_paste->setEnabled (m_doc->isReadWrite());
+
+ QStringList l;
+
+ l << "edit_replace" << "set_insert" << "tools_spelling" << "tools_indent"
+ << "tools_unindent" << "tools_cleanIndent" << "tools_align" << "tools_comment"
+ << "tools_uncomment" << "tools_uppercase" << "tools_lowercase"
+ << "tools_capitalize" << "tools_join_lines" << "tools_apply_wordwrap"
+ << "edit_undo" << "edit_redo" << "tools_spelling_from_cursor"
+ << "tools_spelling_selection";
+
+ KAction *a = 0;
+ for (uint z = 0; z < l.size(); z++)
+ if ((a = actionCollection()->action( l[z].ascii() )))
+ a->setEnabled (m_doc->isReadWrite());
+}
+
+void KateView::slotNewUndo()
+{
+ if (m_doc->readOnly())
+ return;
+
+ if ((m_doc->undoCount() > 0) != m_editUndo->isEnabled())
+ m_editUndo->setEnabled(m_doc->undoCount() > 0);
+
+ if ((m_doc->redoCount() > 0) != m_editRedo->isEnabled())
+ m_editRedo->setEnabled(m_doc->redoCount() > 0);
+}
+
+void KateView::slotDropEventPass( QDropEvent * ev )
+{
+ KURL::List lstDragURLs;
+ bool ok = KURLDrag::decode( ev, lstDragURLs );
+
+ KParts::BrowserExtension * ext = KParts::BrowserExtension::childObject( doc() );
+ if ( ok && ext )
+ emit ext->openURLRequest( lstDragURLs.first() );
+}
+
+void KateView::contextMenuEvent( QContextMenuEvent *ev )
+{
+ if ( !m_doc || !m_doc->browserExtension() )
+ return;
+ emit m_doc->browserExtension()->popupMenu( /*this, */ev->globalPos(), m_doc->url(),
+ QString::fromLatin1( "text/plain" ) );
+ ev->accept();
+}
+
+bool KateView::setCursorPositionInternal( uint line, uint col, uint tabwidth, bool calledExternally )
+{
+ KateTextLine::Ptr l = m_doc->kateTextLine( line );
+
+ if (!l)
+ return false;
+
+ QString line_str = m_doc->textLine( line );
+
+ uint z;
+ uint x = 0;
+ for (z = 0; z < line_str.length() && z < col; z++) {
+ if (line_str[z] == QChar('\t')) x += tabwidth - (x % tabwidth); else x++;
+ }
+
+ m_viewInternal->updateCursor( KateTextCursor( line, x ), false, true, calledExternally );
+
+ return true;
+}
+
+void KateView::setOverwriteMode( bool b )
+{
+ if ( isOverwriteMode() && !b )
+ m_doc->setConfigFlags( m_doc->config()->configFlags() ^ KateDocument::cfOvr );
+ else
+ m_doc->setConfigFlags( m_doc->config()->configFlags() | KateDocument::cfOvr );
+
+ m_toggleInsert->setChecked (isOverwriteMode ());
+}
+
+void KateView::toggleInsert()
+{
+ m_doc->setConfigFlags(m_doc->config()->configFlags() ^ KateDocument::cfOvr);
+ m_toggleInsert->setChecked (isOverwriteMode ());
+
+ emit newStatus();
+}
+
+bool KateView::canDiscard()
+{
+ return m_doc->closeURL();
+}
+
+void KateView::flush()
+{
+ m_doc->closeURL();
+}
+
+KateView::saveResult KateView::save()
+{
+ if( !m_doc->url().isValid() || !doc()->isReadWrite() )
+ return saveAs();
+
+ if( m_doc->save() )
+ return SAVE_OK;
+
+ return SAVE_ERROR;
+}
+
+KateView::saveResult KateView::saveAs()
+{
+
+ KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveURLAndEncoding(doc()->config()->encoding(),
+ m_doc->url().url(),QString::null,this,i18n("Save File"));
+
+// kdDebug()<<"urllist is emtpy?"<<res.URLs.isEmpty()<<endl;
+// kdDebug()<<"url is:"<<res.URLs.first()<<endl;
+ if( res.URLs.isEmpty() || !checkOverwrite( res.URLs.first() ) )
+ return SAVE_CANCEL;
+
+ m_doc->config()->setEncoding( res.encoding );
+
+ if( m_doc->saveAs( res.URLs.first() ) )
+ return SAVE_OK;
+
+ return SAVE_ERROR;
+}
+
+bool KateView::checkOverwrite( KURL u )
+{
+ if( !u.isLocalFile() )
+ return true;
+
+ QFileInfo info( u.path() );
+ if( !info.exists() )
+ return true;
+
+ return KMessageBox::Continue
+ == KMessageBox::warningContinueCancel
+ ( this,
+ i18n( "A file named \"%1\" already exists. Are you sure you want to overwrite it?" ).arg( info.fileName() ),
+ i18n( "Overwrite File?" ),
+ KGuiItem( i18n( "&Overwrite" ), "filesave", i18n( "Overwrite the file" ) )
+ );
+}
+
+void KateView::slotSaveCanceled( const QString& error )
+{
+ if ( !error.isEmpty() ) // happens when cancelling a job
+ KMessageBox::error( this, error );
+}
+
+void KateView::gotoLine()
+{
+ KateGotoLineDialog *dlg = new KateGotoLineDialog (this, m_viewInternal->getCursor().line() + 1, m_doc->numLines());
+
+ if (dlg->exec() == QDialog::Accepted)
+ gotoLineNumber( dlg->getLine() - 1 );
+
+ delete dlg;
+}
+
+void KateView::gotoLineNumber( int line )
+{
+ // clear selection, unless we are in persistent selection mode
+ if ( !config()->persistentSelection() )
+ clearSelection();
+ setCursorPositionInternal ( line, 0, 1 );
+}
+
+void KateView::joinLines()
+{
+ int first = selStartLine();
+ int last = selEndLine();
+ //int left = m_doc->textLine( last ).length() - m_doc->selEndCol();
+ if ( first == last )
+ {
+ first = cursorLine();
+ last = first + 1;
+ }
+ m_doc->joinLines( first, last );
+}
+
+void KateView::readSessionConfig(KConfig *config)
+{
+ setCursorPositionInternal (config->readNumEntry("CursorLine"), config->readNumEntry("CursorColumn"), 1);
+}
+
+void KateView::writeSessionConfig(KConfig *config)
+{
+ config->writeEntry("CursorLine",m_viewInternal->cursor.line());
+ config->writeEntry("CursorColumn",m_viewInternal->cursor.col());
+}
+
+int KateView::getEol()
+{
+ return m_doc->config()->eol();
+}
+
+void KateView::setEol(int eol)
+{
+ if (!doc()->isReadWrite())
+ return;
+
+ if (m_updatingDocumentConfig)
+ return;
+
+ m_doc->config()->setEol (eol);
+}
+
+void KateView::setIconBorder( bool enable )
+{
+ config()->setIconBar (enable);
+}
+
+void KateView::toggleIconBorder()
+{
+ config()->setIconBar (!config()->iconBar());
+}
+
+void KateView::setLineNumbersOn( bool enable )
+{
+ config()->setLineNumbers (enable);
+}
+
+void KateView::toggleLineNumbersOn()
+{
+ config()->setLineNumbers (!config()->lineNumbers());
+}
+
+void KateView::setScrollBarMarks( bool enable )
+{
+ config()->setScrollBarMarks (enable);
+}
+
+void KateView::toggleScrollBarMarks()
+{
+ config()->setScrollBarMarks (!config()->scrollBarMarks());
+}
+
+void KateView::toggleDynWordWrap()
+{
+ config()->setDynWordWrap( !config()->dynWordWrap() );
+}
+
+void KateView::setDynWordWrap( bool b )
+{
+ config()->setDynWordWrap( b );
+}
+
+void KateView::toggleWWMarker()
+{
+ m_renderer->config()->setWordWrapMarker (!m_renderer->config()->wordWrapMarker());
+}
+
+void KateView::setFoldingMarkersOn( bool enable )
+{
+ config()->setFoldingBar ( enable );
+}
+
+void KateView::toggleFoldingMarkers()
+{
+ config()->setFoldingBar ( !config()->foldingBar() );
+}
+
+bool KateView::iconBorder() {
+ return m_viewInternal->leftBorder->iconBorderOn();
+}
+
+bool KateView::lineNumbersOn() {
+ return m_viewInternal->leftBorder->lineNumbersOn();
+}
+
+bool KateView::scrollBarMarks() {
+ return m_viewInternal->m_lineScroll->showMarks();
+}
+
+int KateView::dynWrapIndicators() {
+ return m_viewInternal->leftBorder->dynWrapIndicators();
+}
+
+bool KateView::foldingMarkersOn() {
+ return m_viewInternal->leftBorder->foldingMarkersOn();
+}
+
+void KateView::showCmdLine ( bool enabled )
+{
+ if (enabled == m_cmdLineOn)
+ return;
+
+ if (enabled)
+ {
+ if (!m_cmdLine)
+ {
+ m_cmdLine = new KateCmdLine (this);
+ m_grid->addMultiCellWidget (m_cmdLine, 2, 2, 0, 2);
+ }
+
+ m_cmdLine->show ();
+ m_cmdLine->setFocus();
+ }
+ else {
+ m_cmdLine->hide ();
+ //m_toggleCmdLine->setChecked(false);
+ }
+
+ m_cmdLineOn = enabled;
+}
+
+void KateView::toggleCmdLine ()
+{
+ m_config->setCmdLine (!m_config->cmdLine ());
+}
+
+void KateView::toggleWriteLock()
+{
+ m_doc->setReadWrite( ! m_doc->isReadWrite() );
+}
+
+void KateView::enableTextHints(int timeout)
+{
+ m_viewInternal->enableTextHints(timeout);
+}
+
+void KateView::disableTextHints()
+{
+ m_viewInternal->disableTextHints();
+}
+
+void KateView::applyWordWrap ()
+{
+ if (hasSelection())
+ m_doc->wrapText (selectStart.line(), selectEnd.line());
+ else
+ m_doc->wrapText (0, m_doc->lastLine());
+}
+
+void KateView::slotNeedTextHint(int line, int col, QString &text)
+{
+ text=QString("test %1 %2").arg(line).arg(col);
+}
+
+void KateView::find()
+{
+ m_search->find();
+}
+
+void KateView::find( const QString& pattern, long flags, bool add )
+{
+ m_search->find( pattern, flags, add );
+}
+
+void KateView::replace()
+{
+ m_search->replace();
+}
+
+void KateView::replace( const QString &pattern, const QString &replacement, long flags )
+{
+ m_search->replace( pattern, replacement, flags );
+}
+
+void KateView::findAgain( bool back )
+{
+ m_search->findAgain( back );
+}
+
+void KateView::slotSelectionChanged ()
+{
+ m_copy->setEnabled (hasSelection());
+ m_copyHTML->setEnabled (hasSelection());
+ m_deSelect->setEnabled (hasSelection());
+
+ if (m_doc->readOnly())
+ return;
+
+ m_cut->setEnabled (hasSelection());
+
+ m_spell->updateActions ();
+}
+
+void KateView::switchToCmdLine ()
+{
+ if (!m_cmdLineOn)
+ m_config->setCmdLine (true);
+ else {
+ if (m_cmdLine->hasFocus()) {
+ this->setFocus();
+ return;
+ }
+ }
+ m_cmdLine->setFocus ();
+}
+
+void KateView::showArgHint( QStringList arg1, const QString& arg2, const QString& arg3 )
+{
+ m_codeCompletion->showArgHint( arg1, arg2, arg3 );
+}
+
+void KateView::showCompletionBox( QValueList<KTextEditor::CompletionEntry> arg1, int offset, bool cs )
+{
+ emit aboutToShowCompletionBox();
+ m_codeCompletion->showCompletionBox( arg1, offset, cs );
+}
+
+KateRenderer *KateView::renderer ()
+{
+ return m_renderer;
+}
+
+void KateView::updateConfig ()
+{
+ if (m_startingUp)
+ return;
+
+ m_editActions->readShortcutSettings( "Katepart Shortcuts" );
+
+ // dyn. word wrap & markers
+ if (m_hasWrap != config()->dynWordWrap()) {
+ m_viewInternal->prepareForDynWrapChange();
+
+ m_hasWrap = config()->dynWordWrap();
+
+ m_viewInternal->dynWrapChanged();
+
+ m_setDynWrapIndicators->setEnabled(config()->dynWordWrap());
+ m_toggleDynWrap->setChecked( config()->dynWordWrap() );
+ }
+
+ m_viewInternal->leftBorder->setDynWrapIndicators( config()->dynWordWrapIndicators() );
+ m_setDynWrapIndicators->setCurrentItem( config()->dynWordWrapIndicators() );
+
+ // line numbers
+ m_viewInternal->leftBorder->setLineNumbersOn( config()->lineNumbers() );
+ m_toggleLineNumbers->setChecked( config()->lineNumbers() );
+
+ // icon bar
+ m_viewInternal->leftBorder->setIconBorderOn( config()->iconBar() );
+ m_toggleIconBar->setChecked( config()->iconBar() );
+
+ // scrollbar marks
+ m_viewInternal->m_lineScroll->setShowMarks( config()->scrollBarMarks() );
+ m_toggleScrollBarMarks->setChecked( config()->scrollBarMarks() );
+
+ // cmd line
+ showCmdLine (config()->cmdLine());
+ //m_toggleCmdLine->setChecked( config()->cmdLine() );
+
+ // misc edit
+ m_toggleBlockSelection->setChecked( blockSelectionMode() );
+ m_toggleInsert->setChecked( isOverwriteMode() );
+
+ updateFoldingConfig ();
+
+ // bookmark
+ m_bookmarks->setSorting( (KateBookmarks::Sorting) config()->bookmarkSort() );
+
+ m_viewInternal->setAutoCenterLines(config()->autoCenterLines ());
+}
+
+void KateView::updateDocumentConfig()
+{
+ if (m_startingUp)
+ return;
+
+ m_updatingDocumentConfig = true;
+
+ m_setEndOfLine->setCurrentItem (m_doc->config()->eol());
+
+ m_updatingDocumentConfig = false;
+
+ m_viewInternal->updateView (true);
+
+ m_renderer->setTabWidth (m_doc->config()->tabWidth());
+ m_renderer->setIndentWidth (m_doc->config()->indentationWidth());
+}
+
+void KateView::updateRendererConfig()
+{
+ if (m_startingUp)
+ return;
+
+ m_toggleWWMarker->setChecked( m_renderer->config()->wordWrapMarker() );
+
+ // update the text area
+ m_viewInternal->updateView (true);
+ m_viewInternal->repaint ();
+
+ // update the left border right, for example linenumbers
+ m_viewInternal->leftBorder->updateFont();
+ m_viewInternal->leftBorder->repaint ();
+
+// @@ showIndentLines is not cached anymore.
+// m_renderer->setShowIndentLines (m_renderer->config()->showIndentationLines());
+}
+
+void KateView::updateFoldingConfig ()
+{
+ // folding bar
+ bool doit = config()->foldingBar() && m_doc->highlight() && m_doc->highlight()->allowsFolding();
+ m_viewInternal->leftBorder->setFoldingMarkersOn(doit);
+ m_toggleFoldingMarkers->setChecked( doit );
+ m_toggleFoldingMarkers->setEnabled( m_doc->highlight() && m_doc->highlight()->allowsFolding() );
+
+ QStringList l;
+
+ l << "folding_toplevel" << "folding_expandtoplevel"
+ << "folding_collapselocal" << "folding_expandlocal";
+
+ KAction *a = 0;
+ for (uint z = 0; z < l.size(); z++)
+ if ((a = actionCollection()->action( l[z].ascii() )))
+ a->setEnabled (m_doc->highlight() && m_doc->highlight()->allowsFolding());
+}
+
+//BEGIN EDIT STUFF
+void KateView::editStart ()
+{
+ m_viewInternal->editStart ();
+}
+
+void KateView::editEnd (int editTagLineStart, int editTagLineEnd, bool tagFrom)
+{
+ m_viewInternal->editEnd (editTagLineStart, editTagLineEnd, tagFrom);
+}
+
+void KateView::editSetCursor (const KateTextCursor &cursor)
+{
+ m_viewInternal->editSetCursor (cursor);
+}
+//END
+
+//BEGIN TAG & CLEAR
+bool KateView::tagLine (const KateTextCursor& virtualCursor)
+{
+ return m_viewInternal->tagLine (virtualCursor);
+}
+
+bool KateView::tagLines (int start, int end, bool realLines)
+{
+ return m_viewInternal->tagLines (start, end, realLines);
+}
+
+bool KateView::tagLines (KateTextCursor start, KateTextCursor end, bool realCursors)
+{
+ return m_viewInternal->tagLines (start, end, realCursors);
+}
+
+void KateView::tagAll ()
+{
+ m_viewInternal->tagAll ();
+}
+
+void KateView::clear ()
+{
+ m_viewInternal->clear ();
+}
+
+void KateView::repaintText (bool paintOnlyDirty)
+{
+ m_viewInternal->paintText(0,0,m_viewInternal->width(),m_viewInternal->height(), paintOnlyDirty);
+}
+
+void KateView::updateView (bool changed)
+{
+ m_viewInternal->updateView (changed);
+ m_viewInternal->leftBorder->update();
+}
+
+//END
+
+void KateView::slotHlChanged()
+{
+ KateHighlighting *hl = m_doc->highlight();
+ bool ok ( !hl->getCommentStart(0).isEmpty() || !hl->getCommentSingleLineStart(0).isEmpty() );
+
+ if (actionCollection()->action("tools_comment"))
+ actionCollection()->action("tools_comment")->setEnabled( ok );
+
+ if (actionCollection()->action("tools_uncomment"))
+ actionCollection()->action("tools_uncomment")->setEnabled( ok );
+
+ // show folding bar if "view defaults" says so, otherwise enable/disable only the menu entry
+ updateFoldingConfig ();
+}
+
+uint KateView::cursorColumn()
+{
+ uint r = m_doc->currentColumn(m_viewInternal->getCursor());
+ if ( !( m_doc->config()->configFlags() & KateDocumentConfig::cfWrapCursor ) &&
+ (uint)m_viewInternal->getCursor().col() > m_doc->textLine( m_viewInternal->getCursor().line() ).length() )
+ r += m_viewInternal->getCursor().col() - m_doc->textLine( m_viewInternal->getCursor().line() ).length();
+
+ return r;
+}
+
+//BEGIN KTextEditor::SelectionInterface stuff
+
+bool KateView::setSelection( const KateTextCursor& start, const KateTextCursor& end )
+{
+ KateTextCursor oldSelectStart = selectStart;
+ KateTextCursor oldSelectEnd = selectEnd;
+
+ if (start <= end) {
+ selectStart.setPos(start);
+ selectEnd.setPos(end);
+ } else {
+ selectStart.setPos(end);
+ selectEnd.setPos(start);
+ }
+
+ tagSelection(oldSelectStart, oldSelectEnd);
+
+ repaintText(true);
+
+ emit selectionChanged ();
+ emit m_doc->selectionChanged ();
+
+ return true;
+}
+
+bool KateView::setSelection( uint startLine, uint startCol, uint endLine, uint endCol )
+{
+ if (hasSelection())
+ clearSelection(false, false);
+
+ return setSelection( KateTextCursor(startLine, startCol), KateTextCursor(endLine, endCol) );
+}
+
+void KateView::syncSelectionCache()
+{
+ m_viewInternal->selStartCached = selectStart;
+ m_viewInternal->selEndCached = selectEnd;
+ m_viewInternal->selectAnchor = selectEnd;
+}
+
+bool KateView::clearSelection()
+{
+ return clearSelection(true);
+}
+
+bool KateView::clearSelection(bool redraw, bool finishedChangingSelection)
+{
+ if( !hasSelection() )
+ return false;
+
+ KateTextCursor oldSelectStart = selectStart;
+ KateTextCursor oldSelectEnd = selectEnd;
+
+ selectStart.setPos(-1, -1);
+ selectEnd.setPos(-1, -1);
+
+ tagSelection(oldSelectStart, oldSelectEnd);
+
+ oldSelectStart = selectStart;
+ oldSelectEnd = selectEnd;
+
+ if (redraw)
+ repaintText(true);
+
+ if (finishedChangingSelection)
+ {
+ emit selectionChanged();
+ emit m_doc->selectionChanged ();
+ }
+
+ return true;
+}
+
+bool KateView::hasSelection() const
+{
+ return selectStart != selectEnd;
+}
+
+QString KateView::selection() const
+{
+ int sc = selectStart.col();
+ int ec = selectEnd.col();
+
+ if ( blockSelect )
+ {
+ if (sc > ec)
+ {
+ uint tmp = sc;
+ sc = ec;
+ ec = tmp;
+ }
+ }
+ return m_doc->text (selectStart.line(), sc, selectEnd.line(), ec, blockSelect);
+}
+
+bool KateView::removeSelectedText ()
+{
+ if (!hasSelection())
+ return false;
+
+ m_doc->editStart ();
+
+ int sc = selectStart.col();
+ int ec = selectEnd.col();
+
+ if ( blockSelect )
+ {
+ if (sc > ec)
+ {
+ uint tmp = sc;
+ sc = ec;
+ ec = tmp;
+ }
+ }
+
+ m_doc->removeText (selectStart.line(), sc, selectEnd.line(), ec, blockSelect);
+
+ // don't redraw the cleared selection - that's done in editEnd().
+ clearSelection(false);
+
+ m_doc->editEnd ();
+
+ return true;
+}
+
+bool KateView::selectAll()
+{
+ setBlockSelectionMode (false);
+
+ return setSelection (0, 0, m_doc->lastLine(), m_doc->lineLength(m_doc->lastLine()));
+}
+
+bool KateView::lineColSelected (int line, int col)
+{
+ if ( (!blockSelect) && (col < 0) )
+ col = 0;
+
+ KateTextCursor cursor(line, col);
+
+ if (blockSelect)
+ return cursor.line() >= selectStart.line() && cursor.line() <= selectEnd.line() && cursor.col() >= selectStart.col() && cursor.col() < selectEnd.col();
+ else
+ return (cursor >= selectStart) && (cursor < selectEnd);
+}
+
+bool KateView::lineSelected (int line)
+{
+ return (!blockSelect)
+ && (selectStart <= KateTextCursor(line, 0))
+ && (line < selectEnd.line());
+}
+
+bool KateView::lineEndSelected (int line, int endCol)
+{
+ return (!blockSelect)
+ && (line > selectStart.line() || (line == selectStart.line() && (selectStart.col() < endCol || endCol == -1)))
+ && (line < selectEnd.line() || (line == selectEnd.line() && (endCol <= selectEnd.col() && endCol != -1)));
+}
+
+bool KateView::lineHasSelected (int line)
+{
+ return (selectStart < selectEnd)
+ && (line >= selectStart.line())
+ && (line <= selectEnd.line());
+}
+
+bool KateView::lineIsSelection (int line)
+{
+ return (line == selectStart.line() && line == selectEnd.line());
+}
+
+void KateView::tagSelection(const KateTextCursor &oldSelectStart, const KateTextCursor &oldSelectEnd)
+{
+ if (hasSelection()) {
+ if (oldSelectStart.line() == -1) {
+ // We have to tag the whole lot if
+ // 1) we have a selection, and:
+ // a) it's new; or
+ tagLines(selectStart, selectEnd, true);
+
+ } else if (blockSelectionMode() && (oldSelectStart.col() != selectStart.col() || oldSelectEnd.col() != selectEnd.col())) {
+ // b) we're in block selection mode and the columns have changed
+ tagLines(selectStart, selectEnd, true);
+ tagLines(oldSelectStart, oldSelectEnd, true);
+
+ } else {
+ if (oldSelectStart != selectStart) {
+ if (oldSelectStart < selectStart)
+ tagLines(oldSelectStart, selectStart, true);
+ else
+ tagLines(selectStart, oldSelectStart, true);
+ }
+
+ if (oldSelectEnd != selectEnd) {
+ if (oldSelectEnd < selectEnd)
+ tagLines(oldSelectEnd, selectEnd, true);
+ else
+ tagLines(selectEnd, oldSelectEnd, true);
+ }
+ }
+
+ } else {
+ // No more selection, clean up
+ tagLines(oldSelectStart, oldSelectEnd, true);
+ }
+}
+
+void KateView::selectWord( const KateTextCursor& cursor )
+{
+ int start, end, len;
+
+ KateTextLine::Ptr textLine = m_doc->plainKateTextLine(cursor.line());
+
+ if (!textLine)
+ return;
+
+ len = textLine->length();
+ start = end = cursor.col();
+ while (start > 0 && m_doc->highlight()->isInWord(textLine->getChar(start - 1), textLine->attribute(start - 1))) start--;
+ while (end < len && m_doc->highlight()->isInWord(textLine->getChar(end), textLine->attribute(start - 1))) end++;
+ if (end <= start) return;
+
+ setSelection (cursor.line(), start, cursor.line(), end);
+}
+
+void KateView::selectLine( const KateTextCursor& cursor )
+{
+ if (cursor.line()+1 >= m_doc->numLines())
+ setSelection (cursor.line(), 0, cursor.line(), m_doc->lineLength(cursor.line()));
+ else
+ setSelection (cursor.line(), 0, cursor.line()+1, 0);
+}
+
+void KateView::selectLength( const KateTextCursor& cursor, int length )
+{
+ int start, end;
+
+ KateTextLine::Ptr textLine = m_doc->plainKateTextLine(cursor.line());
+
+ if (!textLine)
+ return;
+
+ start = cursor.col();
+ end = start + length;
+ if (end <= start) return;
+
+ setSelection (cursor.line(), start, cursor.line(), end);
+}
+
+void KateView::paste()
+{
+ m_doc->paste( this );
+ emit selectionChanged();
+ m_viewInternal->repaint();
+}
+
+void KateView::cut()
+{
+ if (!hasSelection())
+ return;
+
+ copy();
+ removeSelectedText();
+}
+
+void KateView::copy() const
+{
+ if (!hasSelection())
+ return;
+
+ QApplication::clipboard()->setText(selection ());
+}
+
+void KateView::copyHTML()
+{
+ if (!hasSelection())
+ return;
+
+ KMultipleDrag *drag = new KMultipleDrag();
+
+ QTextDrag *htmltextdrag = new QTextDrag(selectionAsHtml()) ;
+ htmltextdrag->setSubtype("html");
+
+ drag->addDragObject( htmltextdrag);
+ drag->addDragObject( new QTextDrag( selection()));
+
+ QApplication::clipboard()->setData(drag);
+}
+
+QString KateView::selectionAsHtml()
+{
+ int sc = selectStart.col();
+ int ec = selectEnd.col();
+
+ if ( blockSelect )
+ {
+ if (sc > ec)
+ {
+ uint tmp = sc;
+ sc = ec;
+ ec = tmp;
+ }
+ }
+
+ return textAsHtml (selectStart.line(), sc, selectEnd.line(), ec, blockSelect);
+}
+
+QString KateView::textAsHtml ( uint startLine, uint startCol, uint endLine, uint endCol, bool blockwise)
+{
+ kdDebug(13020) << "textAsHtml" << endl;
+ if ( blockwise && (startCol > endCol) )
+ return QString ();
+
+ QString s;
+ QTextStream ts( &s, IO_WriteOnly );
+ ts.setEncoding(QTextStream::UnicodeUTF8);
+ ts << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\">" << endl;
+ ts << "<html xmlns=\"http://www.w3.org/1999/xhtml\">" << endl;
+ ts << "<head>" << endl;
+ ts << "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />" << endl;
+ ts << "<meta name=\"Generator\" content=\"Kate, the KDE Advanced Text Editor\" />" << endl;
+ ts << "</head>" << endl;
+
+ ts << "<body>" << endl;
+ textAsHtmlStream(startLine, startCol, endLine, endCol, blockwise, &ts);
+
+ ts << "</body>" << endl;
+ ts << "</html>" << endl;
+ kdDebug(13020) << "html is: " << s << endl;
+ return s;
+}
+
+void KateView::textAsHtmlStream ( uint startLine, uint startCol, uint endLine, uint endCol, bool blockwise, QTextStream *ts)
+{
+ if ( (blockwise || startLine == endLine) && (startCol > endCol) )
+ return;
+
+ if (startLine == endLine)
+ {
+ KateTextLine::Ptr textLine = m_doc->kateTextLine(startLine);
+ if ( !textLine )
+ return;
+
+ (*ts) << "<pre>" << endl;
+
+ lineAsHTML(textLine, startCol, endCol-startCol, ts);
+ }
+ else
+ {
+ (*ts) << "<pre>" << endl;
+
+ for (uint i = startLine; (i <= endLine) && (i < m_doc->numLines()); i++)
+ {
+ KateTextLine::Ptr textLine = m_doc->kateTextLine(i);
+
+ if ( !blockwise )
+ {
+ if (i == startLine)
+ lineAsHTML(textLine, startCol, textLine->length()-startCol, ts);
+ else if (i == endLine)
+ lineAsHTML(textLine, 0, endCol, ts);
+ else
+ lineAsHTML(textLine, 0, textLine->length(), ts);
+ }
+ else
+ {
+ lineAsHTML( textLine, startCol, endCol-startCol, ts);
+ }
+
+ if ( i < endLine )
+ (*ts) << "\n"; //we are inside a <pre>, so a \n is a new line
+ }
+ }
+ (*ts) << "</pre>";
+}
+
+// fully rewritten to use only inline CSS and support all used attribs.
+// anders, 2005-11-01 23:39:43
+void KateView::lineAsHTML (KateTextLine::Ptr line, uint startCol, uint length, QTextStream *outputStream)
+{
+ if(length == 0)
+ return;
+
+ // do not recalculate the style strings again and again
+ QMap<uchar,QString> stylecache;
+ // do not insert equally styled characters one by one
+ QString textcache;
+
+ KateAttribute *charAttributes = 0;
+
+ for (uint curPos=startCol;curPos<(length+startCol);curPos++)
+ {
+ if ( curPos == 0 || line->attribute( curPos ) != line->attribute( curPos - 1 ) &&
+ // Since many highlight files contains itemdatas that have the exact
+ // same styles, join those to keep the HTML text size down
+ KateAttribute(*charAttributes) != KateAttribute(*m_renderer->attribute(line->attribute(curPos))) )
+ {
+ (*outputStream) << textcache;
+ textcache.truncate(0);
+
+ if ( curPos > startCol )
+ (*outputStream) << "</span>";
+
+ charAttributes = m_renderer->attribute(line->attribute(curPos));
+
+ if ( ! stylecache.contains( line->attribute(curPos) ) )
+ {
+ QString textdecoration;
+ QString style;
+
+ if ( charAttributes->bold() )
+ style.append("font-weight: bold;");
+ if ( charAttributes->italic() )
+ style.append("font-style: italic;");
+ if ( charAttributes->underline() )
+ textdecoration = "underline";
+ if ( charAttributes->overline() )
+ textdecoration.append(" overline" );
+ if ( charAttributes->strikeOut() )
+ textdecoration.append(" line-trough" );
+ if ( !textdecoration.isEmpty() )
+ style.append("text-decoration: %1;").arg(textdecoration);
+ // QColor::name() returns a string in the form "#RRGGBB" in Qt 3.
+ // NOTE Qt 4 returns "#AARRGGBB"
+ if ( charAttributes->itemSet(KateAttribute::BGColor) )
+ style.append(QString("background-color: %1;").arg(charAttributes->bgColor().name()));
+ if ( charAttributes->itemSet(KateAttribute::TextColor) )
+ style.append(QString("color: %1;").arg(charAttributes->textColor().name()));
+
+ stylecache[line->attribute(curPos)] = style;
+ }
+ (*outputStream)<<"<span style=\""
+ << stylecache[line->attribute(curPos)]
+ << "\">";
+ }
+
+ QString s( line->getChar(curPos) );
+ if ( s == "&" ) s = "&amp;";
+ else if ( s == "<" ) s = "&lt;";
+ else if ( s == ">" ) s = "&gt;";
+ textcache.append( s );
+ }
+
+ (*outputStream) << textcache << "</span>";
+}
+
+void KateView::exportAsHTML ()
+{
+ KURL url = KFileDialog::getSaveURL(m_doc->docName(),"text/html",0,i18n("Export File as HTML"));
+
+ if ( url.isEmpty() )
+ return;
+
+ QString filename;
+ KTempFile tmp; // ### only used for network export
+
+ if ( url.isLocalFile() )
+ filename = url.path();
+ else
+ filename = tmp.name();
+
+ KSaveFile *savefile=new KSaveFile(filename);
+ if (!savefile->status())
+ {
+ QTextStream *outputStream = savefile->textStream();
+
+ outputStream->setEncoding(QTextStream::UnicodeUTF8);
+
+ // let's write the HTML header :
+ (*outputStream) << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
+ (*outputStream) << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\">" << endl;
+ (*outputStream) << "<html xmlns=\"http://www.w3.org/1999/xhtml\">" << endl;
+ (*outputStream) << "<head>" << endl;
+ (*outputStream) << "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />" << endl;
+ (*outputStream) << "<meta name=\"Generator\" content=\"Kate, the KDE Advanced Text Editor\" />" << endl;
+ // for the title, we write the name of the file (/usr/local/emmanuel/myfile.cpp -> myfile.cpp)
+ (*outputStream) << "<title>" << m_doc->docName () << "</title>" << endl;
+ (*outputStream) << "</head>" << endl;
+ (*outputStream) << "<body>" << endl;
+
+ textAsHtmlStream(0,0, m_doc->lastLine(), m_doc->lineLength(m_doc->lastLine()), false, outputStream);
+
+ (*outputStream) << "</body>" << endl;
+ (*outputStream) << "</html>" << endl;
+
+
+ savefile->close();
+ //if (!savefile->status()) --> Error
+ }
+// else
+// {/*ERROR*/}
+ delete savefile;
+
+ if ( url.isLocalFile() )
+ return;
+
+ KIO::NetAccess::upload( filename, url, 0 );
+}
+//END
+
+//BEGIN KTextEditor::BlockSelectionInterface stuff
+
+bool KateView::blockSelectionMode ()
+{
+ return blockSelect;
+}
+
+bool KateView::setBlockSelectionMode (bool on)
+{
+ if (on != blockSelect)
+ {
+ blockSelect = on;
+
+ KateTextCursor oldSelectStart = selectStart;
+ KateTextCursor oldSelectEnd = selectEnd;
+
+ clearSelection(false, false);
+
+ setSelection(oldSelectStart, oldSelectEnd);
+
+ slotSelectionTypeChanged();
+ }
+
+ return true;
+}
+
+bool KateView::toggleBlockSelectionMode ()
+{
+ m_toggleBlockSelection->setChecked (!blockSelect);
+ return setBlockSelectionMode (!blockSelect);
+}
+
+bool KateView::wrapCursor ()
+{
+ return !blockSelectionMode() && (m_doc->configFlags() & KateDocument::cfWrapCursor);
+}
+
+//END
+
+//BEGIN IM INPUT STUFF
+void KateView::setIMSelectionValue( uint imStartLine, uint imStart, uint imEnd,
+ uint imSelStart, uint imSelEnd, bool imComposeEvent )
+{
+ m_imStartLine = imStartLine;
+ m_imStart = imStart;
+ m_imEnd = imEnd;
+ m_imSelStart = imSelStart;
+ m_imSelEnd = imSelEnd;
+ m_imComposeEvent = imComposeEvent;
+}
+
+bool KateView::isIMSelection( int _line, int _column )
+{
+ return ( ( int( m_imStartLine ) == _line ) && ( m_imSelStart < m_imSelEnd ) && ( _column >= int( m_imSelStart ) ) &&
+ ( _column < int( m_imSelEnd ) ) );
+}
+
+bool KateView::isIMEdit( int _line, int _column )
+{
+ return ( ( int( m_imStartLine ) == _line ) && ( m_imStart < m_imEnd ) && ( _column >= int( m_imStart ) ) &&
+ ( _column < int( m_imEnd ) ) );
+}
+
+void KateView::getIMSelectionValue( uint *imStartLine, uint *imStart, uint *imEnd,
+ uint *imSelStart, uint *imSelEnd )
+{
+ *imStartLine = m_imStartLine;
+ *imStart = m_imStart;
+ *imEnd = m_imEnd;
+ *imSelStart = m_imSelStart;
+ *imSelEnd = m_imSelEnd;
+}
+//END IM INPUT STUFF
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/kateview.h b/kate/part/kateview.h
new file mode 100644
index 000000000..0e2f310b8
--- /dev/null
+++ b/kate/part/kateview.h
@@ -0,0 +1,571 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org>
+ Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
+ Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef kate_view_h
+#define kate_view_h
+
+#include "katedocument.h"
+#include "kateviewinternal.h"
+#include "kateconfig.h"
+
+#include "../interfaces/view.h"
+
+#include <ktexteditor/sessionconfiginterface.h>
+#include <ktexteditor/viewstatusmsginterface.h>
+#include <ktexteditor/texthintinterface.h>
+
+#include <qguardedptr.h>
+
+class KateDocument;
+class KateBookmarks;
+class KateSearch;
+class KateCmdLine;
+class KateCodeCompletion;
+class KateViewConfig;
+class KateViewSchemaAction;
+class KateRenderer;
+class KateSpell;
+
+class KToggleAction;
+class KAction;
+class KRecentFilesAction;
+class KSelectAction;
+
+class QGridLayout;
+
+//
+// Kate KTextEditor::View class ;)
+//
+class KateView : public Kate::View,
+ public KTextEditor::SessionConfigInterface,
+ public KTextEditor::ViewStatusMsgInterface,
+ public KTextEditor::TextHintInterface,
+ public KTextEditor::SelectionInterface,
+ public KTextEditor::SelectionInterfaceExt,
+ public KTextEditor::BlockSelectionInterface
+{
+ Q_OBJECT
+
+ friend class KateViewInternal;
+ friend class KateIconBorder;
+ friend class KateCodeCompletion;
+
+ public:
+ KateView( KateDocument* doc, QWidget* parent = 0L, const char* name = 0 );
+ ~KateView ();
+
+ //
+ // KTextEditor::View
+ //
+ public:
+ KTextEditor::Document* document() const { return m_doc; }
+
+ //
+ // KTextEditor::ClipboardInterface
+ //
+ public slots:
+ // TODO: Factor out of m_viewInternal
+ void paste();
+ void cut();
+ void copy() const;
+
+ /**
+ * internal use, copy text as HTML to clipboard
+ */
+ void copyHTML();
+
+ // helper to export text as html stuff
+ private:
+ QString selectionAsHtml ();
+ QString textAsHtml ( uint startLine, uint startCol, uint endLine, uint endCol, bool blockwise);
+ void textAsHtmlStream ( uint startLine, uint startCol, uint endLine, uint endCol, bool blockwise, QTextStream *ts);
+
+ /**
+ * Gets a substring in valid-xml html.
+ * Example: "<b>const</b> b = <i>34</i>"
+ * It won't contain <p> or <body> or <html> or anything like that.
+ *
+ * @param startCol start column of substring
+ * @param length length of substring
+ * @param renderer The katerenderer. This will have the schema
+ * information that describes how to render the
+ * attributes.
+ * @param outputStream A stream to write the html to
+ */
+ void lineAsHTML (KateTextLine::Ptr line, uint startCol, uint length, QTextStream *outputStream);
+
+ public slots:
+ void exportAsHTML ();
+
+ //
+ // KTextEditor::PopupMenuInterface
+ //
+ public:
+ void installPopup( QPopupMenu* menu ) { m_rmbMenu = menu; }
+ QPopupMenu* popup() const { return m_rmbMenu; }
+
+ //
+ // KTextEditor::ViewCursorInterface
+ //
+ public slots:
+ QPoint cursorCoordinates()
+ { return m_viewInternal->cursorCoordinates(); }
+ void cursorPosition( uint* l, uint* c )
+ { if( l ) *l = cursorLine(); if( c ) *c = cursorColumn(); }
+ void cursorPositionReal( uint* l, uint* c )
+ { if( l ) *l = cursorLine(); if( c ) *c = cursorColumnReal(); }
+ bool setCursorPosition( uint line, uint col )
+ { return setCursorPositionInternal( line, col, tabWidth(), true ); }
+ bool setCursorPositionReal( uint line, uint col)
+ { return setCursorPositionInternal( line, col, 1, true ); }
+ uint cursorLine()
+ { return m_viewInternal->getCursor().line(); }
+ uint cursorColumn();
+ uint cursorColumnReal()
+ { return m_viewInternal->getCursor().col(); }
+
+ signals:
+ void cursorPositionChanged();
+
+ //
+ // KTextEditor::CodeCompletionInterface
+ //
+ public slots:
+ void showArgHint( QStringList arg1, const QString& arg2, const QString& arg3 );
+ void showCompletionBox( QValueList<KTextEditor::CompletionEntry> arg1, int offset = 0, bool cs = true );
+
+ signals:
+ void completionAborted();
+ void completionDone();
+ void argHintHidden();
+ void completionDone(KTextEditor::CompletionEntry);
+ void filterInsertString(KTextEditor::CompletionEntry*,QString *);
+ void aboutToShowCompletionBox();
+
+ //
+ // KTextEditor::TextHintInterface
+ //
+ public:
+ void enableTextHints(int timeout);
+ void disableTextHints();
+
+ signals:
+ void needTextHint(int line, int col, QString &text);
+
+ //
+ // KTextEditor::DynWordWrapInterface
+ //
+ public:
+ void setDynWordWrap( bool b );
+ bool dynWordWrap() const { return m_hasWrap; }
+
+ //
+ // KTextEditor::SelectionInterface stuff
+ //
+ public slots:
+ bool setSelection ( const KateTextCursor & start,
+ const KateTextCursor & end );
+ bool setSelection ( uint startLine, uint startCol,
+ uint endLine, uint endCol );
+ bool clearSelection ();
+ bool clearSelection (bool redraw, bool finishedChangingSelection = true);
+
+ bool hasSelection () const;
+ QString selection () const ;
+
+ bool removeSelectedText ();
+
+ bool selectAll();
+
+ //
+ // KTextEditor::SelectionInterfaceExt
+ //
+ int selStartLine() { return selectStart.line(); };
+ int selStartCol() { return selectStart.col(); };
+ int selEndLine() { return selectEnd.line(); };
+ int selEndCol() { return selectEnd.col(); };
+
+ signals:
+ void selectionChanged ();
+
+ //
+ // internal helper stuff, for katerenderer and so on
+ //
+ public:
+ /**
+ * accessors to the selection start
+ * @return selection start cursor (read-only)
+ */
+ inline const KateSuperCursor &selStart () const { return selectStart; }
+
+ /**
+ * accessors to the selection end
+ * @return selection end cursor (read-only)
+ */
+ inline const KateSuperCursor &selEnd () const { return selectEnd; }
+
+ // should cursor be wrapped ? take config + blockselection state in account
+ bool wrapCursor ();
+
+ // some internal functions to get selection state of a line/col
+ bool lineColSelected (int line, int col);
+ bool lineSelected (int line);
+ bool lineEndSelected (int line, int endCol);
+ bool lineHasSelected (int line);
+ bool lineIsSelection (int line);
+
+ void tagSelection (const KateTextCursor &oldSelectStart, const KateTextCursor &oldSelectEnd);
+
+ void selectWord( const KateTextCursor& cursor );
+ void selectLine( const KateTextCursor& cursor );
+ void selectLength( const KateTextCursor& cursor, int length );
+
+ // this method will sync the KateViewInternal's sel{Start,End}Cached and selectAnchor
+ // with the current selection, to make it "stick" instead of reverting back to sel*Cached
+ void syncSelectionCache();
+
+ //
+ // KTextEditor::BlockSelectionInterface stuff
+ //
+ public slots:
+ bool blockSelectionMode ();
+ bool setBlockSelectionMode (bool on);
+ bool toggleBlockSelectionMode ();
+
+
+ //BEGIN EDIT STUFF
+ public:
+ void editStart ();
+ void editEnd (int editTagLineStart, int editTagLineEnd, bool tagFrom);
+
+ void editSetCursor (const KateTextCursor &cursor);
+ //END
+
+ //BEGIN TAG & CLEAR
+ public:
+ bool tagLine (const KateTextCursor& virtualCursor);
+
+ bool tagLines (int start, int end, bool realLines = false );
+ bool tagLines (KateTextCursor start, KateTextCursor end, bool realCursors = false);
+
+ void tagAll ();
+
+ void clear ();
+
+ void repaintText (bool paintOnlyDirty = false);
+
+ void updateView (bool changed = false);
+ //END
+
+ //
+ // Kate::View
+ //
+ public:
+ bool isOverwriteMode() const;
+ void setOverwriteMode( bool b );
+
+ QString currentTextLine()
+ { return getDoc()->textLine( cursorLine() ); }
+ QString currentWord()
+ { return m_doc->getWord( m_viewInternal->getCursor() ); }
+ void insertText( const QString& mark )
+ { getDoc()->insertText( cursorLine(), cursorColumnReal(), mark ); }
+ bool canDiscard();
+ int tabWidth() { return m_doc->config()->tabWidth(); }
+ void setTabWidth( int w ) { m_doc->config()->setTabWidth(w); }
+ void setEncoding( QString e ) { m_doc->setEncoding(e); }
+ bool isLastView() { return m_doc->isLastView(1); }
+
+ public slots:
+ void flush();
+ saveResult save();
+ saveResult saveAs();
+
+ void indent() { m_doc->indent( this, cursorLine(), 1 ); }
+ void unIndent() { m_doc->indent( this, cursorLine(), -1 ); }
+ void cleanIndent() { m_doc->indent( this, cursorLine(), 0 ); }
+ void align() { m_doc->align( this, cursorLine() ); }
+ void comment() { m_doc->comment( this, cursorLine(), cursorColumnReal(), 1 ); }
+ void uncomment() { m_doc->comment( this, cursorLine(), cursorColumnReal(),-1 ); }
+ void killLine() { m_doc->removeLine( cursorLine() ); }
+
+ /**
+ Uppercases selected text, or an alphabetic character next to the cursor.
+ */
+ void uppercase() { m_doc->transform( this, m_viewInternal->cursor, KateDocument::Uppercase ); }
+ /**
+ Lowercases selected text, or an alphabetic character next to the cursor.
+ */
+ void lowercase() { m_doc->transform( this, m_viewInternal->cursor, KateDocument::Lowercase ); }
+ /**
+ Capitalizes the selection (makes each word start with an uppercase) or
+ the word under the cursor.
+ */
+ void capitalize() { m_doc->transform( this, m_viewInternal->cursor, KateDocument::Capitalize ); }
+ /**
+ Joins lines touched by the selection
+ */
+ void joinLines();
+
+
+ void keyReturn() { m_viewInternal->doReturn(); }
+ void backspace() { m_viewInternal->doBackspace(); }
+ void deleteWordLeft() { m_viewInternal->doDeleteWordLeft(); }
+ void keyDelete() { m_viewInternal->doDelete(); }
+ void deleteWordRight() { m_viewInternal->doDeleteWordRight(); }
+ void transpose() { m_viewInternal->doTranspose(); }
+ void cursorLeft() { m_viewInternal->cursorLeft(); }
+ void shiftCursorLeft() { m_viewInternal->cursorLeft(true); }
+ void cursorRight() { m_viewInternal->cursorRight(); }
+ void shiftCursorRight() { m_viewInternal->cursorRight(true); }
+ void wordLeft() { m_viewInternal->wordLeft(); }
+ void shiftWordLeft() { m_viewInternal->wordLeft(true); }
+ void wordRight() { m_viewInternal->wordRight(); }
+ void shiftWordRight() { m_viewInternal->wordRight(true); }
+ void home() { m_viewInternal->home(); }
+ void shiftHome() { m_viewInternal->home(true); }
+ void end() { m_viewInternal->end(); }
+ void shiftEnd() { m_viewInternal->end(true); }
+ void up() { m_viewInternal->cursorUp(); }
+ void shiftUp() { m_viewInternal->cursorUp(true); }
+ void down() { m_viewInternal->cursorDown(); }
+ void shiftDown() { m_viewInternal->cursorDown(true); }
+ void scrollUp() { m_viewInternal->scrollUp(); }
+ void scrollDown() { m_viewInternal->scrollDown(); }
+ void topOfView() { m_viewInternal->topOfView(); }
+ void shiftTopOfView() { m_viewInternal->topOfView(true); }
+ void bottomOfView() { m_viewInternal->bottomOfView(); }
+ void shiftBottomOfView() { m_viewInternal->bottomOfView(true); }
+ void pageUp() { m_viewInternal->pageUp(); }
+ void shiftPageUp() { m_viewInternal->pageUp(true); }
+ void pageDown() { m_viewInternal->pageDown(); }
+ void shiftPageDown() { m_viewInternal->pageDown(true); }
+ void top() { m_viewInternal->top_home(); }
+ void shiftTop() { m_viewInternal->top_home(true); }
+ void bottom() { m_viewInternal->bottom_end(); }
+ void shiftBottom() { m_viewInternal->bottom_end(true); }
+ void toMatchingBracket() { m_viewInternal->cursorToMatchingBracket();}
+ void shiftToMatchingBracket() { m_viewInternal->cursorToMatchingBracket(true);}
+
+ void gotoLine();
+ void gotoLineNumber( int linenumber );
+
+ // config file / session management functions
+ public:
+ void readSessionConfig(KConfig *);
+ void writeSessionConfig(KConfig *);
+
+ public slots:
+ int getEol();
+ void setEol( int eol );
+ void find();
+ void find( const QString&, long, bool add=true ); ///< proxy for KateSearch
+ void replace();
+ void replace( const QString&, const QString &, long ); ///< proxy for KateSearch
+ /** Highly confusing but KateSearch::findAgain() is backwards too. */
+ void findAgain( bool back );
+ void findAgain() { findAgain( false ); }
+ void findPrev() { findAgain( true ); }
+
+ void setFoldingMarkersOn( bool enable ); // Not in Kate::View, but should be
+ void setIconBorder( bool enable );
+ void setLineNumbersOn( bool enable );
+ void setScrollBarMarks( bool enable );
+ void showCmdLine ( bool enable );
+ void toggleFoldingMarkers();
+ void toggleIconBorder();
+ void toggleLineNumbersOn();
+ void toggleScrollBarMarks();
+ void toggleDynWordWrap ();
+ void toggleCmdLine ();
+ void setDynWrapIndicators(int mode);
+
+ void applyWordWrap ();
+
+ public:
+ KateRenderer *renderer ();
+
+ bool iconBorder();
+ bool lineNumbersOn();
+ bool scrollBarMarks();
+ int dynWrapIndicators();
+ bool foldingMarkersOn();
+ Kate::Document* getDoc() { return m_doc; }
+
+ void setActive( bool b ) { m_active = b; }
+ bool isActive() { return m_active; }
+
+ public slots:
+ void gotoMark( KTextEditor::Mark* mark ) { setCursorPositionInternal ( mark->line, 0, 1 ); }
+ void slotSelectionChanged ();
+
+ signals:
+ void gotFocus( Kate::View* );
+ void lostFocus( Kate::View* );
+ void newStatus(); // Not in Kate::View, but should be (Kate app connects to it)
+
+ //
+ // Extras
+ //
+ public:
+ // Is it really necessary to have 3 methods for this?! :)
+ KateDocument* doc() const { return m_doc; }
+
+ KActionCollection* editActionCollection() const { return m_editActions; }
+
+ public slots:
+ void slotNewUndo();
+ void slotUpdate();
+ void toggleInsert();
+ void reloadFile();
+ void toggleWWMarker();
+ void toggleWriteLock();
+ void switchToCmdLine ();
+ void slotReadWriteChanged ();
+
+ signals:
+ void dropEventPass(QDropEvent*);
+ void viewStatusMsg (const QString &msg);
+
+ public:
+ bool setCursorPositionInternal( uint line, uint col, uint tabwidth = 1, bool calledExternally = false );
+
+ protected:
+ void contextMenuEvent( QContextMenuEvent* );
+ bool checkOverwrite( KURL );
+
+ public slots:
+ void slotSelectionTypeChanged();
+
+ private slots:
+ void slotGotFocus();
+ void slotLostFocus();
+ void slotDropEventPass( QDropEvent* ev );
+ void slotStatusMsg();
+ void slotSaveCanceled( const QString& error );
+ void slotExpandToplevel();
+ void slotCollapseLocal();
+ void slotExpandLocal();
+
+ private:
+ void setupConnections();
+ void setupActions();
+ void setupEditActions();
+ void setupCodeFolding();
+ void setupCodeCompletion();
+
+ KActionCollection* m_editActions;
+ KAction* m_editUndo;
+ KAction* m_editRedo;
+ KRecentFilesAction* m_fileRecent;
+ KToggleAction* m_toggleFoldingMarkers;
+ KToggleAction* m_toggleIconBar;
+ KToggleAction* m_toggleLineNumbers;
+ KToggleAction* m_toggleScrollBarMarks;
+ KToggleAction* m_toggleDynWrap;
+ KSelectAction* m_setDynWrapIndicators;
+ KToggleAction* m_toggleWWMarker;
+ KAction* m_switchCmdLine;
+
+ KSelectAction* m_setEndOfLine;
+
+ KAction *m_cut;
+ KAction *m_copy;
+ KAction *m_copyHTML;
+ KAction *m_paste;
+ KAction *m_selectAll;
+ KAction *m_deSelect;
+
+ KToggleAction *m_toggleBlockSelection;
+ KToggleAction *m_toggleInsert;
+ KToggleAction *m_toggleWriteLock;
+
+ KateDocument* m_doc;
+ KateViewInternal* m_viewInternal;
+ KateRenderer* m_renderer;
+ KateSearch* m_search;
+ KateSpell *m_spell;
+ KateBookmarks* m_bookmarks;
+ QGuardedPtr<QPopupMenu> m_rmbMenu;
+ KateCodeCompletion* m_codeCompletion;
+
+ KateCmdLine *m_cmdLine;
+ bool m_cmdLineOn;
+
+ QGridLayout *m_grid;
+
+ bool m_active;
+ bool m_hasWrap;
+
+ private slots:
+ void slotNeedTextHint(int line, int col, QString &text);
+ void slotHlChanged();
+
+ /**
+ * Configuration
+ */
+ public:
+ inline KateViewConfig *config () { return m_config; };
+
+ void updateConfig ();
+
+ void updateDocumentConfig();
+
+ void updateRendererConfig();
+
+ private slots:
+ void updateFoldingConfig ();
+
+ private:
+ KateViewConfig *m_config;
+ bool m_startingUp;
+ bool m_updatingDocumentConfig;
+
+ private:
+ // stores the current selection
+ KateSuperCursor selectStart;
+ KateSuperCursor selectEnd;
+
+ // do we select normal or blockwise ?
+ bool blockSelect;
+
+ /**
+ * IM input stuff
+ */
+ public:
+ void setIMSelectionValue( uint imStartLine, uint imStart, uint imEnd,
+ uint imSelStart, uint imSelEnd, bool m_imComposeEvent );
+ void getIMSelectionValue( uint *imStartLine, uint *imStart, uint *imEnd,
+ uint *imSelStart, uint *imSelEnd );
+ bool isIMSelection( int _line, int _column );
+ bool isIMEdit( int _line, int _column );
+ bool imComposeEvent () const { return m_imComposeEvent; }
+
+ private:
+ uint m_imStartLine;
+ uint m_imStart;
+ uint m_imEnd;
+ uint m_imSelStart;
+ uint m_imSelEnd;
+ bool m_imComposeEvent;
+};
+
+#endif
diff --git a/kate/part/kateviewhelpers.cpp b/kate/part/kateviewhelpers.cpp
new file mode 100644
index 000000000..4ca753486
--- /dev/null
+++ b/kate/part/kateviewhelpers.cpp
@@ -0,0 +1,1205 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org>
+ Copyright (C) 2001 Anders Lund <anders@alweb.dk>
+ Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kateviewhelpers.h"
+#include "kateviewhelpers.moc"
+
+#include "../interfaces/document.h"
+#include "../interfaces/katecmd.h"
+#include "kateattribute.h"
+#include "katecodefoldinghelpers.h"
+#include "kateconfig.h"
+#include "katedocument.h"
+#include "katefactory.h"
+#include "katerenderer.h"
+#include "kateview.h"
+#include "kateviewinternal.h"
+
+#include <kapplication.h>
+#include <kglobalsettings.h>
+#include <klocale.h>
+#include <knotifyclient.h>
+#include <kglobal.h>
+#include <kcharsets.h>
+#include <kpopupmenu.h>
+
+#include <qcursor.h>
+#include <qpainter.h>
+#include <qpopupmenu.h>
+#include <qstyle.h>
+#include <qtimer.h>
+#include <qwhatsthis.h>
+#include <qregexp.h>
+#include <qtextcodec.h>
+
+#include <math.h>
+
+#include <kdebug.h>
+
+//BEGIN KateScrollBar
+KateScrollBar::KateScrollBar (Orientation orientation, KateViewInternal* parent, const char* name)
+ : QScrollBar (orientation, parent->m_view, name)
+ , m_middleMouseDown (false)
+ , m_view(parent->m_view)
+ , m_doc(parent->m_doc)
+ , m_viewInternal(parent)
+ , m_topMargin(-1)
+ , m_bottomMargin(-1)
+ , m_savVisibleLines(0)
+ , m_showMarks(false)
+{
+ connect(this, SIGNAL(valueChanged(int)), SLOT(sliderMaybeMoved(int)));
+ connect(m_doc, SIGNAL(marksChanged()), this, SLOT(marksChanged()));
+
+ m_lines.setAutoDelete(true);
+}
+
+void KateScrollBar::mousePressEvent(QMouseEvent* e)
+{
+ if (e->button() == MidButton)
+ m_middleMouseDown = true;
+
+ QScrollBar::mousePressEvent(e);
+
+ redrawMarks();
+}
+
+void KateScrollBar::mouseReleaseEvent(QMouseEvent* e)
+{
+ QScrollBar::mouseReleaseEvent(e);
+
+ m_middleMouseDown = false;
+
+ redrawMarks();
+}
+
+void KateScrollBar::mouseMoveEvent(QMouseEvent* e)
+{
+ QScrollBar::mouseMoveEvent(e);
+
+ if (e->state() | LeftButton)
+ redrawMarks();
+}
+
+void KateScrollBar::paintEvent(QPaintEvent *e)
+{
+ QScrollBar::paintEvent(e);
+ redrawMarks();
+}
+
+void KateScrollBar::resizeEvent(QResizeEvent *e)
+{
+ QScrollBar::resizeEvent(e);
+ recomputeMarksPositions();
+}
+
+void KateScrollBar::styleChange(QStyle &s)
+{
+ QScrollBar::styleChange(s);
+ m_topMargin = -1;
+ recomputeMarksPositions();
+}
+
+void KateScrollBar::valueChange()
+{
+ QScrollBar::valueChange();
+ redrawMarks();
+}
+
+void KateScrollBar::rangeChange()
+{
+ QScrollBar::rangeChange();
+ recomputeMarksPositions();
+}
+
+void KateScrollBar::marksChanged()
+{
+ recomputeMarksPositions(true);
+}
+
+void KateScrollBar::redrawMarks()
+{
+ if (!m_showMarks)
+ return;
+
+ QPainter painter(this);
+ QRect rect = sliderRect();
+ for (QIntDictIterator<QColor> it(m_lines); it.current(); ++it)
+ {
+ if (it.currentKey() < rect.top() || it.currentKey() > rect.bottom())
+ {
+ painter.setPen(*it.current());
+ painter.drawLine(0, it.currentKey(), width(), it.currentKey());
+ }
+ }
+}
+
+void KateScrollBar::recomputeMarksPositions(bool forceFullUpdate)
+{
+ if (m_topMargin == -1)
+ watchScrollBarSize();
+
+ m_lines.clear();
+ m_savVisibleLines = m_doc->visibleLines();
+
+ int realHeight = frameGeometry().height() - m_topMargin - m_bottomMargin;
+
+ QPtrList<KTextEditor::Mark> marks = m_doc->marks();
+ KateCodeFoldingTree *tree = m_doc->foldingTree();
+
+ for (KTextEditor::Mark *mark = marks.first(); mark; mark = marks.next())
+ {
+ uint line = mark->line;
+
+ if (tree)
+ {
+ KateCodeFoldingNode *node = tree->findNodeForLine(line);
+
+ while (node)
+ {
+ if (!node->isVisible())
+ line = tree->getStartLine(node);
+ node = node->getParentNode();
+ }
+ }
+
+ line = m_doc->getVirtualLine(line);
+
+ double d = (double)line / (m_savVisibleLines - 1);
+ m_lines.insert(m_topMargin + (int)(d * realHeight),
+ new QColor(KateRendererConfig::global()->lineMarkerColor((KTextEditor::MarkInterface::MarkTypes)mark->type)));
+ }
+
+ if (forceFullUpdate)
+ update();
+ else
+ redrawMarks();
+}
+
+void KateScrollBar::watchScrollBarSize()
+{
+ int savMax = maxValue();
+ setMaxValue(0);
+ QRect rect = sliderRect();
+ setMaxValue(savMax);
+
+ m_topMargin = rect.top();
+ m_bottomMargin = frameGeometry().height() - rect.bottom();
+}
+
+void KateScrollBar::sliderMaybeMoved(int value)
+{
+ if (m_middleMouseDown)
+ emit sliderMMBMoved(value);
+}
+//END
+
+//BEGIN KateCmdLnWhatsThis
+class KateCmdLnWhatsThis : public QWhatsThis
+{
+ public:
+ KateCmdLnWhatsThis( KateCmdLine *parent )
+ : QWhatsThis( parent )
+ , m_parent( parent ) {;}
+
+ QString text( const QPoint & )
+ {
+ QString beg = "<qt background=\"white\"><div><table width=\"100%\"><tr><td bgcolor=\"brown\"><font color=\"white\"><b>Help: <big>";
+ QString mid = "</big></b></font></td></tr><tr><td>";
+ QString end = "</td></tr></table></div><qt>";
+
+ QString t = m_parent->text();
+ QRegExp re( "\\s*help\\s+(.*)" );
+ if ( re.search( t ) > -1 )
+ {
+ QString s;
+ // get help for command
+ QString name = re.cap( 1 );
+ if ( name == "list" )
+ {
+ return beg + i18n("Available Commands") + mid
+ + KateCmd::self()->cmds().join(" ")
+ + i18n("<p>For help on individual commands, do <code>'help &lt;command&gt;'</code></p>")
+ + end;
+ }
+ else if ( ! name.isEmpty() )
+ {
+ Kate::Command *cmd = KateCmd::self()->queryCommand( name );
+ if ( cmd )
+ {
+ if ( cmd->help( (Kate::View*)m_parent->parentWidget(), name, s ) )
+ return beg + name + mid + s + end;
+ else
+ return beg + name + mid + i18n("No help for '%1'").arg( name ) + end;
+ }
+ else
+ return beg + mid + i18n("No such command <b>%1</b>").arg(name) + end;
+ }
+ }
+
+ return beg + mid + i18n(
+ "<p>This is the Katepart <b>command line</b>.<br>"
+ "Syntax: <code><b>command [ arguments ]</b></code><br>"
+ "For a list of available commands, enter <code><b>help list</b></code><br>"
+ "For help for individual commands, enter <code><b>help &lt;command&gt;</b></code></p>")
+ + end;
+ }
+
+ private:
+ KateCmdLine *m_parent;
+};
+//END KateCmdLnWhatsThis
+
+//BEGIN KateCmdLineFlagCompletion
+/**
+ * This class provide completion of flags. It shows a short description of
+ * each flag, and flags are appended.
+ */
+class KateCmdLineFlagCompletion : public KCompletion
+{
+ public:
+ KateCmdLineFlagCompletion() {;}
+
+ QString makeCompletion( const QString & string )
+ {
+ return QString::null;
+ }
+
+};
+//END KateCmdLineFlagCompletion
+
+//BEGIN KateCmdLine
+KateCmdLine::KateCmdLine (KateView *view)
+ : KLineEdit (view)
+ , m_view (view)
+ , m_msgMode (false)
+ , m_histpos( 0 )
+ , m_cmdend( 0 )
+ , m_command( 0L )
+ , m_oldCompletionObject( 0L )
+{
+ connect (this, SIGNAL(returnPressed(const QString &)),
+ this, SLOT(slotReturnPressed(const QString &)));
+
+ completionObject()->insertItems (KateCmd::self()->cmds());
+ setAutoDeleteCompletionObject( false );
+ m_help = new KateCmdLnWhatsThis( this );
+}
+
+void KateCmdLine::slotReturnPressed ( const QString& text )
+{
+
+ // silently ignore leading space
+ uint n = 0;
+ while( text[n].isSpace() )
+ n++;
+
+ QString cmd = text.mid( n );
+
+ // Built in help: if the command starts with "help", [try to] show some help
+ if ( cmd.startsWith( "help" ) )
+ {
+ m_help->display( m_help->text( QPoint() ), mapToGlobal(QPoint(0,0)) );
+ clear();
+ KateCmd::self()->appendHistory( cmd );
+ m_histpos = KateCmd::self()->historyLength();
+ m_oldText = QString ();
+ return;
+ }
+
+ if (cmd.length () > 0)
+ {
+ Kate::Command *p = KateCmd::self()->queryCommand (cmd);
+
+ m_oldText = cmd;
+ m_msgMode = true;
+
+ if (p)
+ {
+ QString msg;
+
+ if (p->exec (m_view, cmd, msg))
+ {
+ KateCmd::self()->appendHistory( cmd );
+ m_histpos = KateCmd::self()->historyLength();
+ m_oldText = QString ();
+
+ if (msg.length() > 0)
+ setText (i18n ("Success: ") + msg);
+ else
+ setText (i18n ("Success"));
+ }
+ else
+ {
+ if (msg.length() > 0)
+ setText (i18n ("Error: ") + msg);
+ else
+ setText (i18n ("Command \"%1\" failed.").arg (cmd));
+ KNotifyClient::beep();
+ }
+ }
+ else
+ {
+ setText (i18n ("No such command: \"%1\"").arg (cmd));
+ KNotifyClient::beep();
+ }
+ }
+
+ // clean up
+ if ( m_oldCompletionObject )
+ {
+ KCompletion *c = completionObject();
+ setCompletionObject( m_oldCompletionObject );
+ m_oldCompletionObject = 0;
+ delete c;
+ c = 0;
+ }
+ m_command = 0;
+ m_cmdend = 0;
+
+ m_view->setFocus ();
+ QTimer::singleShot( 4000, this, SLOT(hideMe()) );
+}
+
+void KateCmdLine::hideMe () // unless i have focus ;)
+{
+ if ( isVisibleTo(parentWidget()) && ! hasFocus() ) {
+ m_view->toggleCmdLine ();
+ }
+}
+
+void KateCmdLine::focusInEvent ( QFocusEvent *ev )
+{
+ if (m_msgMode)
+ {
+ m_msgMode = false;
+ setText (m_oldText);
+ selectAll();
+ }
+
+ KLineEdit::focusInEvent (ev);
+}
+
+void KateCmdLine::keyPressEvent( QKeyEvent *ev )
+{
+ if (ev->key() == Key_Escape)
+ {
+ m_view->setFocus ();
+ hideMe();
+ }
+ else if ( ev->key() == Key_Up )
+ fromHistory( true );
+ else if ( ev->key() == Key_Down )
+ fromHistory( false );
+
+ uint cursorpos = cursorPosition();
+ KLineEdit::keyPressEvent (ev);
+
+ // during typing, let us see if we have a valid command
+ if ( ! m_cmdend || cursorpos <= m_cmdend )
+ {
+ QChar c;
+ if ( ! ev->text().isEmpty() )
+ c = ev->text()[0];
+
+ if ( ! m_cmdend && ! c.isNull() ) // we have no command, so lets see if we got one
+ {
+ if ( ! c.isLetterOrNumber() && c != '-' && c != '_' )
+ {
+ m_command = KateCmd::self()->queryCommand( text().stripWhiteSpace() );
+ if ( m_command )
+ {
+ //kdDebug(13025)<<"keypress in commandline: We have a command! "<<m_command<<". text is '"<<text()<<"'"<<endl;
+ // if the typed character is ":",
+ // we try if the command has flag completions
+ m_cmdend = cursorpos;
+ //kdDebug(13025)<<"keypress in commandline: Set m_cmdend to "<<m_cmdend<<endl;
+ }
+ else
+ m_cmdend = 0;
+ }
+ }
+ else // since cursor is inside the command name, we reconsider it
+ {
+ kdDebug(13025)<<"keypress in commandline: \\W -- text is "<<text()<<endl;
+ m_command = KateCmd::self()->queryCommand( text().stripWhiteSpace() );
+ if ( m_command )
+ {
+ //kdDebug(13025)<<"keypress in commandline: We have a command! "<<m_command<<endl;
+ QString t = text();
+ m_cmdend = 0;
+ bool b = false;
+ for ( ; m_cmdend < t.length(); m_cmdend++ )
+ {
+ if ( t[m_cmdend].isLetter() )
+ b = true;
+ if ( b && ( ! t[m_cmdend].isLetterOrNumber() && t[m_cmdend] != '-' && t[m_cmdend] != '_' ) )
+ break;
+ }
+
+ if ( c == ':' && cursorpos == m_cmdend )
+ {
+ // check if this command wants to complete flags
+ //kdDebug(13025)<<"keypress in commandline: Checking if flag completion is desired!"<<endl;
+ }
+ }
+ else
+ {
+ // clean up if needed
+ if ( m_oldCompletionObject )
+ {
+ KCompletion *c = completionObject();
+ setCompletionObject( m_oldCompletionObject );
+ m_oldCompletionObject = 0;
+ delete c;
+ c = 0;
+ }
+
+ m_cmdend = 0;
+ }
+ }
+
+ // if we got a command, check if it wants to do semething.
+ if ( m_command )
+ {
+ //kdDebug(13025)<<"Checking for CommandExtension.."<<endl;
+ Kate::CommandExtension *ce = dynamic_cast<Kate::CommandExtension*>(m_command);
+ if ( ce )
+ {
+ KCompletion *cmpl = ce->completionObject( text().left( m_cmdend ).stripWhiteSpace(), m_view );
+ if ( cmpl )
+ {
+ // save the old completion object and use what the command provides
+ // instead. We also need to prepend the current command name + flag string
+ // when completion is done
+ //kdDebug(13025)<<"keypress in commandline: Setting completion object!"<<endl;
+ if ( ! m_oldCompletionObject )
+ m_oldCompletionObject = completionObject();
+
+ setCompletionObject( cmpl );
+ }
+ }
+ }
+ }
+ else if ( m_command )// check if we should call the commands processText()
+ {
+ Kate::CommandExtension *ce = dynamic_cast<Kate::CommandExtension*>( m_command );
+ if ( ce && ce->wantsToProcessText( text().left( m_cmdend ).stripWhiteSpace() )
+ && ! ( ev->text().isNull() || ev->text().isEmpty() ) )
+ ce->processText( m_view, text() );
+ }
+}
+
+void KateCmdLine::fromHistory( bool up )
+{
+ if ( ! KateCmd::self()->historyLength() )
+ return;
+
+ QString s;
+
+ if ( up )
+ {
+ if ( m_histpos > 0 )
+ {
+ m_histpos--;
+ s = KateCmd::self()->fromHistory( m_histpos );
+ }
+ }
+ else
+ {
+ if ( m_histpos < ( KateCmd::self()->historyLength() - 1 ) )
+ {
+ m_histpos++;
+ s = KateCmd::self()->fromHistory( m_histpos );
+ }
+ else
+ {
+ m_histpos = KateCmd::self()->historyLength();
+ setText( m_oldText );
+ }
+ }
+ if ( ! s.isEmpty() )
+ {
+ // Select the argument part of the command, so that it is easy to overwrite
+ setText( s );
+ static QRegExp reCmd = QRegExp(".*[\\w\\-]+(?:[^a-zA-Z0-9_-]|:\\w+)(.*)");
+ if ( reCmd.search( text() ) == 0 )
+ setSelection( text().length() - reCmd.cap(1).length(), reCmd.cap(1).length() );
+ }
+}
+//END KateCmdLine
+
+//BEGIN KateIconBorder
+using namespace KTextEditor;
+
+static const char* const plus_xpm[] = {
+"11 11 3 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+"...........",
+".+++++++++.",
+".+++++++++.",
+".++++.++++.",
+".++++.++++.",
+".++.....++.",
+".++++.++++.",
+".++++.++++.",
+".+++++++++.",
+".+++++++++.",
+"..........."};
+
+static const char* const minus_xpm[] = {
+"11 11 3 1",
+" c None",
+". c #000000",
+"+ c #FFFFFF",
+"...........",
+".+++++++++.",
+".+++++++++.",
+".+++++++++.",
+".+++++++++.",
+".++.....++.",
+".+++++++++.",
+".+++++++++.",
+".+++++++++.",
+".+++++++++.",
+"..........."};
+
+static const char * const bookmark_xpm[] = {
+"14 13 82 1",
+" c None",
+". c #F27D01",
+"+ c #EF7901",
+"@ c #F3940F",
+"# c #EE8F12",
+"$ c #F9C834",
+"% c #F5C33A",
+"& c #F09110",
+"* c #FCEE3E",
+"= c #FBEB3F",
+"- c #E68614",
+"; c #FA8700",
+"> c #F78703",
+", c #F4920E",
+"' c #F19113",
+") c #F6C434",
+"! c #FDF938",
+"~ c #FDF839",
+"{ c #F1BC3A",
+"] c #E18017",
+"^ c #DA7210",
+"/ c #D5680B",
+"( c #CA5404",
+"_ c #FD8F06",
+": c #FCB62D",
+"< c #FDE049",
+"[ c #FCE340",
+"} c #FBE334",
+"| c #FDF035",
+"1 c #FEF834",
+"2 c #FCEF36",
+"3 c #F8DF32",
+"4 c #F7DC3D",
+"5 c #F5CE3E",
+"6 c #DE861B",
+"7 c #C64C03",
+"8 c #F78C07",
+"9 c #F8B019",
+"0 c #FDE12D",
+"a c #FEE528",
+"b c #FEE229",
+"c c #FBD029",
+"d c #E18814",
+"e c #CB5605",
+"f c #EF8306",
+"g c #F3A00E",
+"h c #FBC718",
+"i c #FED31C",
+"j c #FED11D",
+"k c #F8B91C",
+"l c #E07D0D",
+"m c #CB5301",
+"n c #ED8A0E",
+"o c #F7A90D",
+"p c #FEC113",
+"q c #FEC013",
+"r c #F09B0E",
+"s c #D35E03",
+"t c #EF9213",
+"u c #F9A208",
+"v c #FEAA0C",
+"w c #FCA10B",
+"x c #FCA70B",
+"y c #FEAF0B",
+"z c #F39609",
+"A c #D86203",
+"B c #F08C0D",
+"C c #FA9004",
+"D c #F17F04",
+"E c #E36D04",
+"F c #E16F03",
+"G c #EE8304",
+"H c #F88C04",
+"I c #DC6202",
+"J c #E87204",
+"K c #E66A01",
+"L c #DC6001",
+"M c #D15601",
+"N c #DA5D01",
+"O c #D25200",
+"P c #DA5F00",
+"Q c #BC3C00",
+" .+ ",
+" @# ",
+" $% ",
+" &*=- ",
+" ;>,')!~{]^/( ",
+"_:<[}|11234567",
+" 890aaaaabcde ",
+" fghiiijklm ",
+" nopqpqrs ",
+" tuvwxyzA ",
+" BCDEFGHI ",
+" JKL MNO ",
+" P Q "};
+
+const int iconPaneWidth = 16;
+const int halfIPW = 8;
+
+KateIconBorder::KateIconBorder ( KateViewInternal* internalView, QWidget *parent )
+ : QWidget(parent, "", Qt::WStaticContents | Qt::WRepaintNoErase | Qt::WResizeNoErase )
+ , m_view( internalView->m_view )
+ , m_doc( internalView->m_doc )
+ , m_viewInternal( internalView )
+ , m_iconBorderOn( false )
+ , m_lineNumbersOn( false )
+ , m_foldingMarkersOn( false )
+ , m_dynWrapIndicatorsOn( false )
+ , m_dynWrapIndicators( 0 )
+ , m_cachedLNWidth( 0 )
+ , m_maxCharWidth( 0 )
+{
+ setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Minimum ) );
+
+ setBackgroundMode( NoBackground );
+
+ m_doc->setDescription( MarkInterface::markType01, i18n("Bookmark") );
+ m_doc->setPixmap( MarkInterface::markType01, QPixmap((const char**)bookmark_xpm) );
+
+ updateFont();
+}
+
+void KateIconBorder::setIconBorderOn( bool enable )
+{
+ if( enable == m_iconBorderOn )
+ return;
+
+ m_iconBorderOn = enable;
+
+ updateGeometry();
+
+ QTimer::singleShot( 0, this, SLOT(update()) );
+}
+
+void KateIconBorder::setLineNumbersOn( bool enable )
+{
+ if( enable == m_lineNumbersOn )
+ return;
+
+ m_lineNumbersOn = enable;
+ m_dynWrapIndicatorsOn = (m_dynWrapIndicators == 1) ? enable : m_dynWrapIndicators;
+
+ updateGeometry();
+
+ QTimer::singleShot( 0, this, SLOT(update()) );
+}
+
+void KateIconBorder::setDynWrapIndicators( int state )
+{
+ if (state == m_dynWrapIndicators )
+ return;
+
+ m_dynWrapIndicators = state;
+ m_dynWrapIndicatorsOn = (state == 1) ? m_lineNumbersOn : state;
+
+ updateGeometry ();
+
+ QTimer::singleShot( 0, this, SLOT(update()) );
+}
+
+void KateIconBorder::setFoldingMarkersOn( bool enable )
+{
+ if( enable == m_foldingMarkersOn )
+ return;
+
+ m_foldingMarkersOn = enable;
+
+ updateGeometry();
+
+ QTimer::singleShot( 0, this, SLOT(update()) );
+}
+
+QSize KateIconBorder::sizeHint() const
+{
+ int w = 0;
+
+ if (m_iconBorderOn)
+ w += iconPaneWidth + 1;
+
+ if (m_lineNumbersOn || (m_view->dynWordWrap() && m_dynWrapIndicatorsOn)) {
+ w += lineNumberWidth();
+ }
+
+ if (m_foldingMarkersOn)
+ w += iconPaneWidth;
+
+ w += 4;
+
+ return QSize( w, 0 );
+}
+
+// This function (re)calculates the maximum width of any of the digit characters (0 -> 9)
+// for graceful handling of variable-width fonts as the linenumber font.
+void KateIconBorder::updateFont()
+{
+ const QFontMetrics *fm = m_view->renderer()->config()->fontMetrics();
+ m_maxCharWidth = 0;
+ // Loop to determine the widest numeric character in the current font.
+ // 48 is ascii '0'
+ for (int i = 48; i < 58; i++) {
+ int charWidth = fm->width( QChar(i) );
+ m_maxCharWidth = kMax(m_maxCharWidth, charWidth);
+ }
+}
+
+int KateIconBorder::lineNumberWidth() const
+{
+ int width = m_lineNumbersOn ? ((int)log10((double)(m_view->doc()->numLines())) + 1) * m_maxCharWidth + 4 : 0;
+
+ if (m_view->dynWordWrap() && m_dynWrapIndicatorsOn) {
+ width = kMax(style().scrollBarExtent().width() + 4, width);
+
+ if (m_cachedLNWidth != width || m_oldBackgroundColor != m_view->renderer()->config()->iconBarColor()) {
+ int w = style().scrollBarExtent().width();
+ int h = m_view->renderer()->config()->fontMetrics()->height();
+
+ QSize newSize(w, h);
+ if ((m_arrow.size() != newSize || m_oldBackgroundColor != m_view->renderer()->config()->iconBarColor()) && !newSize.isEmpty()) {
+ m_arrow.resize(newSize);
+
+ QPainter p(&m_arrow);
+ p.fillRect( 0, 0, w, h, m_view->renderer()->config()->iconBarColor() );
+
+ h = m_view->renderer()->config()->fontMetrics()->ascent();
+
+ p.setPen(m_view->renderer()->attribute(0)->textColor());
+ p.drawLine(w/2, h/2, w/2, 0);
+#if 1
+ p.lineTo(w/4, h/4);
+ p.lineTo(0, 0);
+ p.lineTo(0, h/2);
+ p.lineTo(w/2, h-1);
+ p.lineTo(w*3/4, h-1);
+ p.lineTo(w-1, h*3/4);
+ p.lineTo(w*3/4, h/2);
+ p.lineTo(0, h/2);
+#else
+ p.lineTo(w*3/4, h/4);
+ p.lineTo(w-1,0);
+ p.lineTo(w-1, h/2);
+ p.lineTo(w/2, h-1);
+ p.lineTo(w/4,h-1);
+ p.lineTo(0, h*3/4);
+ p.lineTo(w/4, h/2);
+ p.lineTo(w-1, h/2);
+#endif
+ }
+ }
+ }
+
+ return width;
+}
+
+void KateIconBorder::paintEvent(QPaintEvent* e)
+{
+ paintBorder(e->rect().x(), e->rect().y(), e->rect().width(), e->rect().height());
+}
+
+void KateIconBorder::paintBorder (int /*x*/, int y, int /*width*/, int height)
+{
+ static QPixmap minus_px ((const char**)minus_xpm);
+ static QPixmap plus_px ((const char**)plus_xpm);
+
+ uint h = m_view->renderer()->config()->fontStruct()->fontHeight;
+ uint startz = (y / h);
+ uint endz = startz + 1 + (height / h);
+ uint lineRangesSize = m_viewInternal->lineRanges.size();
+
+ // center the folding boxes
+ int m_px = (h - 11) / 2;
+ if (m_px < 0)
+ m_px = 0;
+
+ int lnWidth( 0 );
+ if ( m_lineNumbersOn || (m_view->dynWordWrap() && m_dynWrapIndicatorsOn) ) // avoid calculating unless needed ;-)
+ {
+ lnWidth = lineNumberWidth();
+ if ( lnWidth != m_cachedLNWidth || m_oldBackgroundColor != m_view->renderer()->config()->iconBarColor() )
+ {
+ // we went from n0 ->n9 lines or vice verca
+ // this causes an extra updateGeometry() first time the line numbers
+ // are displayed, but sizeHint() is supposed to be const so we can't set
+ // the cached value there.
+ m_cachedLNWidth = lnWidth;
+ m_oldBackgroundColor = m_view->renderer()->config()->iconBarColor();
+ updateGeometry();
+ update ();
+ return;
+ }
+ }
+
+ int w( this->width() ); // sane value/calc only once
+
+ QPainter p ( this );
+ p.setFont ( *m_view->renderer()->config()->font() ); // for line numbers
+ // the line number color is for the line numbers, vertical separator lines
+ // and for for the code folding lines.
+ p.setPen ( m_view->renderer()->config()->lineNumberColor() );
+
+ KateLineInfo oldInfo;
+ if (startz < lineRangesSize)
+ {
+ if ((m_viewInternal->lineRanges[startz].line-1) < 0)
+ oldInfo.topLevel = true;
+ else
+ m_doc->lineInfo(&oldInfo,m_viewInternal->lineRanges[startz].line-1);
+ }
+
+ for (uint z=startz; z <= endz; z++)
+ {
+ int y = h * z;
+ int realLine = -1;
+
+ if (z < lineRangesSize)
+ realLine = m_viewInternal->lineRanges[z].line;
+
+ int lnX ( 0 );
+
+ p.fillRect( 0, y, w-4, h, m_view->renderer()->config()->iconBarColor() );
+ p.fillRect( w-4, y, 4, h, m_view->renderer()->config()->backgroundColor() );
+
+ // icon pane
+ if( m_iconBorderOn )
+ {
+ p.drawLine(lnX+iconPaneWidth, y, lnX+iconPaneWidth, y+h);
+
+ if( (realLine > -1) && (m_viewInternal->lineRanges[z].startCol == 0) )
+ {
+ uint mrk ( m_doc->mark( realLine ) ); // call only once
+
+ if ( mrk )
+ {
+ for( uint bit = 0; bit < 32; bit++ )
+ {
+ MarkInterface::MarkTypes markType = (MarkInterface::MarkTypes)(1<<bit);
+ if( mrk & markType )
+ {
+ QPixmap *px_mark (m_doc->markPixmap( markType ));
+
+ if (px_mark)
+ {
+ // center the mark pixmap
+ int x_px = (iconPaneWidth - px_mark->width()) / 2;
+ if (x_px < 0)
+ x_px = 0;
+
+ int y_px = (h - px_mark->height()) / 2;
+ if (y_px < 0)
+ y_px = 0;
+
+ p.drawPixmap( lnX+x_px, y+y_px, *px_mark);
+ }
+ }
+ }
+ }
+ }
+
+ lnX += iconPaneWidth + 1;
+ }
+
+ // line number
+ if( m_lineNumbersOn || (m_view->dynWordWrap() && m_dynWrapIndicatorsOn) )
+ {
+ lnX +=2;
+
+ if (realLine > -1)
+ if (m_viewInternal->lineRanges[z].startCol == 0) {
+ if (m_lineNumbersOn)
+ p.drawText( lnX + 1, y, lnWidth-4, h, Qt::AlignRight|Qt::AlignVCenter, QString("%1").arg( realLine + 1 ) );
+ } else if (m_view->dynWordWrap() && m_dynWrapIndicatorsOn) {
+ p.drawPixmap(lnX + lnWidth - m_arrow.width() - 4, y, m_arrow);
+ }
+
+ lnX += lnWidth;
+ }
+
+ // folding markers
+ if( m_foldingMarkersOn )
+ {
+ if( realLine > -1 )
+ {
+ KateLineInfo info;
+ m_doc->lineInfo(&info,realLine);
+
+ if (!info.topLevel)
+ {
+ if (info.startsVisibleBlock && (m_viewInternal->lineRanges[z].startCol == 0))
+ {
+ if (oldInfo.topLevel)
+ p.drawLine(lnX+halfIPW,y+m_px,lnX+halfIPW,y+h-1);
+ else
+ p.drawLine(lnX+halfIPW,y,lnX+halfIPW,y+h-1);
+
+ p.drawPixmap(lnX+3,y+m_px,minus_px);
+ }
+ else if (info.startsInVisibleBlock)
+ {
+ if (m_viewInternal->lineRanges[z].startCol == 0)
+ {
+ if (oldInfo.topLevel)
+ p.drawLine(lnX+halfIPW,y+m_px,lnX+halfIPW,y+h-1);
+ else
+ p.drawLine(lnX+halfIPW,y,lnX+halfIPW,y+h-1);
+
+ p.drawPixmap(lnX+3,y+m_px,plus_px);
+ }
+ else
+ {
+ p.drawLine(lnX+halfIPW,y,lnX+halfIPW,y+h-1);
+ }
+
+ if (!m_viewInternal->lineRanges[z].wrap)
+ p.drawLine(lnX+halfIPW,y+h-1,lnX+iconPaneWidth-2,y+h-1);
+ }
+ else
+ {
+ p.drawLine(lnX+halfIPW,y,lnX+halfIPW,y+h-1);
+
+ if (info.endsBlock && !m_viewInternal->lineRanges[z].wrap)
+ p.drawLine(lnX+halfIPW,y+h-1,lnX+iconPaneWidth-2,y+h-1);
+ }
+ }
+
+ oldInfo = info;
+ }
+
+ lnX += iconPaneWidth;
+ }
+ }
+}
+
+KateIconBorder::BorderArea KateIconBorder::positionToArea( const QPoint& p ) const
+{
+ int x = 0;
+ if( m_iconBorderOn ) {
+ x += iconPaneWidth;
+ if( p.x() <= x )
+ return IconBorder;
+ }
+ if( m_lineNumbersOn || m_dynWrapIndicators ) {
+ x += lineNumberWidth();
+ if( p.x() <= x )
+ return LineNumbers;
+ }
+ if( m_foldingMarkersOn ) {
+ x += iconPaneWidth;
+ if( p.x() <= x )
+ return FoldingMarkers;
+ }
+ return None;
+}
+
+void KateIconBorder::mousePressEvent( QMouseEvent* e )
+{
+ m_lastClickedLine = m_viewInternal->yToKateLineRange(e->y()).line;
+
+ if ( positionToArea( e->pos() ) != IconBorder )
+ {
+ QMouseEvent forward( QEvent::MouseButtonPress,
+ QPoint( 0, e->y() ), e->button(), e->state() );
+ m_viewInternal->mousePressEvent( &forward );
+ }
+ e->accept();
+}
+
+void KateIconBorder::mouseMoveEvent( QMouseEvent* e )
+{
+ if ( positionToArea( e->pos() ) != IconBorder )
+ {
+ QMouseEvent forward( QEvent::MouseMove,
+ QPoint( 0, e->y() ), e->button(), e->state() );
+ m_viewInternal->mouseMoveEvent( &forward );
+ }
+}
+
+void KateIconBorder::mouseReleaseEvent( QMouseEvent* e )
+{
+ uint cursorOnLine = m_viewInternal->yToKateLineRange(e->y()).line;
+
+ if (cursorOnLine == m_lastClickedLine &&
+ cursorOnLine <= m_doc->lastLine() )
+ {
+ BorderArea area = positionToArea( e->pos() );
+ if( area == IconBorder) {
+ if (e->button() == LeftButton) {
+ if( m_doc->editableMarks() & KateViewConfig::global()->defaultMarkType() ) {
+ if( m_doc->mark( cursorOnLine ) & KateViewConfig::global()->defaultMarkType() )
+ m_doc->removeMark( cursorOnLine, KateViewConfig::global()->defaultMarkType() );
+ else
+ m_doc->addMark( cursorOnLine, KateViewConfig::global()->defaultMarkType() );
+ } else {
+ showMarkMenu( cursorOnLine, QCursor::pos() );
+ }
+ }
+ else
+ if (e->button() == RightButton) {
+ showMarkMenu( cursorOnLine, QCursor::pos() );
+ }
+ }
+
+ if ( area == FoldingMarkers) {
+ KateLineInfo info;
+ m_doc->lineInfo(&info,cursorOnLine);
+ if ((info.startsVisibleBlock) || (info.startsInVisibleBlock)) {
+ emit toggleRegionVisibility(cursorOnLine);
+ }
+ }
+ }
+
+ QMouseEvent forward( QEvent::MouseButtonRelease,
+ QPoint( 0, e->y() ), e->button(), e->state() );
+ m_viewInternal->mouseReleaseEvent( &forward );
+}
+
+void KateIconBorder::mouseDoubleClickEvent( QMouseEvent* e )
+{
+ QMouseEvent forward( QEvent::MouseButtonDblClick,
+ QPoint( 0, e->y() ), e->button(), e->state() );
+ m_viewInternal->mouseDoubleClickEvent( &forward );
+}
+
+void KateIconBorder::showMarkMenu( uint line, const QPoint& pos )
+{
+ QPopupMenu markMenu;
+ QPopupMenu selectDefaultMark;
+
+ typedef QValueVector<int> MarkTypeVector;
+ MarkTypeVector vec( 33 );
+ int i=1;
+
+ for( uint bit = 0; bit < 32; bit++ ) {
+ MarkInterface::MarkTypes markType = (MarkInterface::MarkTypes)(1<<bit);
+ if( !(m_doc->editableMarks() & markType) )
+ continue;
+
+ if( !m_doc->markDescription( markType ).isEmpty() ) {
+ markMenu.insertItem( m_doc->markDescription( markType ), i );
+ selectDefaultMark.insertItem( m_doc->markDescription( markType ), i+100);
+ } else {
+ markMenu.insertItem( i18n("Mark Type %1").arg( bit + 1 ), i );
+ selectDefaultMark.insertItem( i18n("Mark Type %1").arg( bit + 1 ), i+100);
+ }
+
+ if( m_doc->mark( line ) & markType )
+ markMenu.setItemChecked( i, true );
+
+ if( markType & KateViewConfig::global()->defaultMarkType() )
+ selectDefaultMark.setItemChecked( i+100, true );
+
+ vec[i++] = markType;
+ }
+
+ if( markMenu.count() == 0 )
+ return;
+
+ if( markMenu.count() > 1 )
+ markMenu.insertItem( i18n("Set Default Mark Type" ), &selectDefaultMark);
+
+ int result = markMenu.exec( pos );
+ if( result <= 0 )
+ return;
+
+ if ( result > 100)
+ {
+ KateViewConfig::global()->setDefaultMarkType (vec[result-100]);
+ // flush config, otherwise it isn't nessecarily done
+ KConfig *config = kapp->config();
+ config->setGroup("Kate View Defaults");
+ KateViewConfig::global()->writeConfig( config );
+ }
+ else
+ {
+ MarkInterface::MarkTypes markType = (MarkInterface::MarkTypes) vec[result];
+ if( m_doc->mark( line ) & markType ) {
+ m_doc->removeMark( line, markType );
+ } else {
+ m_doc->addMark( line, markType );
+ }
+ }
+}
+//END KateIconBorder
+
+KateViewEncodingAction::KateViewEncodingAction(KateDocument *_doc, KateView *_view, const QString& text, QObject* parent, const char* name)
+ : KActionMenu (text, parent, name), doc(_doc), view (_view)
+{
+ connect(popupMenu(),SIGNAL(aboutToShow()),this,SLOT(slotAboutToShow()));
+}
+
+void KateViewEncodingAction::slotAboutToShow()
+{
+ QStringList modes (KGlobal::charsets()->descriptiveEncodingNames());
+
+ popupMenu()->clear ();
+ for (uint z=0; z<modes.size(); ++z)
+ {
+ popupMenu()->insertItem ( modes[z], this, SLOT(setMode(int)), 0, z);
+
+ bool found = false;
+ QTextCodec *codecForEnc = KGlobal::charsets()->codecForName(KGlobal::charsets()->encodingForName(modes[z]), found);
+
+ if (found && codecForEnc)
+ {
+ if (codecForEnc->name() == doc->config()->codec()->name())
+ popupMenu()->setItemChecked (z, true);
+ }
+ }
+}
+
+void KateViewEncodingAction::setMode (int mode)
+{
+ QStringList modes (KGlobal::charsets()->descriptiveEncodingNames());
+ doc->config()->setEncoding( KGlobal::charsets()->encodingForName( modes[mode] ) );
+ // now we don't want the encoding changed again unless the user does so using the menu.
+ doc->setEncodingSticky( true );
+ doc->reloadFile();
+}
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/kateviewhelpers.h b/kate/part/kateviewhelpers.h
new file mode 100644
index 000000000..4687365f6
--- /dev/null
+++ b/kate/part/kateviewhelpers.h
@@ -0,0 +1,207 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org>
+ Copyright (C) 2001 Anders Lund <anders@alweb.dk>
+ Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KATE_VIEW_HELPERS_H__
+#define __KATE_VIEW_HELPERS_H__
+
+#include <kaction.h>
+#include <klineedit.h>
+
+#include <qwidget.h>
+#include <qpixmap.h>
+#include <qcolor.h>
+#include <qscrollbar.h>
+#include <qintdict.h>
+
+class KateDocument;
+class KateView;
+class KateViewInternal;
+
+namespace Kate {
+ class Command;
+}
+
+/**
+ * This class is required because QScrollBar's sliderMoved() signal is
+ * really supposed to be a sliderDragged() signal... so this way we can capture
+ * MMB slider moves as well
+ *
+ * Also, it adds some usefull indicators on the scrollbar.
+ */
+class KateScrollBar : public QScrollBar
+{
+ Q_OBJECT
+
+ public:
+ KateScrollBar(Orientation orientation, class KateViewInternal *parent, const char* name = 0L);
+
+ inline bool showMarks() { return m_showMarks; };
+ inline void setShowMarks(bool b) { m_showMarks = b; update(); };
+
+ signals:
+ void sliderMMBMoved(int value);
+
+ protected:
+ virtual void mousePressEvent(QMouseEvent* e);
+ virtual void mouseReleaseEvent(QMouseEvent* e);
+ virtual void mouseMoveEvent (QMouseEvent* e);
+ virtual void paintEvent(QPaintEvent *);
+ virtual void resizeEvent(QResizeEvent *);
+ virtual void styleChange(QStyle &oldStyle);
+ virtual void valueChange();
+ virtual void rangeChange();
+
+ protected slots:
+ void sliderMaybeMoved(int value);
+ void marksChanged();
+
+ private:
+ void redrawMarks();
+ void recomputeMarksPositions(bool forceFullUpdate = false);
+ void watchScrollBarSize();
+
+ bool m_middleMouseDown;
+
+ KateView *m_view;
+ KateDocument *m_doc;
+ class KateViewInternal *m_viewInternal;
+
+ int m_topMargin;
+ int m_bottomMargin;
+ uint m_savVisibleLines;
+
+ QIntDict<QColor> m_lines;
+
+ bool m_showMarks;
+};
+
+class KateCmdLine : public KLineEdit
+{
+ Q_OBJECT
+
+ public:
+ KateCmdLine (KateView *view);
+
+ private slots:
+ void slotReturnPressed ( const QString& cmd );
+ void hideMe ();
+
+ protected:
+ void focusInEvent ( QFocusEvent *ev );
+ void keyPressEvent( QKeyEvent *ev );
+
+ private:
+ void fromHistory( bool up );
+ KateView *m_view;
+ bool m_msgMode;
+ QString m_oldText;
+ uint m_histpos; ///< position in the history
+ uint m_cmdend; ///< the point where a command ends in the text, if we have a valid one.
+ Kate::Command *m_command; ///< For completing flags/args and interactiveness
+ class KCompletion *m_oldCompletionObject; ///< save while completing command args.
+ class KateCmdLnWhatsThis *m_help;
+};
+
+class KateIconBorder : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ KateIconBorder( KateViewInternal* internalView, QWidget *parent );
+
+ // VERY IMPORTANT ;)
+ virtual QSize sizeHint() const;
+
+ void updateFont();
+ int lineNumberWidth() const;
+
+ void setIconBorderOn( bool enable );
+ void setLineNumbersOn( bool enable );
+ void setDynWrapIndicators(int state );
+ int dynWrapIndicators() const { return m_dynWrapIndicators; }
+ bool dynWrapIndicatorsOn() const { return m_dynWrapIndicatorsOn; }
+ void setFoldingMarkersOn( bool enable );
+ void toggleIconBorder() { setIconBorderOn( !iconBorderOn() ); }
+ void toggleLineNumbers() { setLineNumbersOn( !lineNumbersOn() ); }
+ void toggleFoldingMarkers() { setFoldingMarkersOn( !foldingMarkersOn() ); }
+ bool iconBorderOn() const { return m_iconBorderOn; }
+ bool lineNumbersOn() const { return m_lineNumbersOn; }
+ bool foldingMarkersOn() const { return m_foldingMarkersOn; }
+
+ enum BorderArea { None, LineNumbers, IconBorder, FoldingMarkers };
+ BorderArea positionToArea( const QPoint& ) const;
+
+ signals:
+ void toggleRegionVisibility( unsigned int );
+
+ private:
+ void paintEvent( QPaintEvent* );
+ void paintBorder (int x, int y, int width, int height);
+
+ void mousePressEvent( QMouseEvent* );
+ void mouseMoveEvent( QMouseEvent* );
+ void mouseReleaseEvent( QMouseEvent* );
+ void mouseDoubleClickEvent( QMouseEvent* );
+
+ void showMarkMenu( uint line, const QPoint& pos );
+
+ KateView *m_view;
+ KateDocument *m_doc;
+ KateViewInternal *m_viewInternal;
+
+ bool m_iconBorderOn:1;
+ bool m_lineNumbersOn:1;
+ bool m_foldingMarkersOn:1;
+ bool m_dynWrapIndicatorsOn:1;
+ int m_dynWrapIndicators;
+
+ uint m_lastClickedLine;
+
+ int m_cachedLNWidth;
+
+ int m_maxCharWidth;
+
+ mutable QPixmap m_arrow;
+ mutable QColor m_oldBackgroundColor;
+};
+
+class KateViewEncodingAction : public KActionMenu
+{
+ Q_OBJECT
+
+ public:
+ KateViewEncodingAction(KateDocument *_doc, KateView *_view, const QString& text, QObject* parent = 0, const char* name = 0);
+
+ ~KateViewEncodingAction(){;};
+
+ private:
+ KateDocument* doc;
+ KateView *view;
+
+ public slots:
+ void slotAboutToShow();
+
+ private slots:
+ void setMode (int mode);
+};
+
+#endif
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/kateviewinternal.cpp b/kate/part/kateviewinternal.cpp
new file mode 100644
index 000000000..96edc1a9c
--- /dev/null
+++ b/kate/part/kateviewinternal.cpp
@@ -0,0 +1,3496 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org>
+ Copyright (C) 2002 Joseph Wenninger <jowenn@kde.org>
+ Copyright (C) 2002,2003 Christoph Cullmann <cullmann@kde.org>
+ Copyright (C) 2002,2003 Hamish Rodda <rodda@kde.org>
+ Copyright (C) 2003 Anakim Border <aborder@sources.sourceforge.net>
+
+ Based on:
+ KWriteView : Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kateviewinternal.h"
+#include "kateviewinternal.moc"
+
+#include "kateview.h"
+#include "katecodefoldinghelpers.h"
+#include "kateviewhelpers.h"
+#include "katehighlight.h"
+#include "katesupercursor.h"
+#include "katerenderer.h"
+#include "katecodecompletion.h"
+#include "kateconfig.h"
+
+#include <kcursor.h>
+#include <kdebug.h>
+#include <kapplication.h>
+#include <kglobalsettings.h>
+#include <kurldrag.h>
+
+#include <qstyle.h>
+#include <qdragobject.h>
+#include <qpopupmenu.h>
+#include <qdropsite.h>
+#include <qpainter.h>
+#include <qlayout.h>
+#include <qclipboard.h>
+#include <qpixmap.h>
+#include <qvbox.h>
+
+KateViewInternal::KateViewInternal(KateView *view, KateDocument *doc)
+ : QWidget (view, "", Qt::WStaticContents | Qt::WRepaintNoErase | Qt::WResizeNoErase )
+ , editSessionNumber (0)
+ , editIsRunning (false)
+ , m_view (view)
+ , m_doc (doc)
+ , cursor (doc, true, 0, 0, this)
+ , possibleTripleClick (false)
+ , m_dummy (0)
+ , m_startPos(doc, true, 0,0)
+ , m_madeVisible(false)
+ , m_shiftKeyPressed (false)
+ , m_autoCenterLines (false)
+ , m_selChangedByUser (false)
+ , selectAnchor (-1, -1)
+ , m_selectionMode( Default )
+ , m_preserveMaxX(false)
+ , m_currentMaxX(0)
+ , m_usePlainLines(false)
+ , m_updatingView(true)
+ , m_cachedMaxStartPos(-1, -1)
+ , m_dragScrollTimer(this)
+ , m_scrollTimer (this)
+ , m_cursorTimer (this)
+ , m_textHintTimer (this)
+ , m_textHintEnabled(false)
+ , m_textHintMouseX(-1)
+ , m_textHintMouseY(-1)
+ , m_imPreeditStartLine(0)
+ , m_imPreeditStart(0)
+ , m_imPreeditLength(0)
+ , m_imPreeditSelStart(0)
+{
+ setMinimumSize (0,0);
+
+ // cursor
+ cursor.setMoveOnInsert (true);
+
+ // invalidate selStartCached, or keyb selection is screwed initially
+ selStartCached.setLine( -1 );
+ //
+ // scrollbar for lines
+ //
+ m_lineScroll = new KateScrollBar(QScrollBar::Vertical, this);
+ m_lineScroll->show();
+ m_lineScroll->setTracking (true);
+
+ m_lineLayout = new QVBoxLayout();
+ m_colLayout = new QHBoxLayout();
+
+ m_colLayout->addWidget(m_lineScroll);
+ m_lineLayout->addLayout(m_colLayout);
+
+ // bottom corner box
+ m_dummy = new QWidget(m_view);
+ m_dummy->setFixedHeight(style().scrollBarExtent().width());
+
+ if (m_view->dynWordWrap())
+ m_dummy->hide();
+ else
+ m_dummy->show();
+
+ m_lineLayout->addWidget(m_dummy);
+
+ // Hijack the line scroller's controls, so we can scroll nicely for word-wrap
+ connect(m_lineScroll, SIGNAL(prevPage()), SLOT(scrollPrevPage()));
+ connect(m_lineScroll, SIGNAL(nextPage()), SLOT(scrollNextPage()));
+
+ connect(m_lineScroll, SIGNAL(prevLine()), SLOT(scrollPrevLine()));
+ connect(m_lineScroll, SIGNAL(nextLine()), SLOT(scrollNextLine()));
+
+ connect(m_lineScroll, SIGNAL(sliderMoved(int)), SLOT(scrollLines(int)));
+ connect(m_lineScroll, SIGNAL(sliderMMBMoved(int)), SLOT(scrollLines(int)));
+
+ // catch wheel events, completing the hijack
+ m_lineScroll->installEventFilter(this);
+
+ //
+ // scrollbar for columns
+ //
+ m_columnScroll = new QScrollBar(QScrollBar::Horizontal,m_view);
+
+ // hide the column scrollbar in the dynamic word wrap mode
+ if (m_view->dynWordWrap())
+ m_columnScroll->hide();
+ else
+ m_columnScroll->show();
+
+ m_columnScroll->setTracking(true);
+ m_startX = 0;
+
+ connect( m_columnScroll, SIGNAL( valueChanged (int) ),
+ this, SLOT( scrollColumns (int) ) );
+
+ //
+ // iconborder ;)
+ //
+ leftBorder = new KateIconBorder( this, m_view );
+ leftBorder->show ();
+
+ connect( leftBorder, SIGNAL(toggleRegionVisibility(unsigned int)),
+ m_doc->foldingTree(), SLOT(toggleRegionVisibility(unsigned int)));
+
+ connect( doc->foldingTree(), SIGNAL(regionVisibilityChangedAt(unsigned int)),
+ this, SLOT(slotRegionVisibilityChangedAt(unsigned int)));
+ connect( doc, SIGNAL(codeFoldingUpdated()),
+ this, SLOT(slotCodeFoldingChanged()) );
+
+ displayCursor.setPos(0, 0);
+ cursor.setPos(0, 0);
+ cXPos = 0;
+
+ setAcceptDrops( true );
+ setBackgroundMode( NoBackground );
+
+ // event filter
+ installEventFilter(this);
+
+ // im
+ setInputMethodEnabled(true);
+
+ // set initial cursor
+ setCursor( KCursor::ibeamCursor() );
+ m_mouseCursor = IbeamCursor;
+
+ // call mouseMoveEvent also if no mouse button is pressed
+ setMouseTracking(true);
+
+ dragInfo.state = diNone;
+
+ // timers
+ connect( &m_dragScrollTimer, SIGNAL( timeout() ),
+ this, SLOT( doDragScroll() ) );
+
+ connect( &m_scrollTimer, SIGNAL( timeout() ),
+ this, SLOT( scrollTimeout() ) );
+
+ connect( &m_cursorTimer, SIGNAL( timeout() ),
+ this, SLOT( cursorTimeout() ) );
+
+ connect( &m_textHintTimer, SIGNAL( timeout() ),
+ this, SLOT( textHintTimeout() ) );
+
+ // selection changed to set anchor
+ connect( m_view, SIGNAL( selectionChanged() ),
+ this, SLOT( viewSelectionChanged() ) );
+
+
+// this is a work arround for RTL desktops
+// should be changed in kde 3.3
+// BTW: this comment has been "ported" from 3.1.X tree
+// any hacker with BIDI knowlege is welcomed to fix kate problems :)
+ if (QApplication::reverseLayout()){
+ m_view->m_grid->addMultiCellWidget(leftBorder, 0, 1, 2, 2);
+ m_view->m_grid->addMultiCellWidget(m_columnScroll, 1, 1, 0, 1);
+ m_view->m_grid->addMultiCellLayout(m_lineLayout, 0, 0, 0, 0);
+ }
+ else{
+ m_view->m_grid->addMultiCellLayout(m_lineLayout, 0, 1, 2, 2);
+ m_view->m_grid->addMultiCellWidget(m_columnScroll, 1, 1, 0, 1);
+ m_view->m_grid->addWidget(leftBorder, 0, 0);
+ }
+
+ updateView ();
+}
+
+KateViewInternal::~KateViewInternal ()
+{
+}
+
+void KateViewInternal::prepareForDynWrapChange()
+{
+ // Which is the current view line?
+ m_wrapChangeViewLine = displayViewLine(displayCursor, true);
+}
+
+void KateViewInternal::dynWrapChanged()
+{
+ if (m_view->dynWordWrap())
+ {
+ m_columnScroll->hide();
+ m_dummy->hide ();
+ }
+ else
+ {
+ m_columnScroll->show();
+ m_dummy->show ();
+ }
+
+ tagAll();
+ updateView();
+
+ if (m_view->dynWordWrap())
+ scrollColumns(0);
+
+ // Determine where the cursor should be to get the cursor on the same view line
+ if (m_wrapChangeViewLine != -1) {
+ KateTextCursor newStart = viewLineOffset(displayCursor, -m_wrapChangeViewLine);
+ makeVisible(newStart, newStart.col(), true);
+ } else {
+ update();
+ }
+}
+
+KateTextCursor KateViewInternal::endPos() const
+{
+ int viewLines = linesDisplayed() - 1;
+
+ if (viewLines < 0) {
+ kdDebug(13030) << "WARNING: viewLines wrong!" << endl;
+ viewLines = 0;
+ }
+
+ // Check to make sure that lineRanges isn't invalid
+ if (!lineRanges.count() || lineRanges[0].line == -1 || viewLines >= (int)lineRanges.count()) {
+ // Switch off use of the cache
+ return KateTextCursor(m_doc->numVisLines() - 1, m_doc->lineLength(m_doc->getRealLine(m_doc->numVisLines() - 1)));
+ }
+
+ for (int i = viewLines; i >= 0; i--) {
+ KateLineRange& thisRange = lineRanges[i];
+
+ if (thisRange.line == -1) continue;
+
+ if (thisRange.virtualLine >= (int)m_doc->numVisLines()) {
+ // Cache is too out of date
+ return KateTextCursor(m_doc->numVisLines() - 1, m_doc->lineLength(m_doc->getRealLine(m_doc->numVisLines() - 1)));
+ }
+
+ return KateTextCursor(thisRange.virtualLine, thisRange.wrap ? thisRange.endCol - 1 : thisRange.endCol);
+ }
+
+ Q_ASSERT(false);
+ kdDebug(13030) << "WARNING: could not find a lineRange at all" << endl;
+ return KateTextCursor(-1, -1);
+}
+
+uint KateViewInternal::endLine() const
+{
+ return endPos().line();
+}
+
+KateLineRange KateViewInternal::yToKateLineRange(uint y) const
+{
+ uint range = y / m_view->renderer()->fontHeight();
+
+ // lineRanges is always bigger than 0, after the initial updateView call
+ if (range >= lineRanges.size())
+ return lineRanges[lineRanges.size()-1];
+
+ return lineRanges[range];
+}
+
+int KateViewInternal::lineToY(uint viewLine) const
+{
+ return (viewLine-startLine()) * m_view->renderer()->fontHeight();
+}
+
+void KateViewInternal::slotIncFontSizes()
+{
+ m_view->renderer()->increaseFontSizes();
+}
+
+void KateViewInternal::slotDecFontSizes()
+{
+ m_view->renderer()->decreaseFontSizes();
+}
+
+/**
+ * Line is the real line number to scroll to.
+ */
+void KateViewInternal::scrollLines ( int line )
+{
+ KateTextCursor newPos(line, 0);
+ scrollPos(newPos);
+}
+
+// This can scroll less than one true line
+void KateViewInternal::scrollViewLines(int offset)
+{
+ KateTextCursor c = viewLineOffset(startPos(), offset);
+ scrollPos(c);
+
+ m_lineScroll->blockSignals(true);
+ m_lineScroll->setValue(startLine());
+ m_lineScroll->blockSignals(false);
+}
+
+void KateViewInternal::scrollNextPage()
+{
+ scrollViewLines(kMax( (int)linesDisplayed() - 1, 0 ));
+}
+
+void KateViewInternal::scrollPrevPage()
+{
+ scrollViewLines(-kMax( (int)linesDisplayed() - 1, 0 ));
+}
+
+void KateViewInternal::scrollPrevLine()
+{
+ scrollViewLines(-1);
+}
+
+void KateViewInternal::scrollNextLine()
+{
+ scrollViewLines(1);
+}
+
+KateTextCursor KateViewInternal::maxStartPos(bool changed)
+{
+ m_usePlainLines = true;
+
+ if (m_cachedMaxStartPos.line() == -1 || changed)
+ {
+ KateTextCursor end(m_doc->numVisLines() - 1, m_doc->lineLength(m_doc->getRealLine(m_doc->numVisLines() - 1)));
+
+ m_cachedMaxStartPos = viewLineOffset(end, -((int)linesDisplayed() - 1));
+ }
+
+ m_usePlainLines = false;
+
+ return m_cachedMaxStartPos;
+}
+
+// c is a virtual cursor
+void KateViewInternal::scrollPos(KateTextCursor& c, bool force, bool calledExternally)
+{
+ if (!force && ((!m_view->dynWordWrap() && c.line() == (int)startLine()) || c == startPos()))
+ return;
+
+ if (c.line() < 0)
+ c.setLine(0);
+
+ KateTextCursor limit = maxStartPos();
+ if (c > limit) {
+ c = limit;
+
+ // Re-check we're not just scrolling to the same place
+ if (!force && ((!m_view->dynWordWrap() && c.line() == (int)startLine()) || c == startPos()))
+ return;
+ }
+
+ int viewLinesScrolled = 0;
+
+ // only calculate if this is really used and usefull, could be wrong here, please recheck
+ // for larger scrolls this makes 2-4 seconds difference on my xeon with dyn. word wrap on
+ // try to get it really working ;)
+ bool viewLinesScrolledUsable = !force
+ && (c.line() >= (int)startLine()-(int)linesDisplayed()-1)
+ && (c.line() <= (int)endLine()+(int)linesDisplayed()+1);
+
+ if (viewLinesScrolledUsable)
+ viewLinesScrolled = displayViewLine(c);
+
+ m_startPos.setPos(c);
+
+ // set false here but reversed if we return to makeVisible
+ m_madeVisible = false;
+
+ if (viewLinesScrolledUsable)
+ {
+ int lines = linesDisplayed();
+ if ((int)m_doc->numVisLines() < lines) {
+ KateTextCursor end(m_doc->numVisLines() - 1, m_doc->lineLength(m_doc->getRealLine(m_doc->numVisLines() - 1)));
+ lines = kMin((int)linesDisplayed(), displayViewLine(end) + 1);
+ }
+
+ Q_ASSERT(lines >= 0);
+
+ if (!calledExternally && QABS(viewLinesScrolled) < lines)
+ {
+ updateView(false, viewLinesScrolled);
+
+ int scrollHeight = -(viewLinesScrolled * (int)m_view->renderer()->fontHeight());
+ int scrollbarWidth = style().scrollBarExtent().width();
+
+ //
+ // updates are for working around the scrollbar leaving blocks in the view
+ //
+ scroll(0, scrollHeight);
+ update(0, height()+scrollHeight-scrollbarWidth, width(), 2*scrollbarWidth);
+
+ leftBorder->scroll(0, scrollHeight);
+ leftBorder->update(0, leftBorder->height()+scrollHeight-scrollbarWidth, leftBorder->width(), 2*scrollbarWidth);
+
+ return;
+ }
+ }
+
+ updateView();
+ update();
+ leftBorder->update();
+}
+
+void KateViewInternal::scrollColumns ( int x )
+{
+ if (x == m_startX)
+ return;
+
+ if (x < 0)
+ x = 0;
+
+ int dx = m_startX - x;
+ m_startX = x;
+
+ if (QABS(dx) < width())
+ scroll(dx, 0);
+ else
+ update();
+
+ m_columnScroll->blockSignals(true);
+ m_columnScroll->setValue(m_startX);
+ m_columnScroll->blockSignals(false);
+}
+
+// If changed is true, the lines that have been set dirty have been updated.
+void KateViewInternal::updateView(bool changed, int viewLinesScrolled)
+{
+ m_updatingView = true;
+
+ uint contentLines = m_doc->visibleLines();
+
+ m_lineScroll->blockSignals(true);
+
+ KateTextCursor maxStart = maxStartPos(changed);
+ int maxLineScrollRange = maxStart.line();
+ if (m_view->dynWordWrap() && maxStart.col() != 0)
+ maxLineScrollRange++;
+ m_lineScroll->setRange(0, maxLineScrollRange);
+
+ m_lineScroll->setValue(startPos().line());
+ m_lineScroll->setSteps(1, height() / m_view->renderer()->fontHeight());
+ m_lineScroll->blockSignals(false);
+
+ uint oldSize = lineRanges.size ();
+ uint newSize = (height() / m_view->renderer()->fontHeight()) + 1;
+ if (oldSize != newSize) {
+ lineRanges.resize((height() / m_view->renderer()->fontHeight()) + 1);
+ if (newSize > oldSize) {
+ static KateLineRange blank;
+ for (uint i = oldSize; i < newSize; i++) {
+ lineRanges[i] = blank;
+ }
+ }
+ }
+
+ if (oldSize < lineRanges.size ())
+ {
+ for (uint i=oldSize; i < lineRanges.size(); i++)
+ lineRanges[i].dirty = true;
+ }
+
+ // Move the lineRanges data if we've just scrolled...
+ if (viewLinesScrolled != 0) {
+ // loop backwards if we've just scrolled up...
+ bool forwards = viewLinesScrolled >= 0 ? true : false;
+ for (uint z = forwards ? 0 : lineRanges.count() - 1; z < lineRanges.count(); forwards ? z++ : z--) {
+ uint oldZ = z + viewLinesScrolled;
+ if (oldZ < lineRanges.count()) {
+ lineRanges[z] = lineRanges[oldZ];
+ } else {
+ lineRanges[z].dirty = true;
+ }
+ }
+ }
+
+ if (m_view->dynWordWrap())
+ {
+ KateTextCursor realStart = startPos();
+ realStart.setLine(m_doc->getRealLine(realStart.line()));
+
+ KateLineRange startRange = range(realStart);
+ uint line = startRange.virtualLine;
+ int realLine = startRange.line;
+ uint oldLine = line;
+ int startCol = startRange.startCol;
+ int startX = startRange.startX;
+ int endX = startRange.startX;
+ int shiftX = startRange.startCol ? startRange.shiftX : 0;
+ bool wrap = false;
+ int newViewLine = startRange.viewLine;
+ // z is the current display view line
+ KateTextLine::Ptr text = textLine(realLine);
+
+ bool alreadyDirty = false;
+
+ for (uint z = 0; z < lineRanges.size(); z++)
+ {
+ if (oldLine != line) {
+ realLine = (int)m_doc->getRealLine(line);
+
+ if (z)
+ lineRanges[z-1].startsInvisibleBlock = (realLine != lineRanges[z-1].line + 1);
+
+ text = textLine(realLine);
+ startCol = 0;
+ startX = 0;
+ endX = 0;
+ shiftX = 0;
+ newViewLine = 0;
+ oldLine = line;
+ }
+
+ if (line >= contentLines || !text)
+ {
+ if (lineRanges[z].line != -1)
+ lineRanges[z].dirty = true;
+
+ lineRanges[z].clear();
+
+ line++;
+ }
+ else
+ {
+ if (lineRanges[z].line != realLine || lineRanges[z].startCol != startCol)
+ alreadyDirty = lineRanges[z].dirty = true;
+
+ if (lineRanges[z].dirty || changed || alreadyDirty) {
+ alreadyDirty = true;
+
+ lineRanges[z].virtualLine = line;
+ lineRanges[z].line = realLine;
+ lineRanges[z].startsInvisibleBlock = false;
+
+ int tempEndX = 0;
+
+ int endCol = m_view->renderer()->textWidth(text, startCol, width() - shiftX, &wrap, &tempEndX);
+
+ endX += tempEndX;
+
+ if (wrap)
+ {
+ if (m_view->config()->dynWordWrapAlignIndent() > 0)
+ {
+ if (startX == 0)
+ {
+ int pos = text->nextNonSpaceChar(0);
+
+ if (pos > 0)
+ shiftX = m_view->renderer()->textWidth(text, pos);
+
+ if (shiftX > ((double)width() / 100 * m_view->config()->dynWordWrapAlignIndent()))
+ shiftX = 0;
+ }
+ }
+
+ if ((lineRanges[z].startX != startX) || (lineRanges[z].endX != endX) ||
+ (lineRanges[z].startCol != startCol) || (lineRanges[z].endCol != endCol) ||
+ (lineRanges[z].shiftX != shiftX))
+ lineRanges[z].dirty = true;
+
+ lineRanges[z].startCol = startCol;
+ lineRanges[z].endCol = endCol;
+ lineRanges[z].startX = startX;
+ lineRanges[z].endX = endX;
+ lineRanges[z].viewLine = newViewLine;
+ lineRanges[z].wrap = true;
+
+ startCol = endCol;
+ startX = endX;
+ }
+ else
+ {
+ if ((lineRanges[z].startX != startX) || (lineRanges[z].endX != endX) ||
+ (lineRanges[z].startCol != startCol) || (lineRanges[z].endCol != endCol))
+ lineRanges[z].dirty = true;
+
+ lineRanges[z].startCol = startCol;
+ lineRanges[z].endCol = endCol;
+ lineRanges[z].startX = startX;
+ lineRanges[z].endX = endX;
+ lineRanges[z].viewLine = newViewLine;
+ lineRanges[z].wrap = false;
+
+ line++;
+ }
+
+ lineRanges[z].shiftX = shiftX;
+
+ } else {
+ // The cached data is still intact
+ if (lineRanges[z].wrap) {
+ startCol = lineRanges[z].endCol;
+ startX = lineRanges[z].endX;
+ endX = lineRanges[z].endX;
+ } else {
+ line++;
+ }
+ shiftX = lineRanges[z].shiftX;
+ }
+ }
+ newViewLine++;
+ }
+ }
+ else
+ {
+ uint z = 0;
+
+ for(; (z + startLine() < contentLines) && (z < lineRanges.size()); z++)
+ {
+ if (lineRanges[z].dirty || lineRanges[z].line != (int)m_doc->getRealLine(z + startLine())) {
+ lineRanges[z].dirty = true;
+
+ lineRanges[z].line = m_doc->getRealLine( z + startLine() );
+ if (z)
+ lineRanges[z-1].startsInvisibleBlock = (lineRanges[z].line != lineRanges[z-1].line + 1);
+
+ lineRanges[z].virtualLine = z + startLine();
+ lineRanges[z].startCol = 0;
+ lineRanges[z].endCol = m_doc->lineLength(lineRanges[z].line);
+ lineRanges[z].startX = 0;
+ lineRanges[z].endX = m_view->renderer()->textWidth( textLine( lineRanges[z].line ), -1 );
+ lineRanges[z].shiftX = 0;
+ lineRanges[z].viewLine = 0;
+ lineRanges[z].wrap = false;
+ }
+ else if (z && lineRanges[z-1].dirty)
+ {
+ lineRanges[z-1].startsInvisibleBlock = (lineRanges[z].line != lineRanges[z-1].line + 1);
+ }
+ }
+
+ for (; z < lineRanges.size(); z++)
+ {
+ if (lineRanges[z].line != -1)
+ lineRanges[z].dirty = true;
+
+ lineRanges[z].clear();
+ }
+
+ int max = maxLen(startLine()) - width();
+ if (max < 0)
+ max = 0;
+
+ // if we lose the ability to scroll horizontally, move view to the far-left
+ if (max == 0)
+ {
+ scrollColumns(0);
+ }
+
+ m_columnScroll->blockSignals(true);
+
+ // disable scrollbar
+ m_columnScroll->setDisabled (max == 0);
+
+ m_columnScroll->setRange(0, max);
+
+ m_columnScroll->setValue(m_startX);
+
+ // Approximate linescroll
+ m_columnScroll->setSteps(m_view->renderer()->config()->fontMetrics()->width('a'), width());
+
+ m_columnScroll->blockSignals(false);
+ }
+
+ m_updatingView = false;
+
+ if (changed)
+ paintText(0, 0, width(), height(), true);
+}
+
+void KateViewInternal::paintText (int x, int y, int width, int height, bool paintOnlyDirty)
+{
+ //kdDebug() << k_funcinfo << x << " " << y << " " << width << " " << height << " " << paintOnlyDirty << endl;
+ int xStart = startX() + x;
+ int xEnd = xStart + width;
+ uint h = m_view->renderer()->fontHeight();
+ uint startz = (y / h);
+ uint endz = startz + 1 + (height / h);
+ uint lineRangesSize = lineRanges.size();
+
+ static QPixmap drawBuffer;
+
+ if (drawBuffer.width() < KateViewInternal::width() || drawBuffer.height() < (int)h)
+ drawBuffer.resize(KateViewInternal::width(), (int)h);
+
+ if (drawBuffer.isNull())
+ return;
+
+ QPainter paint(this);
+ QPainter paintDrawBuffer(&drawBuffer);
+
+ // TODO put in the proper places
+ m_view->renderer()->setCaretStyle(m_view->isOverwriteMode() ? KateRenderer::Replace : KateRenderer::Insert);
+ m_view->renderer()->setShowTabs(m_doc->configFlags() & KateDocument::cfShowTabs);
+
+ for (uint z=startz; z <= endz; z++)
+ {
+ if ( (z >= lineRangesSize) || ((lineRanges[z].line == -1) && (!paintOnlyDirty || lineRanges[z].dirty)) )
+ {
+ if (!(z >= lineRangesSize))
+ lineRanges[z].dirty = false;
+
+ paint.fillRect( x, z * h, width, h, m_view->renderer()->config()->backgroundColor() );
+ }
+ else if (!paintOnlyDirty || lineRanges[z].dirty)
+ {
+ lineRanges[z].dirty = false;
+
+ m_view->renderer()->paintTextLine(paintDrawBuffer, &lineRanges[z], xStart, xEnd, &cursor, &bm);
+
+ paint.drawPixmap (x, z * h, drawBuffer, 0, 0, width, h);
+ }
+ }
+}
+
+/**
+ * this function ensures a certain location is visible on the screen.
+ * if endCol is -1, ignore making the columns visible.
+ */
+void KateViewInternal::makeVisible (const KateTextCursor& c, uint endCol, bool force, bool center, bool calledExternally)
+{
+ //kdDebug() << "MakeVisible start [" << startPos().line << "," << startPos().col << "] end [" << endPos().line << "," << endPos().col << "] -> request: [" << c.line << "," << c.col << "]" <<endl;// , new start [" << scroll.line << "," << scroll.col << "] lines " << (linesDisplayed() - 1) << " height " << height() << endl;
+ // if the line is in a folded region, unfold all the way up
+ //if ( m_doc->foldingTree()->findNodeForLine( c.line )->visible )
+ // kdDebug()<<"line ("<<c.line<<") should be visible"<<endl;
+
+ if ( force )
+ {
+ KateTextCursor scroll = c;
+ scrollPos(scroll, force, calledExternally);
+ }
+ else if (center && (c < startPos() || c > endPos()))
+ {
+ KateTextCursor scroll = viewLineOffset(c, -int(linesDisplayed()) / 2);
+ scrollPos(scroll, false, calledExternally);
+ }
+ else if ( c > viewLineOffset(endPos(), -m_minLinesVisible) )
+ {
+ KateTextCursor scroll = viewLineOffset(c, -((int)linesDisplayed() - m_minLinesVisible - 1));
+ scrollPos(scroll, false, calledExternally);
+ }
+ else if ( c < viewLineOffset(startPos(), m_minLinesVisible) )
+ {
+ KateTextCursor scroll = viewLineOffset(c, -m_minLinesVisible);
+ scrollPos(scroll, false, calledExternally);
+ }
+ else
+ {
+ // Check to see that we're not showing blank lines
+ KateTextCursor max = maxStartPos();
+ if (startPos() > max) {
+ scrollPos(max, max.col(), calledExternally);
+ }
+ }
+
+ if (!m_view->dynWordWrap() && endCol != (uint)-1)
+ {
+ int sX = (int)m_view->renderer()->textWidth (textLine( m_doc->getRealLine( c.line() ) ), c.col() );
+
+ int sXborder = sX-8;
+ if (sXborder < 0)
+ sXborder = 0;
+
+ if (sX < m_startX)
+ scrollColumns (sXborder);
+ else if (sX > m_startX + width())
+ scrollColumns (sX - width() + 8);
+ }
+
+ m_madeVisible = !force;
+}
+
+void KateViewInternal::slotRegionVisibilityChangedAt(unsigned int)
+{
+ kdDebug(13030) << "slotRegionVisibilityChangedAt()" << endl;
+ m_cachedMaxStartPos.setLine(-1);
+ KateTextCursor max = maxStartPos();
+ if (startPos() > max)
+ scrollPos(max);
+
+ updateView();
+ update();
+ leftBorder->update();
+}
+
+void KateViewInternal::slotCodeFoldingChanged()
+{
+ leftBorder->update();
+}
+
+void KateViewInternal::slotRegionBeginEndAddedRemoved(unsigned int)
+{
+ kdDebug(13030) << "slotRegionBeginEndAddedRemoved()" << endl;
+ // FIXME: performance problem
+ leftBorder->update();
+}
+
+void KateViewInternal::showEvent ( QShowEvent *e )
+{
+ updateView ();
+
+ QWidget::showEvent (e);
+}
+
+uint KateViewInternal::linesDisplayed() const
+{
+ int h = height();
+ int fh = m_view->renderer()->fontHeight();
+
+ return (h - (h % fh)) / fh;
+}
+
+QPoint KateViewInternal::cursorCoordinates()
+{
+ int viewLine = displayViewLine(displayCursor, true);
+
+ if (viewLine == -1)
+ return QPoint(-1, -1);
+
+ uint y = viewLine * m_view->renderer()->fontHeight();
+ uint x = cXPos - m_startX - lineRanges[viewLine].startX + leftBorder->width() + lineRanges[viewLine].xOffset();
+
+ return QPoint(x, y);
+}
+
+void KateViewInternal::updateMicroFocusHint()
+{
+ int line = displayViewLine(displayCursor, true);
+ /* Check for hasFocus() to avoid crashes in QXIMInputContext as in bug #131266.
+ This is only a workaround until somebody can find the real reason of the crash
+ (probably it's in Qt). */
+ if (line == -1 || !hasFocus())
+ return;
+
+ KateRenderer *renderer = m_view->renderer();
+
+ // Cursor placement code is changed for Asian input method that
+ // shows candidate window. This behavior is same as Qt/E 2.3.7
+ // which supports Asian input methods. Asian input methods need
+ // start point of IM selection text to place candidate window as
+ // adjacent to the selection text.
+ uint preeditStrLen = renderer->textWidth(textLine(m_imPreeditStartLine), cursor.col()) - renderer->textWidth(textLine(m_imPreeditStartLine), m_imPreeditSelStart);
+ uint x = cXPos - m_startX - lineRanges[line].startX + lineRanges[line].xOffset() - preeditStrLen;
+ uint y = line * renderer->fontHeight();
+
+ setMicroFocusHint(x, y, 0, renderer->fontHeight());
+}
+
+void KateViewInternal::doReturn()
+{
+ KateTextCursor c = cursor;
+ m_doc->newLine( c, this );
+ updateCursor( c );
+ updateView();
+}
+
+void KateViewInternal::doDelete()
+{
+ m_doc->del( m_view, cursor );
+ if (m_view->m_codeCompletion->codeCompletionVisible()) {
+ m_view->m_codeCompletion->updateBox();
+ }
+}
+
+void KateViewInternal::doBackspace()
+{
+ m_doc->backspace( m_view, cursor );
+ if (m_view->m_codeCompletion->codeCompletionVisible()) {
+ m_view->m_codeCompletion->updateBox();
+ }
+}
+
+void KateViewInternal::doTranspose()
+{
+ m_doc->transpose( cursor );
+}
+
+void KateViewInternal::doDeleteWordLeft()
+{
+ wordLeft( true );
+ m_view->removeSelectedText();
+ update();
+}
+
+void KateViewInternal::doDeleteWordRight()
+{
+ wordRight( true );
+ m_view->removeSelectedText();
+ update();
+}
+
+class CalculatingCursor : public KateTextCursor {
+public:
+ CalculatingCursor(KateViewInternal* vi)
+ : KateTextCursor()
+ , m_vi(vi)
+ {
+ Q_ASSERT(valid());
+ }
+
+ CalculatingCursor(KateViewInternal* vi, const KateTextCursor& c)
+ : KateTextCursor(c)
+ , m_vi(vi)
+ {
+ Q_ASSERT(valid());
+ }
+
+ // This one constrains its arguments to valid positions
+ CalculatingCursor(KateViewInternal* vi, uint line, uint col)
+ : KateTextCursor(line, col)
+ , m_vi(vi)
+ {
+ makeValid();
+ }
+
+
+ virtual CalculatingCursor& operator+=( int n ) = 0;
+
+ virtual CalculatingCursor& operator-=( int n ) = 0;
+
+ CalculatingCursor& operator++() { return operator+=( 1 ); }
+
+ CalculatingCursor& operator--() { return operator-=( 1 ); }
+
+ void makeValid() {
+ m_line = kMax( 0, kMin( int( m_vi->m_doc->numLines() - 1 ), line() ) );
+ if (m_vi->m_view->wrapCursor())
+ m_col = kMax( 0, kMin( m_vi->m_doc->lineLength( line() ), col() ) );
+ else
+ m_col = kMax( 0, col() );
+ Q_ASSERT( valid() );
+ }
+
+ void toEdge( Bias bias ) {
+ if( bias == left ) m_col = 0;
+ else if( bias == right ) m_col = m_vi->m_doc->lineLength( line() );
+ }
+
+ bool atEdge() const { return atEdge( left ) || atEdge( right ); }
+
+ bool atEdge( Bias bias ) const {
+ switch( bias ) {
+ case left: return col() == 0;
+ case none: return atEdge();
+ case right: return col() == m_vi->m_doc->lineLength( line() );
+ default: Q_ASSERT(false); return false;
+ }
+ }
+
+protected:
+ bool valid() const {
+ return line() >= 0 &&
+ uint( line() ) < m_vi->m_doc->numLines() &&
+ col() >= 0 &&
+ (!m_vi->m_view->wrapCursor() || col() <= m_vi->m_doc->lineLength( line() ));
+ }
+ KateViewInternal* m_vi;
+};
+
+class BoundedCursor : public CalculatingCursor {
+public:
+ BoundedCursor(KateViewInternal* vi)
+ : CalculatingCursor( vi ) {};
+ BoundedCursor(KateViewInternal* vi, const KateTextCursor& c )
+ : CalculatingCursor( vi, c ) {};
+ BoundedCursor(KateViewInternal* vi, uint line, uint col )
+ : CalculatingCursor( vi, line, col ) {};
+ virtual CalculatingCursor& operator+=( int n ) {
+ m_col += n;
+
+ if (n > 0 && m_vi->m_view->dynWordWrap()) {
+ // Need to constrain to current visible text line for dynamic wrapping mode
+ if (m_col > m_vi->m_doc->lineLength(m_line)) {
+ KateLineRange currentRange = m_vi->range(*this);
+
+ int endX;
+ bool crap;
+ m_vi->m_view->renderer()->textWidth(m_vi->textLine(m_line), currentRange.startCol, m_vi->width() - currentRange.xOffset(), &crap, &endX);
+ endX += (m_col - currentRange.endCol + 1) * m_vi->m_view->renderer()->spaceWidth();
+
+ // Constraining if applicable NOTE: some code duplication in KateViewInternal::resize()
+ if (endX >= m_vi->width() - currentRange.xOffset()) {
+ m_col -= n;
+ if ( uint( line() ) < m_vi->m_doc->numLines() - 1 ) {
+ m_line++;
+ m_col = 0;
+ }
+ }
+ }
+
+ } else if (n < 0 && col() < 0 && line() > 0 ) {
+ m_line--;
+ m_col = m_vi->m_doc->lineLength( line() );
+ }
+
+ m_col = kMax( 0, col() );
+
+ Q_ASSERT( valid() );
+ return *this;
+ }
+ virtual CalculatingCursor& operator-=( int n ) {
+ return operator+=( -n );
+ }
+};
+
+class WrappingCursor : public CalculatingCursor {
+public:
+ WrappingCursor(KateViewInternal* vi)
+ : CalculatingCursor( vi) {};
+ WrappingCursor(KateViewInternal* vi, const KateTextCursor& c )
+ : CalculatingCursor( vi, c ) {};
+ WrappingCursor(KateViewInternal* vi, uint line, uint col )
+ : CalculatingCursor( vi, line, col ) {};
+
+ virtual CalculatingCursor& operator+=( int n ) {
+ if( n < 0 ) return operator-=( -n );
+ int len = m_vi->m_doc->lineLength( line() );
+ if( col() + n <= len ) {
+ m_col += n;
+ } else if( uint( line() ) < m_vi->m_doc->numLines() - 1 ) {
+ n -= len - col() + 1;
+ m_col = 0;
+ m_line++;
+ operator+=( n );
+ } else {
+ m_col = len;
+ }
+ Q_ASSERT( valid() );
+ return *this;
+ }
+ virtual CalculatingCursor& operator-=( int n ) {
+ if( n < 0 ) return operator+=( -n );
+ if( col() - n >= 0 ) {
+ m_col -= n;
+ } else if( line() > 0 ) {
+ n -= col() + 1;
+ m_line--;
+ m_col = m_vi->m_doc->lineLength( line() );
+ operator-=( n );
+ } else {
+ m_col = 0;
+ }
+ Q_ASSERT( valid() );
+ return *this;
+ }
+};
+
+void KateViewInternal::moveChar( Bias bias, bool sel )
+{
+ KateTextCursor c;
+ if ( m_view->wrapCursor() ) {
+ c = WrappingCursor( this, cursor ) += bias;
+ } else {
+ c = BoundedCursor( this, cursor ) += bias;
+ }
+
+ updateSelection( c, sel );
+ updateCursor( c );
+}
+
+void KateViewInternal::cursorLeft( bool sel )
+{
+ if ( ! m_view->wrapCursor() && cursor.col() == 0 )
+ return;
+
+ moveChar( left, sel );
+ if (m_view->m_codeCompletion->codeCompletionVisible()) {
+ m_view->m_codeCompletion->updateBox();
+ }
+}
+
+void KateViewInternal::cursorRight( bool sel )
+{
+ moveChar( right, sel );
+ if (m_view->m_codeCompletion->codeCompletionVisible()) {
+ m_view->m_codeCompletion->updateBox();
+ }
+}
+
+void KateViewInternal::wordLeft ( bool sel )
+{
+ WrappingCursor c( this, cursor );
+
+ // First we skip backwards all space.
+ // Then we look up into which category the current position falls:
+ // 1. a "word" character
+ // 2. a "non-word" character (except space)
+ // 3. the beginning of the line
+ // and skip all preceding characters that fall into this class.
+ // The code assumes that space is never part of the word character class.
+
+ KateHighlighting* h = m_doc->highlight();
+ if( !c.atEdge( left ) ) {
+
+ while( !c.atEdge( left ) && m_doc->textLine( c.line() )[ c.col() - 1 ].isSpace() )
+ --c;
+ }
+ if( c.atEdge( left ) )
+ {
+ --c;
+ }
+ else if( h->isInWord( m_doc->textLine( c.line() )[ c.col() - 1 ] ) )
+ {
+ while( !c.atEdge( left ) && h->isInWord( m_doc->textLine( c.line() )[ c.col() - 1 ] ) )
+ --c;
+ }
+ else
+ {
+ while( !c.atEdge( left )
+ && !h->isInWord( m_doc->textLine( c.line() )[ c.col() - 1 ] )
+ // in order to stay symmetric to wordLeft()
+ // we must not skip space preceding a non-word sequence
+ && !m_doc->textLine( c.line() )[ c.col() - 1 ].isSpace() )
+ {
+ --c;
+ }
+ }
+
+ updateSelection( c, sel );
+ updateCursor( c );
+}
+
+void KateViewInternal::wordRight( bool sel )
+{
+ WrappingCursor c( this, cursor );
+
+ // We look up into which category the current position falls:
+ // 1. a "word" character
+ // 2. a "non-word" character (except space)
+ // 3. the end of the line
+ // and skip all following characters that fall into this class.
+ // If the skipped characters are followed by space, we skip that too.
+ // The code assumes that space is never part of the word character class.
+
+ KateHighlighting* h = m_doc->highlight();
+ if( c.atEdge( right ) )
+ {
+ ++c;
+ }
+ else if( h->isInWord( m_doc->textLine( c.line() )[ c.col() ] ) )
+ {
+ while( !c.atEdge( right ) && h->isInWord( m_doc->textLine( c.line() )[ c.col() ] ) )
+ ++c;
+ }
+ else
+ {
+ while( !c.atEdge( right )
+ && !h->isInWord( m_doc->textLine( c.line() )[ c.col() ] )
+ // we must not skip space, because if that space is followed
+ // by more non-word characters, we would skip them, too
+ && !m_doc->textLine( c.line() )[ c.col() ].isSpace() )
+ {
+ ++c;
+ }
+ }
+
+ while( !c.atEdge( right ) && m_doc->textLine( c.line() )[ c.col() ].isSpace() )
+ ++c;
+
+ updateSelection( c, sel );
+ updateCursor( c );
+}
+
+void KateViewInternal::moveEdge( Bias bias, bool sel )
+{
+ BoundedCursor c( this, cursor );
+ c.toEdge( bias );
+ updateSelection( c, sel );
+ updateCursor( c );
+}
+
+void KateViewInternal::home( bool sel )
+{
+ if (m_view->m_codeCompletion->codeCompletionVisible()) {
+ QKeyEvent e(QEvent::KeyPress, Qt::Key_Home, 0, 0);
+ m_view->m_codeCompletion->handleKey(&e);
+ return;
+ }
+
+ if (m_view->dynWordWrap() && currentRange().startCol) {
+ // Allow us to go to the real start if we're already at the start of the view line
+ if (cursor.col() != currentRange().startCol) {
+ KateTextCursor c(cursor.line(), currentRange().startCol);
+ updateSelection( c, sel );
+ updateCursor( c );
+ return;
+ }
+ }
+
+ if( !(m_doc->configFlags() & KateDocument::cfSmartHome) ) {
+ moveEdge( left, sel );
+ return;
+ }
+
+ KateTextLine::Ptr l = textLine( cursor.line() );
+
+ if (!l)
+ return;
+
+ KateTextCursor c = cursor;
+ int lc = l->firstChar();
+
+ if( lc < 0 || c.col() == lc ) {
+ c.setCol(0);
+ } else {
+ c.setCol(lc);
+ }
+
+ updateSelection( c, sel );
+ updateCursor( c, true );
+}
+
+void KateViewInternal::end( bool sel )
+{
+ if (m_view->m_codeCompletion->codeCompletionVisible()) {
+ QKeyEvent e(QEvent::KeyPress, Qt::Key_End, 0, 0);
+ m_view->m_codeCompletion->handleKey(&e);
+ return;
+ }
+
+ KateLineRange range = currentRange();
+
+ if (m_view->dynWordWrap() && range.wrap) {
+ // Allow us to go to the real end if we're already at the end of the view line
+ if (cursor.col() < range.endCol - 1) {
+ KateTextCursor c(cursor.line(), range.endCol - 1);
+ updateSelection( c, sel );
+ updateCursor( c );
+ return;
+ }
+ }
+
+ if( !(m_doc->configFlags() & KateDocument::cfSmartHome) ) {
+ moveEdge( right, sel );
+ return;
+ }
+
+ KateTextLine::Ptr l = textLine( cursor.line() );
+
+ if (!l)
+ return;
+
+ // "Smart End", as requested in bugs #78258 and #106970
+ KateTextCursor c = cursor;
+
+ // If the cursor is already the real end, jump to last non-space character.
+ // Otherwise, go to the real end ... obviously.
+ if (c.col() == m_doc->lineLength(c.line())) {
+ c.setCol(l->lastChar() + 1);
+ updateSelection(c, sel);
+ updateCursor(c, true);
+ } else {
+ moveEdge(right, sel);
+ }
+}
+
+KateLineRange KateViewInternal::range(int realLine, const KateLineRange* previous)
+{
+ // look at the cache first
+ if (!m_updatingView && realLine >= lineRanges[0].line && realLine <= lineRanges[lineRanges.count() - 1].line)
+ for (uint i = 0; i < lineRanges.count(); i++)
+ if (realLine == lineRanges[i].line)
+ if (!m_view->dynWordWrap() || (!previous && lineRanges[i].startCol == 0) || (previous && lineRanges[i].startCol == previous->endCol))
+ return lineRanges[i];
+
+ // Not in the cache, we have to create it
+ KateLineRange ret;
+
+ KateTextLine::Ptr text = textLine(realLine);
+ if (!text) {
+ return KateLineRange();
+ }
+
+ if (!m_view->dynWordWrap()) {
+ Q_ASSERT(!previous);
+ ret.line = realLine;
+ ret.virtualLine = m_doc->getVirtualLine(realLine);
+ ret.startCol = 0;
+ ret.endCol = m_doc->lineLength(realLine);
+ ret.startX = 0;
+ ret.endX = m_view->renderer()->textWidth(text, -1);
+ ret.viewLine = 0;
+ ret.wrap = false;
+ return ret;
+ }
+
+ ret.endCol = (int)m_view->renderer()->textWidth(text, previous ? previous->endCol : 0, width() - (previous ? previous->shiftX : 0), &ret.wrap, &ret.endX);
+
+ Q_ASSERT(ret.endCol > ret.startCol);
+
+ ret.line = realLine;
+
+ if (previous) {
+ ret.virtualLine = previous->virtualLine;
+ ret.startCol = previous->endCol;
+ ret.startX = previous->endX;
+ ret.endX += previous->endX;
+ ret.shiftX = previous->shiftX;
+ ret.viewLine = previous->viewLine + 1;
+
+ } else {
+ // TODO worthwhile optimising this to get the data out of the initial textWidth call?
+ if (m_view->config()->dynWordWrapAlignIndent() > 0) {
+ int pos = text->nextNonSpaceChar(0);
+
+ if (pos > 0)
+ ret.shiftX = m_view->renderer()->textWidth(text, pos);
+
+ if (ret.shiftX > ((double)width() / 100 * m_view->config()->dynWordWrapAlignIndent()))
+ ret.shiftX = 0;
+ }
+
+ ret.virtualLine = m_doc->getVirtualLine(realLine);
+ ret.startCol = 0;
+ ret.startX = 0;
+ ret.viewLine = 0;
+ }
+
+ return ret;
+}
+
+KateLineRange KateViewInternal::currentRange()
+{
+// Q_ASSERT(m_view->dynWordWrap());
+
+ return range(cursor);
+}
+
+KateLineRange KateViewInternal::previousRange()
+{
+ uint currentViewLine = viewLine(cursor);
+
+ if (currentViewLine)
+ return range(cursor.line(), currentViewLine - 1);
+ else
+ return range(m_doc->getRealLine(displayCursor.line() - 1), -1);
+}
+
+KateLineRange KateViewInternal::nextRange()
+{
+ uint currentViewLine = viewLine(cursor) + 1;
+
+ if (currentViewLine >= viewLineCount(cursor.line())) {
+ currentViewLine = 0;
+ return range(cursor.line() + 1, currentViewLine);
+ } else {
+ return range(cursor.line(), currentViewLine);
+ }
+}
+
+KateLineRange KateViewInternal::range(const KateTextCursor& realCursor)
+{
+// Q_ASSERT(m_view->dynWordWrap());
+
+ KateLineRange thisRange;
+ bool first = true;
+
+ do {
+ thisRange = range(realCursor.line(), first ? 0L : &thisRange);
+ first = false;
+ } while (thisRange.wrap && !(realCursor.col() >= thisRange.startCol && realCursor.col() < thisRange.endCol) && thisRange.startCol != thisRange.endCol);
+
+ return thisRange;
+}
+
+KateLineRange KateViewInternal::range(uint realLine, int viewLine)
+{
+// Q_ASSERT(m_view->dynWordWrap());
+
+ KateLineRange thisRange;
+ bool first = true;
+
+ do {
+ thisRange = range(realLine, first ? 0L : &thisRange);
+ first = false;
+ } while (thisRange.wrap && viewLine != thisRange.viewLine && thisRange.startCol != thisRange.endCol);
+
+ if (viewLine != -1 && viewLine != thisRange.viewLine)
+ kdDebug(13030) << "WARNING: viewLine " << viewLine << " of line " << realLine << " does not exist." << endl;
+
+ return thisRange;
+}
+
+/**
+ * This returns the view line upon which realCursor is situated.
+ * The view line is the number of lines in the view from the first line
+ * The supplied cursor should be in real lines.
+ */
+uint KateViewInternal::viewLine(const KateTextCursor& realCursor)
+{
+ if (!m_view->dynWordWrap()) return 0;
+
+ if (realCursor.col() == 0) return 0;
+
+ KateLineRange thisRange;
+ bool first = true;
+
+ do {
+ thisRange = range(realCursor.line(), first ? 0L : &thisRange);
+ first = false;
+ } while (thisRange.wrap && !(realCursor.col() >= thisRange.startCol && realCursor.col() < thisRange.endCol) && thisRange.startCol != thisRange.endCol);
+
+ return thisRange.viewLine;
+}
+
+int KateViewInternal::displayViewLine(const KateTextCursor& virtualCursor, bool limitToVisible)
+{
+ KateTextCursor work = startPos();
+
+ int limit = linesDisplayed();
+
+ // Efficient non-word-wrapped path
+ if (!m_view->dynWordWrap()) {
+ int ret = virtualCursor.line() - startLine();
+ if (limitToVisible && (ret < 0 || ret > limit))
+ return -1;
+ else
+ return ret;
+ }
+
+ if (work == virtualCursor) {
+ return 0;
+ }
+
+ int ret = -(int)viewLine(work);
+ bool forwards = (work < virtualCursor) ? true : false;
+
+ // FIXME switch to using ranges? faster?
+ if (forwards) {
+ while (work.line() != virtualCursor.line()) {
+ ret += viewLineCount(m_doc->getRealLine(work.line()));
+ work.setLine(work.line() + 1);
+ if (limitToVisible && ret > limit)
+ return -1;
+ }
+ } else {
+ while (work.line() != virtualCursor.line()) {
+ work.setLine(work.line() - 1);
+ ret -= viewLineCount(m_doc->getRealLine(work.line()));
+ if (limitToVisible && ret < 0)
+ return -1;
+ }
+ }
+
+ // final difference
+ KateTextCursor realCursor = virtualCursor;
+ realCursor.setLine(m_doc->getRealLine(realCursor.line()));
+ if (realCursor.col() == -1) realCursor.setCol(m_doc->lineLength(realCursor.line()));
+ ret += viewLine(realCursor);
+
+ if (limitToVisible && (ret < 0 || ret > limit))
+ return -1;
+
+ return ret;
+}
+
+uint KateViewInternal::lastViewLine(uint realLine)
+{
+ if (!m_view->dynWordWrap()) return 0;
+
+ KateLineRange thisRange;
+ bool first = true;
+
+ do {
+ thisRange = range(realLine, first ? 0L : &thisRange);
+ first = false;
+ } while (thisRange.wrap && thisRange.startCol != thisRange.endCol);
+
+ return thisRange.viewLine;
+}
+
+uint KateViewInternal::viewLineCount(uint realLine)
+{
+ return lastViewLine(realLine) + 1;
+}
+
+/*
+ * This returns the cursor which is offset by (offset) view lines.
+ * This is the main function which is called by code not specifically dealing with word-wrap.
+ * The opposite conversion (cursor to offset) can be done with displayViewLine.
+ *
+ * The cursors involved are virtual cursors (ie. equivalent to displayCursor)
+ */
+KateTextCursor KateViewInternal::viewLineOffset(const KateTextCursor& virtualCursor, int offset, bool keepX)
+{
+ if (!m_view->dynWordWrap()) {
+ KateTextCursor ret(kMin((int)m_doc->visibleLines() - 1, virtualCursor.line() + offset), 0);
+
+ if (ret.line() < 0)
+ ret.setLine(0);
+
+ if (keepX) {
+ int realLine = m_doc->getRealLine(ret.line());
+ ret.setCol(m_doc->lineLength(realLine) - 1);
+
+ if (m_currentMaxX > cXPos)
+ cXPos = m_currentMaxX;
+
+ if (m_view->wrapCursor())
+ cXPos = kMin(cXPos, (int)m_view->renderer()->textWidth(textLine(realLine), m_doc->lineLength(realLine)));
+
+ m_view->renderer()->textWidth(ret, cXPos);
+ }
+
+ return ret;
+ }
+
+ KateTextCursor realCursor = virtualCursor;
+ realCursor.setLine(m_doc->getRealLine(virtualCursor.line()));
+
+ uint cursorViewLine = viewLine(realCursor);
+
+ int currentOffset = 0;
+ int virtualLine = 0;
+
+ bool forwards = (offset > 0) ? true : false;
+
+ if (forwards) {
+ currentOffset = lastViewLine(realCursor.line()) - cursorViewLine;
+ if (offset <= currentOffset) {
+ // the answer is on the same line
+ KateLineRange thisRange = range(realCursor.line(), cursorViewLine + offset);
+ Q_ASSERT(thisRange.virtualLine == virtualCursor.line());
+ return KateTextCursor(virtualCursor.line(), thisRange.startCol);
+ }
+
+ virtualLine = virtualCursor.line() + 1;
+
+ } else {
+ offset = -offset;
+ currentOffset = cursorViewLine;
+ if (offset <= currentOffset) {
+ // the answer is on the same line
+ KateLineRange thisRange = range(realCursor.line(), cursorViewLine - offset);
+ Q_ASSERT(thisRange.virtualLine == virtualCursor.line());
+ return KateTextCursor(virtualCursor.line(), thisRange.startCol);
+ }
+
+ virtualLine = virtualCursor.line() - 1;
+ }
+
+ currentOffset++;
+
+ while (virtualLine >= 0 && virtualLine < (int)m_doc->visibleLines())
+ {
+ KateLineRange thisRange;
+ bool first = true;
+ int realLine = m_doc->getRealLine(virtualLine);
+
+ do {
+ thisRange = range(realLine, first ? 0L : &thisRange);
+ first = false;
+
+ if (offset == currentOffset) {
+ if (!forwards) {
+ // We actually want it the other way around
+ int requiredViewLine = lastViewLine(realLine) - thisRange.viewLine;
+ if (requiredViewLine != thisRange.viewLine) {
+ thisRange = range(realLine, requiredViewLine);
+ }
+ }
+
+ KateTextCursor ret(virtualLine, thisRange.startCol);
+
+ // keep column position
+ if (keepX) {
+ ret.setCol(thisRange.endCol - 1);
+ KateTextCursor realCursorTemp(m_doc->getRealLine(virtualCursor.line()), virtualCursor.col());
+ int visibleX = m_view->renderer()->textWidth(realCursorTemp) - range(realCursorTemp).startX;
+ int xOffset = thisRange.startX;
+
+ if (m_currentMaxX > visibleX)
+ visibleX = m_currentMaxX;
+
+ cXPos = xOffset + visibleX;
+
+ cXPos = kMin(cXPos, lineMaxCursorX(thisRange));
+
+ m_view->renderer()->textWidth(ret, cXPos);
+ }
+
+ return ret;
+ }
+
+ currentOffset++;
+
+ } while (thisRange.wrap);
+
+ if (forwards)
+ virtualLine++;
+ else
+ virtualLine--;
+ }
+
+ // Looks like we were asked for something a bit exotic.
+ // Return the max/min valid position.
+ if (forwards)
+ return KateTextCursor(m_doc->visibleLines() - 1, m_doc->lineLength(m_doc->visibleLines() - 1));
+ else
+ return KateTextCursor(0, 0);
+}
+
+int KateViewInternal::lineMaxCursorX(const KateLineRange& range)
+{
+ if (!m_view->wrapCursor() && !range.wrap)
+ return INT_MAX;
+
+ int maxX = range.endX;
+
+ if (maxX && range.wrap) {
+ QChar lastCharInLine = textLine(range.line)->getChar(range.endCol - 1);
+
+ if (lastCharInLine == QChar('\t')) {
+ int lineSize = 0;
+ int lastTabSize = 0;
+ for(int i = range.startCol; i < range.endCol; i++) {
+ if (textLine(range.line)->getChar(i) == QChar('\t')) {
+ lastTabSize = m_view->tabWidth() - (lineSize % m_view->tabWidth());
+ lineSize += lastTabSize;
+ } else {
+ lineSize++;
+ }
+ }
+ maxX -= lastTabSize * m_view->renderer()->spaceWidth();
+ } else {
+ maxX -= m_view->renderer()->config()->fontMetrics()->width(lastCharInLine);
+ }
+ }
+
+ return maxX;
+}
+
+int KateViewInternal::lineMaxCol(const KateLineRange& range)
+{
+ int maxCol = range.endCol;
+
+ if (maxCol && range.wrap)
+ maxCol--;
+
+ return maxCol;
+}
+
+void KateViewInternal::cursorUp(bool sel)
+{
+ if (m_view->m_codeCompletion->codeCompletionVisible()) {
+ QKeyEvent e(QEvent::KeyPress, Qt::Key_Up, 0, 0);
+ m_view->m_codeCompletion->handleKey(&e);
+ return;
+ }
+
+ if (displayCursor.line() == 0 && (!m_view->dynWordWrap() || viewLine(cursor) == 0))
+ return;
+
+ int newLine = cursor.line(), newCol = 0, xOffset = 0, startCol = 0;
+ m_preserveMaxX = true;
+
+ if (m_view->dynWordWrap()) {
+ // Dynamic word wrapping - navigate on visual lines rather than real lines
+ KateLineRange thisRange = currentRange();
+ // This is not the first line because that is already simplified out above
+ KateLineRange pRange = previousRange();
+
+ // Ensure we're in the right spot
+ Q_ASSERT((cursor.line() == thisRange.line) &&
+ (cursor.col() >= thisRange.startCol) &&
+ (!thisRange.wrap || cursor.col() < thisRange.endCol));
+
+ // VisibleX is the distance from the start of the text to the cursor on the current line.
+ int visibleX = m_view->renderer()->textWidth(cursor) - thisRange.startX;
+ int currentLineVisibleX = visibleX;
+
+ // Translate to new line
+ visibleX += thisRange.xOffset();
+ visibleX -= pRange.xOffset();
+
+ // Limit to >= 0
+ visibleX = kMax(0, visibleX);
+
+ startCol = pRange.startCol;
+ xOffset = pRange.startX;
+ newLine = pRange.line;
+
+ // Take into account current max X (ie. if the current line was smaller
+ // than the last definitely specified width)
+ if (thisRange.xOffset() && !pRange.xOffset() && currentLineVisibleX == 0) // Special case for where xOffset may be > m_currentMaxX
+ visibleX = m_currentMaxX;
+ else if (visibleX < m_currentMaxX - pRange.xOffset())
+ visibleX = m_currentMaxX - pRange.xOffset();
+
+ cXPos = xOffset + visibleX;
+
+ cXPos = kMin(cXPos, lineMaxCursorX(pRange));
+
+ newCol = kMin((int)m_view->renderer()->textPos(newLine, visibleX, startCol), lineMaxCol(pRange));
+
+ } else {
+ newLine = m_doc->getRealLine(displayCursor.line() - 1);
+
+ if ((m_view->wrapCursor()) && m_currentMaxX > cXPos)
+ cXPos = m_currentMaxX;
+ }
+
+ KateTextCursor c(newLine, newCol);
+ m_view->renderer()->textWidth(c, cXPos);
+
+ updateSelection( c, sel );
+ updateCursor( c );
+}
+
+void KateViewInternal::cursorDown(bool sel)
+{
+ if (m_view->m_codeCompletion->codeCompletionVisible()) {
+ QKeyEvent e(QEvent::KeyPress, Qt::Key_Down, 0, 0);
+ m_view->m_codeCompletion->handleKey(&e);
+ return;
+ }
+
+ if ((displayCursor.line() >= (int)m_doc->numVisLines() - 1) && (!m_view->dynWordWrap() || viewLine(cursor) == lastViewLine(cursor.line())))
+ return;
+
+ int newLine = cursor.line(), newCol = 0, xOffset = 0, startCol = 0;
+ m_preserveMaxX = true;
+
+ if (m_view->dynWordWrap()) {
+ // Dynamic word wrapping - navigate on visual lines rather than real lines
+ KateLineRange thisRange = currentRange();
+ // This is not the last line because that is already simplified out above
+ KateLineRange nRange = nextRange();
+
+ // Ensure we're in the right spot
+ Q_ASSERT((cursor.line() == thisRange.line) &&
+ (cursor.col() >= thisRange.startCol) &&
+ (!thisRange.wrap || cursor.col() < thisRange.endCol));
+
+ // VisibleX is the distance from the start of the text to the cursor on the current line.
+ int visibleX = m_view->renderer()->textWidth(cursor) - thisRange.startX;
+ int currentLineVisibleX = visibleX;
+
+ // Translate to new line
+ visibleX += thisRange.xOffset();
+ visibleX -= nRange.xOffset();
+
+ // Limit to >= 0
+ visibleX = kMax(0, visibleX);
+
+ if (!thisRange.wrap) {
+ newLine = m_doc->getRealLine(displayCursor.line() + 1);
+ } else {
+ startCol = thisRange.endCol;
+ xOffset = thisRange.endX;
+ }
+
+ // Take into account current max X (ie. if the current line was smaller
+ // than the last definitely specified width)
+ if (thisRange.xOffset() && !nRange.xOffset() && currentLineVisibleX == 0) // Special case for where xOffset may be > m_currentMaxX
+ visibleX = m_currentMaxX;
+ else if (visibleX < m_currentMaxX - nRange.xOffset())
+ visibleX = m_currentMaxX - nRange.xOffset();
+
+ cXPos = xOffset + visibleX;
+
+ cXPos = kMin(cXPos, lineMaxCursorX(nRange));
+
+ newCol = kMin((int)m_view->renderer()->textPos(newLine, visibleX, startCol), lineMaxCol(nRange));
+
+ } else {
+ newLine = m_doc->getRealLine(displayCursor.line() + 1);
+
+ if ((m_view->wrapCursor()) && m_currentMaxX > cXPos)
+ cXPos = m_currentMaxX;
+ }
+
+ KateTextCursor c(newLine, newCol);
+ m_view->renderer()->textWidth(c, cXPos);
+
+ updateSelection(c, sel);
+ updateCursor(c);
+}
+
+void KateViewInternal::cursorToMatchingBracket( bool sel )
+{
+ KateTextCursor start( cursor ), end;
+
+ if( !m_doc->findMatchingBracket( start, end ) )
+ return;
+
+ // The cursor is now placed just to the left of the matching bracket.
+ // If it's an ending bracket, put it to the right (so we can easily
+ // get back to the original bracket).
+ if( end > start )
+ end.setCol(end.col() + 1);
+
+ updateSelection( end, sel );
+ updateCursor( end );
+}
+
+void KateViewInternal::topOfView( bool sel )
+{
+ KateTextCursor c = viewLineOffset(startPos(), m_minLinesVisible);
+ updateSelection( c, sel );
+ updateCursor( c );
+}
+
+void KateViewInternal::bottomOfView( bool sel )
+{
+ // FIXME account for wordwrap
+ KateTextCursor c = viewLineOffset(endPos(), -m_minLinesVisible);
+ updateSelection( c, sel );
+ updateCursor( c );
+}
+
+// lines is the offset to scroll by
+void KateViewInternal::scrollLines( int lines, bool sel )
+{
+ KateTextCursor c = viewLineOffset(displayCursor, lines, true);
+
+ // Fix the virtual cursor -> real cursor
+ c.setLine(m_doc->getRealLine(c.line()));
+
+ updateSelection( c, sel );
+ updateCursor( c );
+}
+
+// This is a bit misleading... it's asking for the view to be scrolled, not the cursor
+void KateViewInternal::scrollUp()
+{
+ KateTextCursor newPos = viewLineOffset(m_startPos, -1);
+ scrollPos(newPos);
+}
+
+void KateViewInternal::scrollDown()
+{
+ KateTextCursor newPos = viewLineOffset(m_startPos, 1);
+ scrollPos(newPos);
+}
+
+void KateViewInternal::setAutoCenterLines(int viewLines, bool updateView)
+{
+ m_autoCenterLines = viewLines;
+ m_minLinesVisible = kMin(int((linesDisplayed() - 1)/2), m_autoCenterLines);
+ if (updateView)
+ KateViewInternal::updateView();
+}
+
+void KateViewInternal::pageUp( bool sel )
+{
+ if (m_view->m_codeCompletion->codeCompletionVisible()) {
+ QKeyEvent e(QEvent::KeyPress, Qt::Key_PageUp, 0, 0);
+ m_view->m_codeCompletion->handleKey(&e);
+ return;
+ }
+
+ // remember the view line and x pos
+ int viewLine = displayViewLine(displayCursor);
+ bool atTop = (startPos().line() == 0 && startPos().col() == 0);
+
+ // Adjust for an auto-centering cursor
+ int lineadj = 2 * m_minLinesVisible;
+ int cursorStart = (linesDisplayed() - 1) - viewLine;
+ if (cursorStart < m_minLinesVisible)
+ lineadj -= m_minLinesVisible - cursorStart;
+
+ int linesToScroll = -kMax( ((int)linesDisplayed() - 1) - lineadj, 0 );
+ m_preserveMaxX = true;
+
+ if (!m_doc->pageUpDownMovesCursor () && !atTop) {
+ int xPos = m_view->renderer()->textWidth(cursor) - currentRange().startX;
+
+ KateTextCursor newStartPos = viewLineOffset(startPos(), linesToScroll - 1);
+ scrollPos(newStartPos);
+
+ // put the cursor back approximately where it was
+ KateTextCursor newPos = viewLineOffset(newStartPos, viewLine, true);
+ newPos.setLine(m_doc->getRealLine(newPos.line()));
+
+ KateLineRange newLine = range(newPos);
+
+ if (m_currentMaxX - newLine.xOffset() > xPos)
+ xPos = m_currentMaxX - newLine.xOffset();
+
+ cXPos = kMin(newLine.startX + xPos, lineMaxCursorX(newLine));
+
+ m_view->renderer()->textWidth( newPos, cXPos );
+
+ m_preserveMaxX = true;
+ updateSelection( newPos, sel );
+ updateCursor(newPos);
+
+ } else {
+ scrollLines( linesToScroll, sel );
+ }
+}
+
+void KateViewInternal::pageDown( bool sel )
+{
+ if (m_view->m_codeCompletion->codeCompletionVisible()) {
+ QKeyEvent e(QEvent::KeyPress, Qt::Key_PageDown, 0, 0);
+ m_view->m_codeCompletion->handleKey(&e);
+ return;
+ }
+
+ // remember the view line
+ int viewLine = displayViewLine(displayCursor);
+ bool atEnd = startPos() >= m_cachedMaxStartPos;
+
+ // Adjust for an auto-centering cursor
+ int lineadj = 2 * m_minLinesVisible;
+ int cursorStart = m_minLinesVisible - viewLine;
+ if (cursorStart > 0)
+ lineadj -= cursorStart;
+
+ int linesToScroll = kMax( ((int)linesDisplayed() - 1) - lineadj, 0 );
+ m_preserveMaxX = true;
+
+ if (!m_doc->pageUpDownMovesCursor () && !atEnd) {
+ int xPos = m_view->renderer()->textWidth(cursor) - currentRange().startX;
+
+ KateTextCursor newStartPos = viewLineOffset(startPos(), linesToScroll + 1);
+ scrollPos(newStartPos);
+
+ // put the cursor back approximately where it was
+ KateTextCursor newPos = viewLineOffset(newStartPos, viewLine, true);
+ newPos.setLine(m_doc->getRealLine(newPos.line()));
+
+ KateLineRange newLine = range(newPos);
+
+ if (m_currentMaxX - newLine.xOffset() > xPos)
+ xPos = m_currentMaxX - newLine.xOffset();
+
+ cXPos = kMin(newLine.startX + xPos, lineMaxCursorX(newLine));
+
+ m_view->renderer()->textWidth( newPos, cXPos );
+
+ m_preserveMaxX = true;
+ updateSelection( newPos, sel );
+ updateCursor(newPos);
+
+ } else {
+ scrollLines( linesToScroll, sel );
+ }
+}
+
+int KateViewInternal::maxLen(uint startLine)
+{
+// Q_ASSERT(!m_view->dynWordWrap());
+
+ int displayLines = (m_view->height() / m_view->renderer()->fontHeight()) + 1;
+
+ int maxLen = 0;
+
+ for (int z = 0; z < displayLines; z++) {
+ int virtualLine = startLine + z;
+
+ if (virtualLine < 0 || virtualLine >= (int)m_doc->visibleLines())
+ break;
+
+ KateLineRange thisRange = range((int)m_doc->getRealLine(virtualLine));
+
+ maxLen = kMax(maxLen, thisRange.endX);
+ }
+
+ return maxLen;
+}
+
+void KateViewInternal::top( bool sel )
+{
+ KateTextCursor c( 0, cursor.col() );
+ m_view->renderer()->textWidth( c, cXPos );
+ updateSelection( c, sel );
+ updateCursor( c );
+}
+
+void KateViewInternal::bottom( bool sel )
+{
+ KateTextCursor c( m_doc->lastLine(), cursor.col() );
+ m_view->renderer()->textWidth( c, cXPos );
+ updateSelection( c, sel );
+ updateCursor( c );
+}
+
+void KateViewInternal::top_home( bool sel )
+{
+ if (m_view->m_codeCompletion->codeCompletionVisible()) {
+ QKeyEvent e(QEvent::KeyPress, Qt::Key_Home, 0, 0);
+ m_view->m_codeCompletion->handleKey(&e);
+ return;
+ }
+ KateTextCursor c( 0, 0 );
+ updateSelection( c, sel );
+ updateCursor( c );
+}
+
+void KateViewInternal::bottom_end( bool sel )
+{
+ if (m_view->m_codeCompletion->codeCompletionVisible()) {
+ QKeyEvent e(QEvent::KeyPress, Qt::Key_End, 0, 0);
+ m_view->m_codeCompletion->handleKey(&e);
+ return;
+ }
+ KateTextCursor c( m_doc->lastLine(), m_doc->lineLength( m_doc->lastLine() ) );
+ updateSelection( c, sel );
+ updateCursor( c );
+}
+
+void KateViewInternal::updateSelection( const KateTextCursor& _newCursor, bool keepSel )
+{
+ KateTextCursor newCursor = _newCursor;
+ if( keepSel )
+ {
+ if ( !m_view->hasSelection() || (selectAnchor.line() == -1)
+ || (m_view->config()->persistentSelection()
+ && ((cursor < m_view->selectStart) || (cursor > m_view->selectEnd))) )
+ {
+ selectAnchor = cursor;
+ m_view->setSelection( cursor, newCursor );
+ }
+ else
+ {
+ bool doSelect = true;
+ switch (m_selectionMode)
+ {
+ case Word:
+ {
+ // Restore selStartCached if needed. It gets nuked by
+ // viewSelectionChanged if we drag the selection into non-existence,
+ // which can legitimately happen if a shift+DC selection is unable to
+ // set a "proper" (i.e. non-empty) cached selection, e.g. because the
+ // start was on something that isn't a word. Word select mode relies
+ // on the cached selection being set properly, even if it is empty
+ // (i.e. selStartCached == selEndCached).
+ if ( selStartCached.line() == -1 )
+ selStartCached = selEndCached;
+
+ int c;
+ if ( newCursor > selEndCached )
+ {
+ selectAnchor = selStartCached;
+
+ KateTextLine::Ptr l = m_doc->kateTextLine( newCursor.line() );
+
+ c = newCursor.col();
+ if ( c > 0 && m_doc->highlight()->isInWord( l->getChar( c-1 ) ) ) {
+ for (; c < l->length(); c++ )
+ if ( !m_doc->highlight()->isInWord( l->getChar( c ) ) )
+ break;
+ }
+
+ newCursor.setCol( c );
+ }
+ else if ( newCursor < selStartCached )
+ {
+ selectAnchor = selEndCached;
+
+ KateTextLine::Ptr l = m_doc->kateTextLine( newCursor.line() );
+
+ c = newCursor.col();
+ if ( c > 0 && c < m_doc->textLine( newCursor.line() ).length()
+ && m_doc->highlight()->isInWord( l->getChar( c ) )
+ && m_doc->highlight()->isInWord( l->getChar( c-1 ) ) ) {
+ for ( c -= 2; c >= 0; c-- )
+ if ( !m_doc->highlight()->isInWord( l->getChar( c ) ) )
+ break;
+ newCursor.setCol( c+1 );
+ }
+
+ }
+ else
+ doSelect = false;
+
+ }
+ break;
+ case Line:
+ if ( newCursor.line() > selStartCached.line() )
+ {
+ if ( newCursor.line()+1 >= m_doc->numLines() )
+ newCursor.setCol( m_doc->textLine( newCursor.line() ).length() );
+ else
+ newCursor.setPos( newCursor.line() + 1, 0 );
+ // Grow to include entire line
+ selectAnchor = selStartCached;
+ selectAnchor.setCol( 0 );
+ }
+ else if ( newCursor.line() < selStartCached.line() )
+ {
+ newCursor.setCol( 0 );
+ // Grow to include entire line
+ selectAnchor = selEndCached;
+ if ( selectAnchor.col() > 0 )
+ {
+ if ( selectAnchor.line()+1 >= m_doc->numLines() )
+ selectAnchor.setCol( m_doc->textLine( selectAnchor.line() ).length() );
+ else
+ selectAnchor.setPos( selectAnchor.line() + 1, 0 );
+ }
+ }
+ else // same line, ignore
+ doSelect = false;
+ break;
+ case Mouse:
+ {
+ if ( selStartCached.line() < 0 ) // invalid
+ break;
+
+ if ( newCursor > selEndCached )
+ selectAnchor = selStartCached;
+ else if ( newCursor < selStartCached )
+ selectAnchor = selEndCached;
+ else
+ doSelect = false;
+ }
+ break;
+ default:
+ {
+ if ( selectAnchor.line() < 0 ) // invalid
+ break;
+ }
+ }
+
+ if ( doSelect )
+ m_view->setSelection( selectAnchor, newCursor);
+ else if ( selStartCached.line() >= 0 ) // we have a cached selection, so we restore that
+ m_view->setSelection( selStartCached, selEndCached );
+ }
+
+ m_selChangedByUser = true;
+ }
+ else if ( !m_view->config()->persistentSelection() )
+ {
+ m_view->clearSelection();
+ selStartCached.setLine( -1 );
+ selectAnchor.setLine( -1 );
+ }
+}
+
+void KateViewInternal::updateCursor( const KateTextCursor& newCursor, bool force, bool center, bool calledExternally )
+{
+ if ( !force && (cursor == newCursor) )
+ {
+ if ( !m_madeVisible && m_view == m_doc->activeView() )
+ {
+ // unfold if required
+ m_doc->foldingTree()->ensureVisible( newCursor.line() );
+
+ makeVisible ( displayCursor, displayCursor.col(), false, center, calledExternally );
+ }
+
+ return;
+ }
+
+ // unfold if required
+ m_doc->foldingTree()->ensureVisible( newCursor.line() );
+
+ KateTextCursor oldDisplayCursor = displayCursor;
+
+ cursor.setPos (newCursor);
+ displayCursor.setPos (m_doc->getVirtualLine(cursor.line()), cursor.col());
+
+ cXPos = m_view->renderer()->textWidth( cursor );
+ if (m_view == m_doc->activeView())
+ makeVisible ( displayCursor, displayCursor.col(), false, center, calledExternally );
+
+ updateBracketMarks();
+
+ // It's efficient enough to just tag them both without checking to see if they're on the same view line
+ tagLine(oldDisplayCursor);
+ tagLine(displayCursor);
+
+ updateMicroFocusHint();
+
+ if (m_cursorTimer.isActive ())
+ {
+ if ( KApplication::cursorFlashTime() > 0 )
+ m_cursorTimer.start( KApplication::cursorFlashTime() / 2 );
+ m_view->renderer()->setDrawCaret(true);
+ }
+
+ // Remember the maximum X position if requested
+ if (m_preserveMaxX)
+ m_preserveMaxX = false;
+ else
+ if (m_view->dynWordWrap())
+ m_currentMaxX = m_view->renderer()->textWidth(displayCursor) - currentRange().startX + currentRange().xOffset();
+ else
+ m_currentMaxX = cXPos;
+
+ //kdDebug() << "m_currentMaxX: " << m_currentMaxX << " (was "<< oldmaxx << "), cXPos: " << cXPos << endl;
+ //kdDebug(13030) << "Cursor now located at real " << cursor.line << "," << cursor.col << ", virtual " << displayCursor.line << ", " << displayCursor.col << "; Top is " << startLine() << ", " << startPos().col << endl;
+
+ paintText(0, 0, width(), height(), true);
+
+ emit m_view->cursorPositionChanged();
+}
+
+void KateViewInternal::updateBracketMarks()
+{
+ if ( bm.isValid() ) {
+ KateTextCursor bmStart(m_doc->getVirtualLine(bm.start().line()), bm.start().col());
+ KateTextCursor bmEnd(m_doc->getVirtualLine(bm.end().line()), bm.end().col());
+
+ if( bm.getMinIndent() != 0 )
+ {
+ // @@ Do this only when cursor near start/end.
+ if( bmStart > bmEnd )
+ {
+ tagLines(bmEnd, bmStart);
+ }
+ else
+ {
+ tagLines(bmStart, bmEnd);
+ }
+ }
+ else
+ {
+ tagLine(bmStart);
+ tagLine(bmEnd);
+ }
+ }
+
+ // add some limit to this, this is really endless on big files without limit
+ int maxLines = linesDisplayed () * 3;
+ m_doc->newBracketMark( cursor, bm, maxLines );
+
+ if ( bm.isValid() ) {
+ KateTextCursor bmStart(m_doc->getVirtualLine(bm.start().line()), bm.start().col());
+ KateTextCursor bmEnd(m_doc->getVirtualLine(bm.end().line()), bm.end().col());
+
+ if( bm.getMinIndent() != 0 )
+ {
+ // @@ Do this only when cursor near start/end.
+ if( bmStart > bmEnd )
+ {
+ tagLines(bmEnd, bmStart);
+ }
+ else
+ {
+ tagLines(bmStart, bmEnd);
+ }
+ }
+ else
+ {
+ tagLine(bmStart);
+ tagLine(bmEnd);
+ }
+ }
+}
+
+bool KateViewInternal::tagLine(const KateTextCursor& virtualCursor)
+{
+ int viewLine = displayViewLine(virtualCursor, true);
+ if (viewLine >= 0 && viewLine < (int)lineRanges.count()) {
+ lineRanges[viewLine].dirty = true;
+ leftBorder->update (0, lineToY(viewLine), leftBorder->width(), m_view->renderer()->fontHeight());
+ return true;
+ }
+ return false;
+}
+
+bool KateViewInternal::tagLines( int start, int end, bool realLines )
+{
+ return tagLines(KateTextCursor(start, 0), KateTextCursor(end, -1), realLines);
+}
+
+bool KateViewInternal::tagLines(KateTextCursor start, KateTextCursor end, bool realCursors)
+{
+ if (realCursors)
+ {
+ //kdDebug()<<"realLines is true"<<endl;
+ start.setLine(m_doc->getVirtualLine( start.line() ));
+ end.setLine(m_doc->getVirtualLine( end.line() ));
+ }
+
+ if (end.line() < (int)startLine())
+ {
+ //kdDebug()<<"end<startLine"<<endl;
+ return false;
+ }
+ if (start.line() > (int)endLine())
+ {
+ //kdDebug()<<"start> endLine"<<start<<" "<<((int)endLine())<<endl;
+ return false;
+ }
+
+ //kdDebug(13030) << "tagLines( [" << start.line << "," << start.col << "], [" << end.line << "," << end.col << "] )\n";
+
+ bool ret = false;
+
+ for (uint z = 0; z < lineRanges.size(); z++)
+ {
+ if ((lineRanges[z].virtualLine > start.line() || (lineRanges[z].virtualLine == start.line() && lineRanges[z].endCol >= start.col() && start.col() != -1)) && (lineRanges[z].virtualLine < end.line() || (lineRanges[z].virtualLine == end.line() && (lineRanges[z].startCol <= end.col() || end.col() == -1)))) {
+ ret = lineRanges[z].dirty = true;
+ //kdDebug() << "Tagged line " << lineRanges[z].line << endl;
+ }
+ }
+
+ if (!m_view->dynWordWrap())
+ {
+ int y = lineToY( start.line() );
+ // FIXME is this enough for when multiple lines are deleted
+ int h = (end.line() - start.line() + 2) * m_view->renderer()->fontHeight();
+ if (end.line() == (int)m_doc->numVisLines() - 1)
+ h = height();
+
+ leftBorder->update (0, y, leftBorder->width(), h);
+ }
+ else
+ {
+ // FIXME Do we get enough good info in editRemoveText to optimise this more?
+ //bool justTagged = false;
+ for (uint z = 0; z < lineRanges.size(); z++)
+ {
+ if ((lineRanges[z].virtualLine > start.line() || (lineRanges[z].virtualLine == start.line() && lineRanges[z].endCol >= start.col() && start.col() != -1)) && (lineRanges[z].virtualLine < end.line() || (lineRanges[z].virtualLine == end.line() && (lineRanges[z].startCol <= end.col() || end.col() == -1))))
+ {
+ //justTagged = true;
+ leftBorder->update (0, z * m_view->renderer()->fontHeight(), leftBorder->width(), leftBorder->height());
+ break;
+ }
+ /*else if (justTagged)
+ {
+ justTagged = false;
+ leftBorder->update (0, z * m_doc->viewFont.fontHeight, leftBorder->width(), m_doc->viewFont.fontHeight);
+ break;
+ }*/
+ }
+ }
+
+ return ret;
+}
+
+void KateViewInternal::tagAll()
+{
+ //kdDebug(13030) << "tagAll()" << endl;
+ for (uint z = 0; z < lineRanges.size(); z++)
+ {
+ lineRanges[z].dirty = true;
+ }
+
+ leftBorder->updateFont();
+ leftBorder->update ();
+}
+
+void KateViewInternal::paintCursor()
+{
+ if (tagLine(displayCursor))
+ paintText (0,0,width(), height(), true);
+}
+
+// Point in content coordinates
+void KateViewInternal::placeCursor( const QPoint& p, bool keepSelection, bool updateSelection )
+{
+ KateLineRange thisRange = yToKateLineRange(p.y());
+
+ if (thisRange.line == -1) {
+ for (int i = (p.y() / m_view->renderer()->fontHeight()); i >= 0; i--) {
+ thisRange = lineRanges[i];
+ if (thisRange.line != -1)
+ break;
+ }
+ Q_ASSERT(thisRange.line != -1);
+ }
+
+ int realLine = thisRange.line;
+ int visibleLine = thisRange.virtualLine;
+ uint startCol = thisRange.startCol;
+
+ visibleLine = kMax( 0, kMin( visibleLine, int(m_doc->numVisLines()) - 1 ) );
+
+ KateTextCursor c(realLine, 0);
+
+ int x = kMin(kMax(-m_startX, p.x() - thisRange.xOffset()), lineMaxCursorX(thisRange) - thisRange.startX);
+
+ m_view->renderer()->textWidth( c, startX() + x, startCol);
+
+ if (updateSelection)
+ KateViewInternal::updateSelection( c, keepSelection );
+
+ updateCursor( c );
+}
+
+// Point in content coordinates
+bool KateViewInternal::isTargetSelected( const QPoint& p )
+{
+ KateLineRange thisRange = yToKateLineRange(p.y());
+
+ KateTextLine::Ptr l = textLine( thisRange.line );
+ if( !l )
+ return false;
+
+ int col = m_view->renderer()->textPos( l, startX() + p.x() - thisRange.xOffset(), thisRange.startCol, false );
+
+ return m_view->lineColSelected( thisRange.line, col );
+}
+
+//BEGIN EVENT HANDLING STUFF
+
+bool KateViewInternal::eventFilter( QObject *obj, QEvent *e )
+{
+ if (obj == m_lineScroll)
+ {
+ // the second condition is to make sure a scroll on the vertical bar doesn't cause a horizontal scroll ;)
+ if (e->type() == QEvent::Wheel && m_lineScroll->minValue() != m_lineScroll->maxValue())
+ {
+ wheelEvent((QWheelEvent*)e);
+ return true;
+ }
+
+ // continue processing
+ return QWidget::eventFilter( obj, e );
+ }
+
+ switch( e->type() )
+ {
+ case QEvent::KeyPress:
+ {
+ QKeyEvent *k = (QKeyEvent *)e;
+
+ if (m_view->m_codeCompletion->codeCompletionVisible ())
+ {
+ kdDebug (13030) << "hint around" << endl;
+
+ if( k->key() == Key_Escape )
+ m_view->m_codeCompletion->abortCompletion();
+ }
+
+ if ((k->key() == Qt::Key_Escape) && !m_view->config()->persistentSelection() )
+ {
+ m_view->clearSelection();
+ return true;
+ }
+ else if ( !((k->state() & ControlButton) || (k->state() & AltButton)) )
+ {
+ keyPressEvent( k );
+ return k->isAccepted();
+ }
+
+ } break;
+
+ case QEvent::DragMove:
+ {
+ QPoint currentPoint = ((QDragMoveEvent*) e)->pos();
+
+ QRect doNotScrollRegion( scrollMargin, scrollMargin,
+ width() - scrollMargin * 2,
+ height() - scrollMargin * 2 );
+
+ if ( !doNotScrollRegion.contains( currentPoint ) )
+ {
+ startDragScroll();
+ // Keep sending move events
+ ( (QDragMoveEvent*)e )->accept( QRect(0,0,0,0) );
+ }
+
+ dragMoveEvent((QDragMoveEvent*)e);
+ } break;
+
+ case QEvent::DragLeave:
+ // happens only when pressing ESC while dragging
+ stopDragScroll();
+ break;
+
+ case QEvent::WindowBlocked:
+ // next focus originates from an internal dialog:
+ // don't show the modonhd prompt
+ m_doc->m_isasking = -1;
+ break;
+
+ default:
+ break;
+ }
+
+ return QWidget::eventFilter( obj, e );
+}
+
+void KateViewInternal::keyPressEvent( QKeyEvent* e )
+{
+ KKey key(e);
+
+ bool codeComp = m_view->m_codeCompletion->codeCompletionVisible ();
+
+ if (codeComp)
+ {
+ kdDebug (13030) << "hint around" << endl;
+
+ if( e->key() == Key_Enter || e->key() == Key_Return ||
+ (key == SHIFT + Qt::Key_Return) || (key == SHIFT + Qt::Key_Enter)) {
+ m_view->m_codeCompletion->doComplete();
+ e->accept();
+ return;
+ }
+ }
+
+ if( !m_doc->isReadWrite() )
+ {
+ e->ignore();
+ return;
+ }
+
+ if ((key == Qt::Key_Return) || (key == Qt::Key_Enter))
+ {
+ m_view->keyReturn();
+ e->accept();
+ return;
+ }
+
+ if ((key == SHIFT + Qt::Key_Return) || (key == SHIFT + Qt::Key_Enter))
+ {
+ uint ln = cursor.line();
+ int col = cursor.col();
+ KateTextLine::Ptr line = m_doc->kateTextLine( ln );
+ int pos = line->firstChar();
+ if (pos > cursor.col()) pos = cursor.col();
+ if (pos != -1) {
+ while ((int)line->length() > pos &&
+ !line->getChar(pos).isLetterOrNumber() &&
+ pos < cursor.col()) ++pos;
+ } else {
+ pos = line->length(); // stay indented
+ }
+ m_doc->editStart();
+ m_doc->insertText( cursor.line(), line->length(), "\n" + line->string(0, pos)
+ + line->string().right( line->length() - cursor.col() ) );
+ cursor.setPos(ln + 1, pos);
+ if (col < int(line->length()))
+ m_doc->editRemoveText(ln, col, line->length() - col);
+ m_doc->editEnd();
+ updateCursor(cursor, true);
+ updateView();
+ e->accept();
+
+ return;
+ }
+
+ if (key == Qt::Key_Backspace || key == SHIFT + Qt::Key_Backspace)
+ {
+ m_view->backspace();
+ e->accept();
+
+ if (codeComp)
+ m_view->m_codeCompletion->updateBox ();
+
+ return;
+ }
+
+ if (key == Qt::Key_Tab || key == SHIFT+Qt::Key_Backtab || key == Qt::Key_Backtab)
+ {
+ if (m_doc->invokeTabInterceptor(key)) {
+ e->accept();
+ return;
+ } else
+ if (m_doc->configFlags() & KateDocumentConfig::cfTabIndents)
+ {
+ if( key == Qt::Key_Tab )
+ {
+ if (m_view->hasSelection() || (m_doc->configFlags() & KateDocumentConfig::cfTabIndentsMode))
+ m_doc->indent( m_view, cursor.line(), 1 );
+ else if (m_doc->configFlags() & KateDocumentConfig::cfTabInsertsTab)
+ m_doc->typeChars ( m_view, QString ("\t") );
+ else
+ m_doc->insertIndentChars ( m_view );
+
+ e->accept();
+
+ if (codeComp)
+ m_view->m_codeCompletion->updateBox ();
+
+ return;
+ }
+
+ if (key == SHIFT+Qt::Key_Backtab || key == Qt::Key_Backtab)
+ {
+ m_doc->indent( m_view, cursor.line(), -1 );
+ e->accept();
+
+ if (codeComp)
+ m_view->m_codeCompletion->updateBox ();
+
+ return;
+ }
+ }
+}
+ if ( !(e->state() & ControlButton) && !(e->state() & AltButton)
+ && m_doc->typeChars ( m_view, e->text() ) )
+ {
+ e->accept();
+
+ if (codeComp)
+ m_view->m_codeCompletion->updateBox ();
+
+ return;
+ }
+
+ e->ignore();
+}
+
+void KateViewInternal::keyReleaseEvent( QKeyEvent* e )
+{
+ KKey key(e);
+
+ if (key == SHIFT)
+ m_shiftKeyPressed = true;
+ else
+ {
+ if (m_shiftKeyPressed)
+ {
+ m_shiftKeyPressed = false;
+
+ if (m_selChangedByUser)
+ {
+ QApplication::clipboard()->setSelectionMode( true );
+ m_view->copy();
+ QApplication::clipboard()->setSelectionMode( false );
+
+ m_selChangedByUser = false;
+ }
+ }
+ }
+
+ e->ignore();
+ return;
+}
+
+void KateViewInternal::contextMenuEvent ( QContextMenuEvent * e )
+{
+ // try to show popup menu
+
+ QPoint p = e->pos();
+
+ if ( m_view->m_doc->browserView() )
+ {
+ m_view->contextMenuEvent( e );
+ return;
+ }
+
+ if ( e->reason() == QContextMenuEvent::Keyboard )
+ {
+ makeVisible( cursor, 0 );
+ p = cursorCoordinates();
+ }
+ else if ( ! m_view->hasSelection() || m_view->config()->persistentSelection() )
+ placeCursor( e->pos() );
+
+ // popup is a qguardedptr now
+ if (m_view->popup()) {
+ m_view->popup()->popup( mapToGlobal( p ) );
+ e->accept ();
+ }
+}
+
+void KateViewInternal::mousePressEvent( QMouseEvent* e )
+{
+ switch (e->button())
+ {
+ case LeftButton:
+ m_selChangedByUser = false;
+
+ if (possibleTripleClick)
+ {
+ possibleTripleClick = false;
+
+ m_selectionMode = Line;
+
+ if ( e->state() & Qt::ShiftButton )
+ {
+ updateSelection( cursor, true );
+ }
+ else
+ {
+ m_view->selectLine( cursor );
+ }
+
+ QApplication::clipboard()->setSelectionMode( true );
+ m_view->copy();
+ QApplication::clipboard()->setSelectionMode( false );
+
+ // Keep the line at the select anchor selected during further
+ // mouse selection
+ if ( selectAnchor.line() > m_view->selectStart.line() )
+ {
+ // Preserve the last selected line
+ if ( selectAnchor == m_view->selectEnd && selectAnchor.col() == 0 )
+ selStartCached = KateTextCursor( selectAnchor.line()-1, 0 );
+ else
+ selStartCached = KateTextCursor( selectAnchor.line(), 0 );
+ selEndCached = m_view->selectEnd;
+ }
+ else
+ {
+ // Preserve the first selected line
+ selStartCached = m_view->selectStart;
+ if ( m_view->selectEnd.line() > m_view->selectStart.line() )
+ selEndCached = KateTextCursor( m_view->selectStart.line()+1, 0 );
+ else
+ selEndCached = m_view->selectEnd;
+ }
+
+ // Set cursor to edge of selection... which edge depends on what
+ // "direction" the selection was made in
+ if ( m_view->selectStart < selectAnchor
+ && selectAnchor.line() != m_view->selectStart.line() )
+ updateCursor( m_view->selectStart );
+ else
+ updateCursor( m_view->selectEnd );
+
+ e->accept ();
+ return;
+ }
+ else if (m_selectionMode == Default)
+ {
+ m_selectionMode = Mouse;
+ }
+
+ if ( e->state() & Qt::ShiftButton )
+ {
+ if (selectAnchor.line() < 0)
+ selectAnchor = cursor;
+ }
+ else
+ {
+ selStartCached.setLine( -1 ); // invalidate
+ }
+
+ if( !( e->state() & Qt::ShiftButton ) && isTargetSelected( e->pos() ) )
+ {
+ dragInfo.state = diPending;
+ dragInfo.start = e->pos();
+ }
+ else
+ {
+ dragInfo.state = diNone;
+
+ if ( e->state() & Qt::ShiftButton )
+ {
+ placeCursor( e->pos(), true, false );
+ if ( selStartCached.line() >= 0 )
+ {
+ if ( cursor > selEndCached )
+ {
+ m_view->setSelection( selStartCached, cursor );
+ selectAnchor = selStartCached;
+ }
+ else if ( cursor < selStartCached )
+ {
+ m_view->setSelection( cursor, selEndCached );
+ selectAnchor = selEndCached;
+ }
+ else
+ {
+ m_view->setSelection( selStartCached, cursor );
+ }
+ }
+ else
+ {
+ m_view->setSelection( selectAnchor, cursor );
+ }
+ }
+ else
+ {
+ placeCursor( e->pos() );
+ }
+
+ scrollX = 0;
+ scrollY = 0;
+
+ m_scrollTimer.start (50);
+ }
+
+ e->accept ();
+ break;
+
+ default:
+ e->ignore ();
+ break;
+ }
+}
+
+void KateViewInternal::mouseDoubleClickEvent(QMouseEvent *e)
+{
+ switch (e->button())
+ {
+ case LeftButton:
+ m_selectionMode = Word;
+
+ if ( e->state() & Qt::ShiftButton )
+ {
+ KateTextCursor oldSelectStart = m_view->selectStart;
+ KateTextCursor oldSelectEnd = m_view->selectEnd;
+
+ // Now select the word under the select anchor
+ int cs, ce;
+ KateTextLine::Ptr l = m_doc->kateTextLine( selectAnchor.line() );
+
+ ce = selectAnchor.col();
+ if ( ce > 0 && m_doc->highlight()->isInWord( l->getChar( ce ) ) ) {
+ for (; ce < l->length(); ce++ )
+ if ( !m_doc->highlight()->isInWord( l->getChar( ce ) ) )
+ break;
+ }
+
+ cs = selectAnchor.col() - 1;
+ if ( cs < m_doc->textLine( selectAnchor.line() ).length()
+ && m_doc->highlight()->isInWord( l->getChar( cs ) ) ) {
+ for ( cs--; cs >= 0; cs-- )
+ if ( !m_doc->highlight()->isInWord( l->getChar( cs ) ) )
+ break;
+ }
+
+ // ...and keep it selected
+ if (cs+1 < ce)
+ {
+ selStartCached = KateTextCursor( selectAnchor.line(), cs+1 );
+ selEndCached = KateTextCursor( selectAnchor.line(), ce );
+ }
+ else
+ {
+ selStartCached = selectAnchor;
+ selEndCached = selectAnchor;
+ }
+ // Now word select to the mouse cursor
+ placeCursor( e->pos(), true );
+ }
+ else
+ {
+ // first clear the selection, otherwise we run into bug #106402
+ // ...and set the cursor position, for the same reason (otherwise there
+ // are *other* idiosyncrasies we can't fix without reintroducing said
+ // bug)
+ // Parameters: 1st false: don't redraw
+ // 2nd false: don't emit selectionChanged signals, as
+ // selectWord() emits this already
+ m_view->clearSelection( false, false );
+ placeCursor( e->pos() );
+ m_view->selectWord( cursor );
+ if (m_view->hasSelection())
+ {
+ selectAnchor = selStartCached = m_view->selectStart;
+ selEndCached = m_view->selectEnd;
+ }
+ else
+ {
+ // if we didn't actually select anything, restore the selection mode
+ // -- see bug #131369 (kling)
+ m_selectionMode = Default;
+ }
+ }
+
+ // Move cursor to end (or beginning) of selected word
+ if (m_view->hasSelection())
+ {
+ QApplication::clipboard()->setSelectionMode( true );
+ m_view->copy();
+ QApplication::clipboard()->setSelectionMode( false );
+
+ // Shift+DC before the "cached" word should move the cursor to the
+ // beginning of the selection, not the end
+ if (m_view->selectStart < selStartCached)
+ updateCursor( m_view->selectStart );
+ else
+ updateCursor( m_view->selectEnd );
+ }
+
+ possibleTripleClick = true;
+ QTimer::singleShot ( QApplication::doubleClickInterval(), this, SLOT(tripleClickTimeout()) );
+
+ scrollX = 0;
+ scrollY = 0;
+
+ m_scrollTimer.start (50);
+
+ e->accept ();
+ break;
+
+ default:
+ e->ignore ();
+ break;
+ }
+}
+
+void KateViewInternal::tripleClickTimeout()
+{
+ possibleTripleClick = false;
+}
+
+void KateViewInternal::mouseReleaseEvent( QMouseEvent* e )
+{
+ switch (e->button())
+ {
+ case LeftButton:
+ m_selectionMode = Default;
+// selStartCached.setLine( -1 );
+
+ if (m_selChangedByUser)
+ {
+ QApplication::clipboard()->setSelectionMode( true );
+ m_view->copy();
+ QApplication::clipboard()->setSelectionMode( false );
+ // Set cursor to edge of selection... which edge depends on what
+ // "direction" the selection was made in
+ if ( m_view->selectStart < selectAnchor )
+ updateCursor( m_view->selectStart );
+ else
+ updateCursor( m_view->selectEnd );
+
+ m_selChangedByUser = false;
+ }
+
+ if (dragInfo.state == diPending)
+ placeCursor( e->pos(), e->state() & ShiftButton );
+ else if (dragInfo.state == diNone)
+ m_scrollTimer.stop ();
+
+ dragInfo.state = diNone;
+
+ e->accept ();
+ break;
+
+ case MidButton:
+ placeCursor( e->pos() );
+
+ if( m_doc->isReadWrite() )
+ {
+ QApplication::clipboard()->setSelectionMode( true );
+ m_view->paste ();
+ QApplication::clipboard()->setSelectionMode( false );
+ }
+
+ e->accept ();
+ break;
+
+ default:
+ e->ignore ();
+ break;
+ }
+}
+
+void KateViewInternal::mouseMoveEvent( QMouseEvent* e )
+{
+ if( e->state() & LeftButton )
+ {
+ if (dragInfo.state == diPending)
+ {
+ // we had a mouse down, but haven't confirmed a drag yet
+ // if the mouse has moved sufficiently, we will confirm
+ QPoint p( e->pos() - dragInfo.start );
+
+ // we've left the drag square, we can start a real drag operation now
+ if( p.manhattanLength() > KGlobalSettings::dndEventDelay() )
+ doDrag();
+
+ return;
+ }
+ else if (dragInfo.state == diDragging)
+ {
+ // Don't do anything after a canceled drag until the user lets go of
+ // the mouse button!
+ return;
+ }
+
+ mouseX = e->x();
+ mouseY = e->y();
+
+ scrollX = 0;
+ scrollY = 0;
+ int d = m_view->renderer()->fontHeight();
+
+ if (mouseX < 0)
+ scrollX = -d;
+
+ if (mouseX > width())
+ scrollX = d;
+
+ if (mouseY < 0)
+ {
+ mouseY = 0;
+ scrollY = -d;
+ }
+
+ if (mouseY > height())
+ {
+ mouseY = height();
+ scrollY = d;
+ }
+
+ placeCursor( QPoint( mouseX, mouseY ), true );
+
+ }
+ else
+ {
+ if (isTargetSelected( e->pos() ) ) {
+ // mouse is over selected text. indicate that the text is draggable by setting
+ // the arrow cursor as other Qt text editing widgets do
+ if (m_mouseCursor != ArrowCursor) {
+ setCursor( KCursor::arrowCursor() );
+ m_mouseCursor = ArrowCursor;
+ }
+ } else {
+ // normal text cursor
+ if (m_mouseCursor != IbeamCursor) {
+ setCursor( KCursor::ibeamCursor() );
+ m_mouseCursor = IbeamCursor;
+ }
+ }
+
+ if (m_textHintEnabled)
+ {
+ m_textHintTimer.start(m_textHintTimeout);
+ m_textHintMouseX=e->x();
+ m_textHintMouseY=e->y();
+ }
+ }
+}
+
+void KateViewInternal::paintEvent(QPaintEvent *e)
+{
+ paintText(e->rect().x(), e->rect().y(), e->rect().width(), e->rect().height());
+}
+
+void KateViewInternal::resizeEvent(QResizeEvent* e)
+{
+ bool expandedHorizontally = width() > e->oldSize().width();
+ bool expandedVertically = height() > e->oldSize().height();
+ bool heightChanged = height() != e->oldSize().height();
+
+ m_madeVisible = false;
+
+ if (heightChanged) {
+ setAutoCenterLines(m_autoCenterLines, false);
+ m_cachedMaxStartPos.setPos(-1, -1);
+ }
+
+ if (m_view->dynWordWrap()) {
+ bool dirtied = false;
+
+ for (uint i = 0; i < lineRanges.count(); i++) {
+ // find the first dirty line
+ // the word wrap updateView algorithm is forced to check all lines after a dirty one
+ if (lineRanges[i].wrap ||
+ (!expandedHorizontally && (lineRanges[i].endX - lineRanges[i].startX) > width())) {
+ dirtied = lineRanges[i].dirty = true;
+ break;
+ }
+ }
+
+ if (dirtied || heightChanged) {
+ updateView(true);
+ leftBorder->update();
+ }
+
+ if (width() < e->oldSize().width()) {
+ if (!m_view->wrapCursor()) {
+ // May have to restrain cursor to new smaller width...
+ if (cursor.col() > m_doc->lineLength(cursor.line())) {
+ KateLineRange thisRange = currentRange();
+
+ KateTextCursor newCursor(cursor.line(), thisRange.endCol + ((width() - thisRange.xOffset() - (thisRange.endX - thisRange.startX)) / m_view->renderer()->spaceWidth()) - 1);
+ updateCursor(newCursor);
+ }
+ }
+ }
+
+ } else {
+ updateView();
+
+ if (expandedHorizontally && startX() > 0)
+ scrollColumns(startX() - (width() - e->oldSize().width()));
+ }
+
+ if (expandedVertically) {
+ KateTextCursor max = maxStartPos();
+ if (startPos() > max)
+ scrollPos(max);
+ }
+}
+
+void KateViewInternal::scrollTimeout ()
+{
+ if (scrollX || scrollY)
+ {
+ scrollLines (startPos().line() + (scrollY / (int)m_view->renderer()->fontHeight()));
+ placeCursor( QPoint( mouseX, mouseY ), true );
+ }
+}
+
+void KateViewInternal::cursorTimeout ()
+{
+ m_view->renderer()->setDrawCaret(!m_view->renderer()->drawCaret());
+ paintCursor();
+}
+
+void KateViewInternal::textHintTimeout ()
+{
+ m_textHintTimer.stop ();
+
+ KateLineRange thisRange = yToKateLineRange(m_textHintMouseY);
+
+ if (thisRange.line == -1) return;
+
+ if (m_textHintMouseX> (lineMaxCursorX(thisRange) - thisRange.startX)) return;
+
+ int realLine = thisRange.line;
+ int startCol = thisRange.startCol;
+
+ KateTextCursor c(realLine, 0);
+ m_view->renderer()->textWidth( c, startX() + m_textHintMouseX, startCol);
+
+ QString tmp;
+
+ emit m_view->needTextHint(c.line(), c.col(), tmp);
+
+ if (!tmp.isEmpty()) kdDebug(13030)<<"Hint text: "<<tmp<<endl;
+}
+
+void KateViewInternal::focusInEvent (QFocusEvent *)
+{
+ if (KApplication::cursorFlashTime() > 0)
+ m_cursorTimer.start ( KApplication::cursorFlashTime() / 2 );
+
+ if (m_textHintEnabled)
+ m_textHintTimer.start( m_textHintTimeout );
+
+ paintCursor();
+
+ m_doc->setActiveView( m_view );
+
+ emit m_view->gotFocus( m_view );
+}
+
+void KateViewInternal::focusOutEvent (QFocusEvent *)
+{
+ if( m_view->renderer() && ! m_view->m_codeCompletion->codeCompletionVisible() )
+ {
+ m_cursorTimer.stop();
+
+ m_view->renderer()->setDrawCaret(true);
+ paintCursor();
+ emit m_view->lostFocus( m_view );
+ }
+
+ m_textHintTimer.stop();
+}
+
+void KateViewInternal::doDrag()
+{
+ dragInfo.state = diDragging;
+ dragInfo.dragObject = new QTextDrag(m_view->selection(), this);
+ dragInfo.dragObject->drag();
+}
+
+void KateViewInternal::dragEnterEvent( QDragEnterEvent* event )
+{
+ event->accept( (QTextDrag::canDecode(event) && m_doc->isReadWrite()) ||
+ KURLDrag::canDecode(event) );
+}
+
+void KateViewInternal::dragMoveEvent( QDragMoveEvent* event )
+{
+ // track the cursor to the current drop location
+ placeCursor( event->pos(), true, false );
+
+ // important: accept action to switch between copy and move mode
+ // without this, the text will always be copied.
+ event->acceptAction();
+}
+
+void KateViewInternal::dropEvent( QDropEvent* event )
+{
+ if ( KURLDrag::canDecode(event) ) {
+
+ emit dropEventPass(event);
+
+ } else if ( QTextDrag::canDecode(event) && m_doc->isReadWrite() ) {
+
+ QString text;
+
+ if (!QTextDrag::decode(event, text))
+ return;
+
+ // is the source our own document?
+ bool priv = false;
+ if (event->source() && event->source()->inherits("KateViewInternal"))
+ priv = m_doc->ownedView( ((KateViewInternal*)(event->source()))->m_view );
+
+ // dropped on a text selection area?
+ bool selected = isTargetSelected( event->pos() );
+
+ if( priv && selected ) {
+ // this is a drag that we started and dropped on our selection
+ // ignore this case
+ return;
+ }
+
+ // use one transaction
+ m_doc->editStart ();
+
+ // on move: remove selected text; on copy: duplicate text
+ if ( event->action() != QDropEvent::Copy )
+ m_view->removeSelectedText();
+
+ m_doc->insertText( cursor.line(), cursor.col(), text );
+
+ m_doc->editEnd ();
+
+ placeCursor( event->pos() );
+
+ event->acceptAction();
+ updateView();
+ }
+
+ // finally finish drag and drop mode
+ dragInfo.state = diNone;
+ // important, because the eventFilter`s DragLeave does not occur
+ stopDragScroll();
+}
+//END EVENT HANDLING STUFF
+
+void KateViewInternal::clear()
+{
+ cursor.setPos(0, 0);
+ displayCursor.setPos(0, 0);
+}
+
+void KateViewInternal::wheelEvent(QWheelEvent* e)
+{
+ if (m_lineScroll->minValue() != m_lineScroll->maxValue() && e->orientation() != Qt::Horizontal) {
+ // React to this as a vertical event
+ if ( ( e->state() & ControlButton ) || ( e->state() & ShiftButton ) ) {
+ if (e->delta() > 0)
+ scrollPrevPage();
+ else
+ scrollNextPage();
+ } else {
+ scrollViewLines(-((e->delta() / 120) * QApplication::wheelScrollLines()));
+ // maybe a menu was opened or a bubbled window title is on us -> we shall erase it
+ update();
+ leftBorder->update();
+ }
+
+ } else if (columnScrollingPossible()) {
+ QWheelEvent copy = *e;
+ QApplication::sendEvent(m_columnScroll, &copy);
+
+ } else {
+ e->ignore();
+ }
+}
+
+void KateViewInternal::startDragScroll()
+{
+ if ( !m_dragScrollTimer.isActive() ) {
+ m_dragScrollTimer.start( scrollTime );
+ }
+}
+
+void KateViewInternal::stopDragScroll()
+{
+ m_dragScrollTimer.stop();
+ updateView();
+}
+
+void KateViewInternal::doDragScroll()
+{
+ QPoint p = this->mapFromGlobal( QCursor::pos() );
+
+ int dx = 0, dy = 0;
+ if ( p.y() < scrollMargin ) {
+ dy = p.y() - scrollMargin;
+ } else if ( p.y() > height() - scrollMargin ) {
+ dy = scrollMargin - (height() - p.y());
+ }
+
+ if ( p.x() < scrollMargin ) {
+ dx = p.x() - scrollMargin;
+ } else if ( p.x() > width() - scrollMargin ) {
+ dx = scrollMargin - (width() - p.x());
+ }
+
+ dy /= 4;
+
+ if (dy)
+ scrollLines(startPos().line() + dy);
+
+ if (columnScrollingPossible () && dx)
+ scrollColumns(kMin (m_startX + dx, m_columnScroll->maxValue()));
+
+ if (!dy && !dx)
+ stopDragScroll();
+}
+
+void KateViewInternal::enableTextHints(int timeout)
+{
+ m_textHintTimeout=timeout;
+ m_textHintEnabled=true;
+ m_textHintTimer.start(timeout);
+}
+
+void KateViewInternal::disableTextHints()
+{
+ m_textHintEnabled=false;
+ m_textHintTimer.stop ();
+}
+
+bool KateViewInternal::columnScrollingPossible ()
+{
+ return !m_view->dynWordWrap() && m_columnScroll->isEnabled() && (m_columnScroll->maxValue() > 0);
+}
+
+//BEGIN EDIT STUFF
+void KateViewInternal::editStart()
+{
+ editSessionNumber++;
+
+ if (editSessionNumber > 1)
+ return;
+
+ editIsRunning = true;
+ editOldCursor = cursor;
+}
+
+void KateViewInternal::editEnd(int editTagLineStart, int editTagLineEnd, bool tagFrom)
+{
+ if (editSessionNumber == 0)
+ return;
+
+ editSessionNumber--;
+
+ if (editSessionNumber > 0)
+ return;
+
+ if (tagFrom && (editTagLineStart <= int(m_doc->getRealLine(startLine()))))
+ tagAll();
+ else
+ tagLines (editTagLineStart, tagFrom ? m_doc->lastLine() : editTagLineEnd, true);
+
+ if (editOldCursor == cursor)
+ updateBracketMarks();
+
+ if (m_imPreeditLength <= 0)
+ updateView(true);
+
+ if ((editOldCursor != cursor) && (m_imPreeditLength <= 0))
+ {
+ m_madeVisible = false;
+ updateCursor ( cursor, true );
+ }
+ else if ( m_view == m_doc->activeView() )
+ {
+ makeVisible(displayCursor, displayCursor.col());
+ }
+
+ editIsRunning = false;
+}
+
+void KateViewInternal::editSetCursor (const KateTextCursor &cursor)
+{
+ if (this->cursor != cursor)
+ {
+ this->cursor.setPos (cursor);
+ }
+}
+//END
+
+void KateViewInternal::viewSelectionChanged ()
+{
+ if (!m_view->hasSelection())
+ {
+ selectAnchor.setPos (-1, -1);
+ selStartCached.setPos (-1, -1);
+ }
+}
+
+//BEGIN IM INPUT STUFF
+void KateViewInternal::imStartEvent( QIMEvent *e )
+{
+ if ( m_doc->m_bReadOnly ) {
+ e->ignore();
+ return;
+ }
+
+ if ( m_view->hasSelection() )
+ m_view->removeSelectedText();
+
+ m_imPreeditStartLine = cursor.line();
+ m_imPreeditStart = cursor.col();
+ m_imPreeditLength = 0;
+ m_imPreeditSelStart = m_imPreeditStart;
+
+ m_view->setIMSelectionValue( m_imPreeditStartLine, m_imPreeditStart, 0, 0, 0, true );
+}
+
+void KateViewInternal::imComposeEvent( QIMEvent *e )
+{
+ if ( m_doc->m_bReadOnly ) {
+ e->ignore();
+ return;
+ }
+
+ // remove old preedit
+ if ( m_imPreeditLength > 0 ) {
+ cursor.setPos( m_imPreeditStartLine, m_imPreeditStart );
+ m_doc->removeText( m_imPreeditStartLine, m_imPreeditStart,
+ m_imPreeditStartLine, m_imPreeditStart + m_imPreeditLength );
+ }
+
+ m_imPreeditLength = e->text().length();
+ m_imPreeditSelStart = m_imPreeditStart + e->cursorPos();
+
+ // update selection
+ m_view->setIMSelectionValue( m_imPreeditStartLine, m_imPreeditStart, m_imPreeditStart + m_imPreeditLength,
+ m_imPreeditSelStart, m_imPreeditSelStart + e->selectionLength(),
+ true );
+
+ // insert new preedit
+ m_doc->insertText( m_imPreeditStartLine, m_imPreeditStart, e->text() );
+
+
+ // update cursor
+ cursor.setPos( m_imPreeditStartLine, m_imPreeditSelStart );
+ updateCursor( cursor, true );
+
+ updateView( true );
+}
+
+void KateViewInternal::imEndEvent( QIMEvent *e )
+{
+ if ( m_doc->m_bReadOnly ) {
+ e->ignore();
+ return;
+ }
+
+ if ( m_imPreeditLength > 0 ) {
+ cursor.setPos( m_imPreeditStartLine, m_imPreeditStart );
+ m_doc->removeText( m_imPreeditStartLine, m_imPreeditStart,
+ m_imPreeditStartLine, m_imPreeditStart + m_imPreeditLength );
+ }
+
+ m_view->setIMSelectionValue( m_imPreeditStartLine, m_imPreeditStart, 0, 0, 0, false );
+
+ if ( e->text().length() > 0 ) {
+ m_doc->insertText( cursor.line(), cursor.col(), e->text() );
+
+ if ( !m_cursorTimer.isActive() && KApplication::cursorFlashTime() > 0 )
+ m_cursorTimer.start ( KApplication::cursorFlashTime() / 2 );
+
+ updateView( true );
+ updateCursor( cursor, true );
+ }
+
+ m_imPreeditStart = 0;
+ m_imPreeditLength = 0;
+ m_imPreeditSelStart = 0;
+}
+//END IM INPUT STUFF
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/kateviewinternal.h b/kate/part/kateviewinternal.h
new file mode 100644
index 000000000..c5004d6be
--- /dev/null
+++ b/kate/part/kateviewinternal.h
@@ -0,0 +1,397 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org>
+ Copyright (C) 2002 Joseph Wenninger <jowenn@kde.org>
+ Copyright (C) 2002 Christoph Cullmann <cullmann@kde.org>
+
+ Based on:
+ KWriteView : Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef _KATE_VIEW_INTERNAL_
+#define _KATE_VIEW_INTERNAL_
+
+#include "katecursor.h"
+#include "katesupercursor.h"
+#include "katelinerange.h"
+#include "katetextline.h"
+#include "katedocument.h"
+
+#include <qpoint.h>
+#include <qtimer.h>
+#include <qintdict.h>
+
+class KateView;
+class KateIconBorder;
+class KateScrollBar;
+
+class QHBoxLayout;
+class QVBoxLayout;
+class QScrollBar;
+
+enum Bias
+{
+ left = -1,
+ none = 0,
+ right = 1
+};
+
+class KateViewInternal : public QWidget
+{
+ Q_OBJECT
+
+ friend class KateView;
+ friend class KateIconBorder;
+ friend class KateScrollBar;
+ friend class CalculatingCursor;
+ friend class BoundedCursor;
+ friend class WrappingCursor;
+
+ public:
+ KateViewInternal ( KateView *view, KateDocument *doc );
+ ~KateViewInternal ();
+
+ //BEGIN EDIT STUFF
+ public:
+ void editStart ();
+ void editEnd (int editTagLineStart, int editTagLineEnd, bool tagFrom);
+
+ void editSetCursor (const KateTextCursor &cursor);
+
+ private:
+ uint editSessionNumber;
+ bool editIsRunning;
+ KateTextCursor editOldCursor;
+ //END
+
+ //BEGIN TAG & CLEAR & UPDATE STUFF
+ public:
+ bool tagLine (const KateTextCursor& virtualCursor);
+
+ bool tagLines (int start, int end, bool realLines = false);
+ bool tagLines (KateTextCursor start, KateTextCursor end, bool realCursors = false);
+
+ void tagAll ();
+
+ void clear ();
+ //END
+
+ private:
+ void updateView (bool changed = false, int viewLinesScrolled = 0);
+ void makeVisible (const KateTextCursor& c, uint endCol, bool force = false, bool center = false, bool calledExternally = false);
+
+ public:
+ inline const KateTextCursor& startPos() const { return m_startPos; }
+ inline uint startLine () const { return m_startPos.line(); }
+ inline uint startX () const { return m_startX; }
+
+ KateTextCursor endPos () const;
+ uint endLine () const;
+
+ KateLineRange yToKateLineRange(uint y) const;
+
+ void prepareForDynWrapChange();
+ void dynWrapChanged();
+
+ KateView *view () { return m_view; }
+
+ public slots:
+ void slotIncFontSizes();
+ void slotDecFontSizes();
+
+ private slots:
+ void scrollLines(int line); // connected to the sliderMoved of the m_lineScroll
+ void scrollViewLines(int offset);
+ void scrollNextPage();
+ void scrollPrevPage();
+ void scrollPrevLine();
+ void scrollNextLine();
+ void scrollColumns (int x); // connected to the valueChanged of the m_columnScroll
+ void viewSelectionChanged ();
+
+ public:
+ void doReturn();
+ void doDelete();
+ void doBackspace();
+ void doTranspose();
+ void doDeleteWordLeft();
+ void doDeleteWordRight();
+
+ void cursorLeft(bool sel=false);
+ void cursorRight(bool sel=false);
+ void wordLeft(bool sel=false);
+ void wordRight(bool sel=false);
+ void home(bool sel=false);
+ void end(bool sel=false);
+ void cursorUp(bool sel=false);
+ void cursorDown(bool sel=false);
+ void cursorToMatchingBracket(bool sel=false);
+ void scrollUp();
+ void scrollDown();
+ void topOfView(bool sel=false);
+ void bottomOfView(bool sel=false);
+ void pageUp(bool sel=false);
+ void pageDown(bool sel=false);
+ void top(bool sel=false);
+ void bottom(bool sel=false);
+ void top_home(bool sel=false);
+ void bottom_end(bool sel=false);
+
+ inline const KateTextCursor& getCursor() { return cursor; }
+ QPoint cursorCoordinates();
+
+ void paintText (int x, int y, int width, int height, bool paintOnlyDirty = false);
+
+ // EVENT HANDLING STUFF - IMPORTANT
+ protected:
+ void paintEvent(QPaintEvent *e);
+ bool eventFilter( QObject *obj, QEvent *e );
+ void keyPressEvent( QKeyEvent* );
+ void keyReleaseEvent( QKeyEvent* );
+ void resizeEvent( QResizeEvent* );
+ void mousePressEvent( QMouseEvent* );
+ void mouseDoubleClickEvent( QMouseEvent* );
+ void mouseReleaseEvent( QMouseEvent* );
+ void mouseMoveEvent( QMouseEvent* );
+ void dragEnterEvent( QDragEnterEvent* );
+ void dragMoveEvent( QDragMoveEvent* );
+ void dropEvent( QDropEvent* );
+ void showEvent ( QShowEvent *);
+ void wheelEvent(QWheelEvent* e);
+ void focusInEvent (QFocusEvent *);
+ void focusOutEvent (QFocusEvent *);
+
+ void contextMenuEvent ( QContextMenuEvent * e );
+
+ private slots:
+ void tripleClickTimeout();
+
+ signals:
+ // emitted when KateViewInternal is not handling its own URI drops
+ void dropEventPass(QDropEvent*);
+
+ private slots:
+ void slotRegionVisibilityChangedAt(unsigned int);
+ void slotRegionBeginEndAddedRemoved(unsigned int);
+ void slotCodeFoldingChanged();
+
+ private:
+ void moveChar( Bias bias, bool sel );
+ void moveEdge( Bias bias, bool sel );
+ KateTextCursor maxStartPos(bool changed = false);
+ void scrollPos(KateTextCursor& c, bool force = false, bool calledExternally = false);
+ void scrollLines( int lines, bool sel );
+
+ uint linesDisplayed() const;
+
+ int lineToY(uint viewLine) const;
+
+ void updateSelection( const KateTextCursor&, bool keepSel );
+ void updateCursor( const KateTextCursor& newCursor, bool force = false, bool center = false, bool calledExternally = false );
+ void updateBracketMarks();
+
+ void paintCursor();
+
+ void updateMicroFocusHint();
+
+ void placeCursor( const QPoint& p, bool keepSelection = false, bool updateSelection = true );
+ bool isTargetSelected( const QPoint& p );
+
+ void doDrag();
+
+ KateView *m_view;
+ KateDocument* m_doc;
+ class KateIconBorder *leftBorder;
+
+ int mouseX;
+ int mouseY;
+ int scrollX;
+ int scrollY;
+
+ Qt::CursorShape m_mouseCursor;
+
+ KateSuperCursor cursor;
+ KateTextCursor displayCursor;
+ int cXPos;
+
+ bool possibleTripleClick;
+
+ // Bracket mark
+ KateBracketRange bm;
+
+ enum DragState { diNone, diPending, diDragging };
+
+ struct _dragInfo {
+ DragState state;
+ QPoint start;
+ QTextDrag* dragObject;
+ } dragInfo;
+
+ uint iconBorderHeight;
+
+ //
+ // line scrollbar + first visible (virtual) line in the current view
+ //
+ KateScrollBar *m_lineScroll;
+ QWidget* m_dummy;
+ QVBoxLayout* m_lineLayout;
+ QHBoxLayout* m_colLayout;
+
+ // These are now cursors to account for word-wrap.
+ KateSuperCursor m_startPos;
+
+ // This is set to false on resize or scroll (other than that called by makeVisible),
+ // so that makeVisible is again called when a key is pressed and the cursor is in the same spot
+ bool m_madeVisible;
+ bool m_shiftKeyPressed;
+
+ // How many lines to should be kept visible above/below the cursor when possible
+ void setAutoCenterLines(int viewLines, bool updateView = true);
+ int m_autoCenterLines;
+ int m_minLinesVisible;
+
+ //
+ // column scrollbar + x position
+ //
+ QScrollBar *m_columnScroll;
+ int m_startX;
+
+ // has selection changed while your mouse or shift key is pressed
+ bool m_selChangedByUser;
+ KateTextCursor selectAnchor;
+
+ enum SelectionMode { Default=0, Word, Line, Mouse }; ///< for drag selection. @since 2.3
+ uint m_selectionMode;
+ // when drag selecting after double/triple click, keep the initial selected
+ // word/line independant of direction.
+ // They get set in the event of a double click, and is used with mouse move + leftbutton
+ KateTextCursor selStartCached;
+ KateTextCursor selEndCached;
+
+ //
+ // lines Ranges, mostly useful to speedup + dyn. word wrap
+ //
+ QMemArray<KateLineRange> lineRanges;
+
+ // maximal lenght of textlines visible from given startLine
+ int maxLen(uint startLine);
+
+ // are we allowed to scroll columns?
+ bool columnScrollingPossible ();
+
+ // returns the maximum X value / col value a cursor can take for a specific line range
+ int lineMaxCursorX(const KateLineRange& range);
+ int lineMaxCol(const KateLineRange& range);
+
+ // get the values for a specific range.
+ // specify lastLine to get the next line of a range.
+ KateLineRange range(int realLine, const KateLineRange* previous = 0L);
+
+ KateLineRange currentRange();
+ KateLineRange previousRange();
+ KateLineRange nextRange();
+
+ // Finds the lineRange currently occupied by the cursor.
+ KateLineRange range(const KateTextCursor& realCursor);
+
+ // Returns the lineRange of the specified realLine + viewLine.
+ KateLineRange range(uint realLine, int viewLine);
+
+ // find the view line of cursor c (0 = same line, 1 = down one, etc.)
+ uint viewLine(const KateTextCursor& realCursor);
+
+ // find the view line of the cursor, relative to the display (0 = top line of view, 1 = second line, etc.)
+ // if limitToVisible is true, only lines which are currently visible will be searched for, and -1 returned if the line is not visible.
+ int displayViewLine(const KateTextCursor& virtualCursor, bool limitToVisible = false);
+
+ // find the index of the last view line for a specific line
+ uint lastViewLine(uint realLine);
+
+ // count the number of view lines for a real line
+ uint viewLineCount(uint realLine);
+
+ // find the cursor offset by (offset) view lines from a cursor.
+ // when keepX is true, the column position will be calculated based on the x
+ // position of the specified cursor.
+ KateTextCursor viewLineOffset(const KateTextCursor& virtualCursor, int offset, bool keepX = false);
+
+ // These variable holds the most recent maximum real & visible column number
+ bool m_preserveMaxX;
+ int m_currentMaxX;
+
+ bool m_usePlainLines; // accept non-highlighted lines if this is set
+
+ inline KateTextLine::Ptr textLine( int realLine )
+ {
+ if (m_usePlainLines)
+ return m_doc->plainKateTextLine(realLine);
+ else
+ return m_doc->kateTextLine(realLine);
+ }
+
+ bool m_updatingView;
+ int m_wrapChangeViewLine;
+ KateTextCursor m_cachedMaxStartPos;
+
+ private slots:
+ void doDragScroll();
+ void startDragScroll();
+ void stopDragScroll();
+
+ private:
+ // Timers
+ QTimer m_dragScrollTimer;
+ QTimer m_scrollTimer;
+ QTimer m_cursorTimer;
+ QTimer m_textHintTimer;
+
+ static const int scrollTime = 30;
+ static const int scrollMargin = 16;
+
+ private slots:
+ void scrollTimeout ();
+ void cursorTimeout ();
+ void textHintTimeout ();
+
+ //TextHint
+ public:
+ void enableTextHints(int timeout);
+ void disableTextHints();
+
+ private:
+ bool m_textHintEnabled;
+ int m_textHintTimeout;
+ int m_textHintMouseX;
+ int m_textHintMouseY;
+
+ /**
+ * IM input stuff
+ */
+ protected:
+ void imStartEvent( QIMEvent *e );
+ void imComposeEvent( QIMEvent *e );
+ void imEndEvent( QIMEvent *e );
+
+ private:
+ int m_imPreeditStartLine;
+ int m_imPreeditStart;
+ int m_imPreeditLength;
+ int m_imPreeditSelStart;
+};
+
+#endif
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/kate/part/test_regression.cpp b/kate/part/test_regression.cpp
new file mode 100644
index 000000000..af36f65f9
--- /dev/null
+++ b/kate/part/test_regression.cpp
@@ -0,0 +1,1344 @@
+/**
+ * This file is part of the KDE project
+ *
+ * Copyright (C) 2001,2003 Peter Kelly (pmk@post.com)
+ * Copyright (C) 2003,2004 Stephan Kulow (coolo@kde.org)
+ * Copyright (C) 2004 Dirk Mueller ( mueller@kde.org )
+ * Copyright 2006 Leo Savernik (l.savernik@aon.at)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <signal.h>
+
+#include <kapplication.h>
+#include <kstandarddirs.h>
+#include <qimage.h>
+#include <qfile.h>
+#include "test_regression.h"
+#include <unistd.h>
+#include <stdio.h>
+
+#include <kaction.h>
+#include <kcmdlineargs.h>
+#include "katefactory.h"
+#include <kio/job.h>
+#include <kmainwindow.h>
+#include <ksimpleconfig.h>
+#include <kglobalsettings.h>
+
+#include <qcolor.h>
+#include <qcursor.h>
+#include <qdir.h>
+#include <qevent.h>
+#include <qobject.h>
+#include <qpushbutton.h>
+#include <qscrollview.h>
+#include <qstring.h>
+#include <qregexp.h>
+#include <qtextstream.h>
+#include <qvaluelist.h>
+#include <qwidget.h>
+#include <qfileinfo.h>
+#include <qtimer.h>
+#include <kstatusbar.h>
+#include <qfileinfo.h>
+
+#include "katedocument.h"
+#include "kateview.h"
+#include <kparts/browserextension.h>
+#include "katejscript.h"
+#include "katedocumenthelpers.h"
+#include "kateconfig.h"
+#include "../interfaces/katecmd.h"
+
+using namespace KJS;
+
+#define BASE_DIR_CONFIG "/.testkateregression-3.5"
+
+//BEGIN TestJScriptEnv
+
+TestJScriptEnv::TestJScriptEnv(KateDocument *part) {
+ ExecState *exec = m_interpreter->globalExec();
+
+ KJS::ObjectImp *wd = wrapDocument(m_interpreter->globalExec(), part);
+ KateView *v = static_cast<KateView *>(part->widget());
+ KJS::ObjectImp *wv = new KateViewObject(exec, v, wrapView(m_interpreter->globalExec(), v));
+
+ *m_view = KJS::Object(wv);
+ *m_document = KJS::Object(wd);
+ m_output = new OutputObject(exec, part, v);
+ m_output->ref();
+
+ // recreate properties
+ m_interpreter->globalObject().put(exec, "document", *m_document);
+ m_interpreter->globalObject().put(exec, "view", *m_view);
+ // create new properties
+ m_interpreter->globalObject().put(exec, "output", KJS::Object(m_output));
+ // add convenience shortcuts
+ m_interpreter->globalObject().put(exec, "d", *m_document);
+ m_interpreter->globalObject().put(exec, "v", *m_view);
+ m_interpreter->globalObject().put(exec, "out", KJS::Object(m_output));
+ m_interpreter->globalObject().put(exec, "o", KJS::Object(m_output));
+}
+
+TestJScriptEnv::~TestJScriptEnv() {
+ m_output->deref();
+}
+
+//END TestJScriptEnv
+
+//BEGIN KateViewObject
+
+KateViewObject::KateViewObject(ExecState *exec, KateView *v, ObjectImp *fallback)
+ : view(v), fallback(fallback)
+{
+// put a function
+#define PUT_FUNC(name, enumval) \
+ putDirect(#name, new KateViewFunction(exec,v,KateViewFunction::enumval,1), DontEnum)
+ fallback->ref();
+
+ PUT_FUNC(keyReturn, KeyReturn);
+ PUT_FUNC(enter, KeyReturn);
+ PUT_FUNC(type, Type);
+ PUT_FUNC(keyDelete, KeyDelete);
+ PUT_FUNC(deleteWordRight, DeleteWordRight);
+ PUT_FUNC(transpose, Transpose);
+ PUT_FUNC(cursorLeft, CursorLeft);
+ PUT_FUNC(cursorPrev, CursorLeft);
+ PUT_FUNC(left, CursorLeft);
+ PUT_FUNC(prev, CursorLeft);
+ PUT_FUNC(shiftCursorLeft, ShiftCursorLeft);
+ PUT_FUNC(shiftCursorPrev, ShiftCursorLeft);
+ PUT_FUNC(shiftLeft, ShiftCursorLeft);
+ PUT_FUNC(shiftPrev, ShiftCursorLeft);
+ PUT_FUNC(cursorRight, CursorRight);
+ PUT_FUNC(cursorNext, CursorRight);
+ PUT_FUNC(right, CursorRight);
+ PUT_FUNC(next, CursorRight);
+ PUT_FUNC(shiftCursorRight, ShiftCursorRight);
+ PUT_FUNC(shiftCursorNext, ShiftCursorRight);
+ PUT_FUNC(shiftRight, ShiftCursorRight);
+ PUT_FUNC(shiftNext, ShiftCursorRight);
+ PUT_FUNC(wordLeft, WordLeft);
+ PUT_FUNC(wordPrev, WordLeft);
+ PUT_FUNC(shiftWordLeft, ShiftWordLeft);
+ PUT_FUNC(shiftWordPrev, ShiftWordLeft);
+ PUT_FUNC(wordRight, WordRight);
+ PUT_FUNC(wordNext, WordRight);
+ PUT_FUNC(shiftWordRight, ShiftWordRight);
+ PUT_FUNC(shiftWordNext, ShiftWordRight);
+ PUT_FUNC(home, Home);
+ PUT_FUNC(shiftHome, ShiftHome);
+ PUT_FUNC(end, End);
+ PUT_FUNC(shiftEnd, ShiftEnd);
+ PUT_FUNC(up, Up);
+ PUT_FUNC(shiftUp, ShiftUp);
+ PUT_FUNC(down, Down);
+ PUT_FUNC(shiftDown, ShiftDown);
+ PUT_FUNC(scrollUp, ScrollUp);
+ PUT_FUNC(scrollDown, ScrollDown);
+ PUT_FUNC(topOfView, TopOfView);
+ PUT_FUNC(shiftTopOfView, ShiftTopOfView);
+ PUT_FUNC(bottomOfView, BottomOfView);
+ PUT_FUNC(shiftBottomOfView, ShiftBottomOfView);
+ PUT_FUNC(pageUp, PageUp);
+ PUT_FUNC(shiftPageUp, ShiftPageUp);
+ PUT_FUNC(pageDown, PageDown);
+ PUT_FUNC(shiftPageDown, ShiftPageDown);
+ PUT_FUNC(top, Top);
+ PUT_FUNC(shiftTop, ShiftTop);
+ PUT_FUNC(bottom, Bottom);
+ PUT_FUNC(shiftBottom, ShiftBottom);
+ PUT_FUNC(toMatchingBracket, ToMatchingBracket);
+ PUT_FUNC(shiftToMatchingBracket, ShiftToMatchingBracket);
+#undef PUT_FUNC
+}
+
+KateViewObject::~KateViewObject()
+{
+ fallback->deref();
+}
+
+const ClassInfo *KateViewObject::classInfo() const {
+ // evil hack II: disguise as fallback, otherwise we can't fall back
+ return fallback->classInfo();
+}
+
+Value KateViewObject::get(ExecState *exec, const Identifier &propertyName) const
+{
+ ValueImp *val = getDirect(propertyName);
+ if (val)
+ return Value(val);
+
+ return fallback->get(exec, propertyName);
+}
+
+//END KateViewObject
+
+//BEGIN KateViewFunction
+
+KateViewFunction::KateViewFunction(ExecState */*exec*/, KateView *v, int _id, int length)
+{
+ m_view = v;
+ id = _id;
+ putDirect("length",length);
+}
+
+bool KateViewFunction::implementsCall() const
+{
+ return true;
+}
+
+Value KateViewFunction::call(ExecState *exec, Object &/*thisObj*/, const List &args)
+{
+ // calls a function repeatedly as specified by its first parameter (once
+ // if not specified).
+#define REP_CALL(enumval, func) \
+ case enumval: {\
+ int cnt = 1;\
+ if (args.size() > 0) cnt = args[0].toInt32(exec);\
+ while (cnt-- > 0) { m_view->func(); }\
+ return Undefined();\
+ }
+ switch (id) {
+ REP_CALL(KeyReturn, keyReturn);
+ REP_CALL(KeyDelete, keyDelete);
+ REP_CALL(DeleteWordRight, deleteWordRight);
+ REP_CALL(Transpose, transpose);
+ REP_CALL(CursorLeft, cursorLeft);
+ REP_CALL(ShiftCursorLeft, shiftCursorLeft);
+ REP_CALL(CursorRight, cursorRight);
+ REP_CALL(ShiftCursorRight, shiftCursorRight);
+ REP_CALL(WordLeft, wordLeft);
+ REP_CALL(ShiftWordLeft, shiftWordLeft);
+ REP_CALL(WordRight, wordRight);
+ REP_CALL(ShiftWordRight, shiftWordRight);
+ REP_CALL(Home, home);
+ REP_CALL(ShiftHome, shiftHome);
+ REP_CALL(End, end);
+ REP_CALL(ShiftEnd, shiftEnd);
+ REP_CALL(Up, up);
+ REP_CALL(ShiftUp, shiftUp);
+ REP_CALL(Down, down);
+ REP_CALL(ShiftDown, shiftDown);
+ REP_CALL(ScrollUp, scrollUp);
+ REP_CALL(ScrollDown, scrollDown);
+ REP_CALL(TopOfView, topOfView);
+ REP_CALL(ShiftTopOfView, shiftTopOfView);
+ REP_CALL(BottomOfView, bottomOfView);
+ REP_CALL(ShiftBottomOfView, shiftBottomOfView);
+ REP_CALL(PageUp, pageUp);
+ REP_CALL(ShiftPageUp, shiftPageUp);
+ REP_CALL(PageDown, pageDown);
+ REP_CALL(ShiftPageDown, shiftPageDown);
+ REP_CALL(Top, top);
+ REP_CALL(ShiftTop, shiftTop);
+ REP_CALL(Bottom, bottom);
+ REP_CALL(ShiftBottom, shiftBottom);
+ REP_CALL(ToMatchingBracket, toMatchingBracket);
+ REP_CALL(ShiftToMatchingBracket, shiftToMatchingBracket);
+ case Type: {
+ UString str = args[0].toString(exec);
+ QString res = str.qstring();
+ return Boolean(m_view->doc()->typeChars(m_view, res));
+ }
+ }
+
+ return Undefined();
+#undef REP_CALL
+}
+
+//END KateViewFunction
+
+//BEGIN OutputObject
+
+OutputObject::OutputObject(KJS::ExecState *exec, KateDocument *d, KateView *v) : doc(d), view(v), changed(0), outstr(0) {
+ putDirect("write", new OutputFunction(exec,this,OutputFunction::Write,-1), DontEnum);
+ putDirect("print", new OutputFunction(exec,this,OutputFunction::Write,-1), DontEnum);
+ putDirect("writeln", new OutputFunction(exec,this,OutputFunction::Writeln,-1), DontEnum);
+ putDirect("println", new OutputFunction(exec,this,OutputFunction::Writeln,-1), DontEnum);
+ putDirect("writeLn", new OutputFunction(exec,this,OutputFunction::Writeln,-1), DontEnum);
+ putDirect("printLn", new OutputFunction(exec,this,OutputFunction::Writeln,-1), DontEnum);
+
+ putDirect("writeCursorPosition", new OutputFunction(exec,this,OutputFunction::WriteCursorPosition,-1), DontEnum);
+ putDirect("cursorPosition", new OutputFunction(exec,this,OutputFunction::WriteCursorPosition,-1), DontEnum);
+ putDirect("pos", new OutputFunction(exec,this,OutputFunction::WriteCursorPosition,-1), DontEnum);
+ putDirect("writeCursorPositionln", new OutputFunction(exec,this,OutputFunction::WriteCursorPositionln,-1), DontEnum);
+ putDirect("cursorPositionln", new OutputFunction(exec,this,OutputFunction::WriteCursorPositionln,-1), DontEnum);
+ putDirect("posln", new OutputFunction(exec,this,OutputFunction::WriteCursorPositionln,-1), DontEnum);
+
+}
+
+OutputObject::~OutputObject() {
+}
+
+KJS::UString OutputObject::className() const {
+ return UString("OutputObject");
+}
+
+//END OutputObject
+
+//BEGIN OutputFunction
+
+OutputFunction::OutputFunction(KJS::ExecState *exec, OutputObject *output, int _id, int length)
+ : o(output)
+{
+ id = _id;
+ if (length >= 0)
+ putDirect("length",length);
+}
+
+bool OutputFunction::implementsCall() const
+{
+ return true;
+}
+
+KJS::Value OutputFunction::call(KJS::ExecState *exec, KJS::Object &thisObj, const KJS::List &args)
+{
+ if (!*o->changed) *o->outstr = QString();
+
+ switch (id) {
+ case Write:
+ case Writeln: {
+ // Gather all parameters and concatenate to string
+ QString res;
+ for (int i = 0; i < args.size(); i++) {
+ res += args[i].toString(exec).qstring();
+ }
+
+ if (id == Writeln)
+ res += "\n";
+
+ *o->outstr += res;
+ break;
+ }
+ case WriteCursorPositionln:
+ case WriteCursorPosition: {
+ // Gather all parameters and concatenate to string
+ QString res;
+ for (int i = 0; i < args.size(); i++) {
+ res += args[i].toString(exec).qstring();
+ }
+
+ // Append cursor position
+ uint l, c;
+ o->view->cursorPosition(&l, &c);
+ res += "(" + QString::number(l) + "," + QString::number(c) + ")";
+
+ if (id == WriteCursorPositionln)
+ res += "\n";
+
+ *o->outstr += res;
+ break;
+ }
+ }
+
+ *o->changed = true;
+ return Undefined();
+}
+
+//END OutputFunction
+
+// -------------------------------------------------------------------------
+
+const char failureSnapshotPrefix[] = "testkateregressionrc-FS.";
+
+static QString findMostRecentFailureSnapshot() {
+ QDir dir(kapp->dirs()->saveLocation("config"),
+ QString(failureSnapshotPrefix)+"*",
+ QDir::Time, QDir::Files);
+ return dir[0].mid(sizeof failureSnapshotPrefix - 1);
+}
+
+static KCmdLineOptions options[] =
+{
+ { "b", 0, 0 },
+ { "base <base_dir>", "Directory containing tests, basedir and output directories.", 0},
+ { "cmp-failures <snapshot>", "Compare failures of this testrun against snapshot <snapshot>. Defaults to the most recently captured failure snapshot or none if none exists.", 0 },
+ { "d", 0, 0 },
+ { "debug", "Do not supress debug output", 0},
+ { "g", 0, 0 } ,
+ { "genoutput", "Regenerate baseline (instead of checking)", 0 } ,
+ { "keep-output", "Keep output files even on success", 0 },
+ { "save-failures <snapshot>", "Save failures of this testrun as failure snapshot <snapshot>", 0 },
+ { "s", 0, 0 } ,
+ { "show", "Show the window while running tests", 0 } ,
+ { "t", 0, 0 } ,
+ { "test <filename>", "Only run a single test. Multiple options allowed.", 0 } ,
+ { "o", 0, 0 },
+ { "output <directory>", "Put output in <directory> instead of <base_dir>/output", 0 } ,
+ { "+[base_dir]", "Directory containing tests,basedir and output directories. Only regarded if -b is not specified.", 0 } ,
+ { "+[testcases]", "Relative path to testcase, or directory of testcases to be run (equivalent to -t).", 0 } ,
+ KCmdLineLastOption
+};
+
+int main(int argc, char *argv[])
+{
+ // forget about any settings
+ passwd* pw = getpwuid( getuid() );
+ if (!pw) {
+ fprintf(stderr, "dang, I don't even know who I am.\n");
+ exit(1);
+ }
+
+ QString kh("/var/tmp/%1_kate_non_existent");
+ kh = kh.arg( pw->pw_name );
+ setenv( "KDEHOME", kh.latin1(), 1 );
+ setenv( "LC_ALL", "C", 1 );
+ setenv( "LANG", "C", 1 );
+
+// signal( SIGALRM, signal_handler );
+
+ KCmdLineArgs::init(argc, argv, "testregression", "TestRegression",
+ "Regression tester for kate", "1.0");
+ KCmdLineArgs::addCmdLineOptions(options);
+
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs( );
+
+ QCString baseDir = args->getOption("base");
+ QCString baseDirConfigFile(::getenv("HOME") + QCString(BASE_DIR_CONFIG));
+ {
+ QFile baseDirConfig(baseDirConfigFile);
+ if (baseDirConfig.open(IO_ReadOnly)) {
+ QTextStream bds(&baseDirConfig);
+ baseDir = bds.readLine().latin1();
+ }
+ }
+
+ if ( args->count() < 1 && baseDir.isEmpty() ) {
+ printf("For regression testing, make sure to have checked out the kate regression\n"
+ "testsuite from svn:\n"
+ "\tsvn co \"https://<user>@svn.kde.org:/home/kde/trunk/tests/katetests/regression\"\n"
+ "Remember the root path into which you checked out the testsuite.\n"
+ "\n");
+ printf("%s needs the root path of the kate regression\n"
+ "testsuite to function properly\n"
+ "By default, the root path is looked up in the file\n"
+ "\t%s\n"
+ "If it doesn't exist yet, create it by invoking\n"
+ "\techo \"<root-path>\" > %s\n"
+ "You may override the location by specifying the root explicitly on the\n"
+ "command line with option -b\n"
+ "", KCmdLineArgs::appName(),
+ (const char *)baseDirConfigFile,
+ (const char *)baseDirConfigFile);
+ ::exit( 1 );
+ }
+
+ int testcase_index = 0;
+ if (baseDir.isEmpty()) baseDir = args->arg(testcase_index++);
+
+ QFileInfo bdInfo(baseDir);
+ baseDir = QFile::encodeName(bdInfo.absFilePath());
+
+ const char *subdirs[] = {"tests", "baseline", "output", "resources"};
+ for ( int i = 0; i < 2; i++ ) {
+ QFileInfo sourceDir(QFile::encodeName( baseDir ) + "/" + subdirs[i]);
+ if ( !sourceDir.exists() || !sourceDir.isDir() ) {
+ fprintf(stderr,"ERROR: Source directory \"%s/%s\": no such directory.\n", (const char *)baseDir, subdirs[i]);
+ exit(1);
+ }
+ }
+
+ KApplication a;
+ a.disableAutoDcopRegistration();
+ a.setStyle("windows");
+ KSimpleConfig cfg( "testkateregressionrc" );
+ cfg.setGroup("Kate Document Defaults");
+ cfg.writeEntry("Basic Config Flags",
+ KateDocumentConfig::cfBackspaceIndents
+// | KateDocumentConfig::cfWordWrap
+// | KateDocumentConfig::cfRemoveSpaces
+ | KateDocumentConfig::cfWrapCursor
+// | KateDocumentConfig::cfAutoBrackets
+// | KateDocumentConfig::cfTabIndentsMode
+// | KateDocumentConfig::cfOvr
+ | KateDocumentConfig::cfKeepIndentProfile
+ | KateDocumentConfig::cfKeepExtraSpaces
+ | KateDocumentConfig::cfTabIndents
+ | KateDocumentConfig::cfShowTabs
+ | KateDocumentConfig::cfSpaceIndent
+ | KateDocumentConfig::cfSmartHome
+ | KateDocumentConfig::cfTabInsertsTab
+// | KateDocumentConfig::cfReplaceTabsDyn
+// | KateDocumentConfig::cfRemoveTrailingDyn
+ | KateDocumentConfig::cfDoxygenAutoTyping
+// | KateDocumentConfig::cfMixedIndent
+ | KateDocumentConfig::cfIndentPastedText
+ );
+ cfg.sync();
+
+ int rv = 1;
+
+ {
+ KSimpleConfig dc( "kdebugrc" );
+ // FIXME adapt to kate
+ static int areas[] = { 1000, 13000, 13001, 13002, 13010,
+ 13020, 13025, 13030, 13033, 13035,
+ 13040, 13050, 13051, 7000, 7006, 170,
+ 171, 7101, 7002, 7019, 7027, 7014,
+ 7001, 7011, 6070, 6080, 6090, 0};
+ int channel = args->isSet( "debug" ) ? 2 : 4;
+ for ( int i = 0; areas[i]; ++i ) {
+ dc.setGroup( QString::number( areas[i] ) );
+ dc.writeEntry( "InfoOutput", channel );
+ }
+ dc.sync();
+
+ kdClearDebugConfig();
+ }
+
+ // create widgets
+ KateFactory *fac = KateFactory::self();
+ KMainWindow *toplevel = new KMainWindow();
+ KateDocument *part = new KateDocument(/*bSingleViewMode*/true,
+ /*bBrowserView*/false,
+ /*bReadOnly*/false,
+ /*parentWidget*/toplevel,
+ /*widgetName*/"testkate");
+ part->readConfig(&cfg);
+
+ toplevel->setCentralWidget( part->widget() );
+
+ Q_ASSERT(part->config()->configFlags() & KateDocumentConfig::cfDoxygenAutoTyping);
+
+ bool visual = false;
+ if (args->isSet("show"))
+ visual = true;
+
+ a.setTopWidget(part->widget());
+ a.setMainWidget( toplevel );
+ if ( visual )
+ toplevel->show();
+
+ // we're not interested
+ toplevel->statusBar()->hide();
+
+ if (!getenv("KDE_DEBUG")) {
+ // set ulimits
+ rlimit vmem_limit = { 256*1024*1024, RLIM_INFINITY }; // 256Mb Memory should suffice
+ setrlimit(RLIMIT_AS, &vmem_limit);
+ rlimit stack_limit = { 8*1024*1024, RLIM_INFINITY }; // 8Mb Memory should suffice
+ setrlimit(RLIMIT_STACK, &stack_limit);
+ }
+
+ // run the tests
+ RegressionTest *regressionTest = new RegressionTest(part,
+ &cfg,
+ baseDir,
+ args->getOption("output"),
+ args->isSet("genoutput"));
+ QObject::connect(part->browserExtension(), SIGNAL(openURLRequest(const KURL &, const KParts::URLArgs &)),
+ regressionTest, SLOT(slotOpenURL(const KURL&, const KParts::URLArgs &)));
+ QObject::connect(part->browserExtension(), SIGNAL(resizeTopLevelWidget( int, int )),
+ regressionTest, SLOT(resizeTopLevelWidget( int, int )));
+
+ regressionTest->m_keepOutput = args->isSet("keep-output");
+ regressionTest->m_showGui = args->isSet("show");
+
+ {
+ QString failureSnapshot = args->getOption("cmp-failures");
+ if (failureSnapshot.isEmpty())
+ failureSnapshot = findMostRecentFailureSnapshot();
+ if (!failureSnapshot.isEmpty())
+ regressionTest->setFailureSnapshotConfig(
+ new KSimpleConfig(failureSnapshotPrefix + failureSnapshot, true),
+ failureSnapshot);
+ }
+
+ if (args->isSet("save-failures")) {
+ QString failureSaver = args->getOption("save-failures");
+ regressionTest->setFailureSnapshotSaver(
+ new KSimpleConfig(failureSnapshotPrefix + failureSaver, false),
+ failureSaver);
+ }
+
+ bool result = false;
+ QCStringList tests = args->getOptionList("test");
+ // merge testcases specified on command line
+ for (; testcase_index < args->count(); testcase_index++)
+ tests << args->arg(testcase_index);
+ if (tests.count() > 0)
+ for (QValueListConstIterator<QCString> it = tests.begin(); it != tests.end(); ++it) {
+ result = regressionTest->runTests(*it,true);
+ if (!result) break;
+ }
+ else
+ result = regressionTest->runTests();
+
+ if (result) {
+ if (args->isSet("genoutput")) {
+ printf("\nOutput generation completed.\n");
+ }
+ else {
+ printf("\nTests completed.\n");
+ printf("Total: %d\n",
+ regressionTest->m_passes_work+
+ regressionTest->m_passes_fail+
+ regressionTest->m_failures_work+
+ regressionTest->m_failures_fail+
+ regressionTest->m_errors);
+ printf("Passes: %d",regressionTest->m_passes_work);
+ if ( regressionTest->m_passes_fail )
+ printf( " (%d unexpected passes)", regressionTest->m_passes_fail );
+ if (regressionTest->m_passes_new)
+ printf(" (%d new since %s)", regressionTest->m_passes_new, regressionTest->m_failureComp->group().latin1());
+ printf( "\n" );
+ printf("Failures: %d",regressionTest->m_failures_work);
+ if ( regressionTest->m_failures_fail )
+ printf( " (%d expected failures)", regressionTest->m_failures_fail );
+ if ( regressionTest->m_failures_new )
+ printf(" (%d new since %s)", regressionTest->m_failures_new, regressionTest->m_failureComp->group().latin1());
+ printf( "\n" );
+ if ( regressionTest->m_errors )
+ printf("Errors: %d\n",regressionTest->m_errors);
+
+ QFile list( regressionTest->m_outputDir + "/links.html" );
+ list.open( IO_WriteOnly|IO_Append );
+ QString link, cl;
+ link = QString( "<hr>%1 failures. (%2 expected failures)" )
+ .arg(regressionTest->m_failures_work )
+ .arg( regressionTest->m_failures_fail );
+ if (regressionTest->m_failures_new)
+ link += QString(" <span style=\"color:red;font-weight:bold\">(%1 new failures since %2)</span>")
+ .arg(regressionTest->m_failures_new)
+ .arg(regressionTest->m_failureComp->group());
+ if (regressionTest->m_passes_new)
+ link += QString(" <p style=\"color:green;font-weight:bold\">%1 new passes since %2</p>")
+ .arg(regressionTest->m_passes_new)
+ .arg(regressionTest->m_failureComp->group());
+ list.writeBlock( link.latin1(), link.length() );
+ list.close();
+ }
+ }
+
+ // Only return a 0 exit code if all tests were successful
+ if (regressionTest->m_failures_work == 0 && regressionTest->m_errors == 0)
+ rv = 0;
+
+ // cleanup
+ delete regressionTest;
+ delete part;
+ delete toplevel;
+// delete fac;
+
+ return rv;
+}
+
+// -------------------------------------------------------------------------
+
+RegressionTest *RegressionTest::curr = 0;
+
+RegressionTest::RegressionTest(KateDocument *part, KConfig *baseConfig,
+ const QString &baseDir,
+ const QString &outputDir, bool _genOutput)
+ : QObject(part)
+{
+ m_part = part;
+ m_view = static_cast<KateView *>(m_part->widget());
+ m_baseConfig = baseConfig;
+ m_baseDir = baseDir;
+ m_baseDir = m_baseDir.replace( "//", "/" );
+ if ( m_baseDir.endsWith( "/" ) )
+ m_baseDir = m_baseDir.left( m_baseDir.length() - 1 );
+ if (outputDir.isEmpty())
+ m_outputDir = m_baseDir + "/output";
+ else
+ m_outputDir = outputDir;
+ createMissingDirs(m_outputDir + "/");
+ m_keepOutput = false;
+ m_genOutput = _genOutput;
+ m_failureComp = 0;
+ m_failureSave = 0;
+ m_showGui = false;
+ m_passes_work = m_passes_fail = m_passes_new = 0;
+ m_failures_work = m_failures_fail = m_failures_new = 0;
+ m_errors = 0;
+
+ ::unlink( QFile::encodeName( m_outputDir + "/links.html" ) );
+ QFile f( m_outputDir + "/empty.html" );
+ QString s;
+ f.open( IO_WriteOnly | IO_Truncate );
+ s = "<html><body>Follow the white rabbit";
+ f.writeBlock( s.latin1(), s.length() );
+ f.close();
+ f.setName( m_outputDir + "/index.html" );
+ f.open( IO_WriteOnly | IO_Truncate );
+ s = "<html><frameset cols=150,*><frame src=links.html><frame name=content src=empty.html>";
+ f.writeBlock( s.latin1(), s.length() );
+ f.close();
+
+ curr = this;
+}
+
+#include <qobjectlist.h>
+
+static QStringList readListFile( const QString &filename )
+{
+ // Read ignore file for this directory
+ QString ignoreFilename = filename;
+ QFileInfo ignoreInfo(ignoreFilename);
+ QStringList ignoreFiles;
+ if (ignoreInfo.exists()) {
+ QFile ignoreFile(ignoreFilename);
+ if (!ignoreFile.open(IO_ReadOnly)) {
+ fprintf(stderr,"Can't open %s\n",ignoreFilename.latin1());
+ exit(1);
+ }
+ QTextStream ignoreStream(&ignoreFile);
+ QString line;
+ while (!(line = ignoreStream.readLine()).isNull())
+ ignoreFiles.append(line);
+ ignoreFile.close();
+ }
+ return ignoreFiles;
+}
+
+RegressionTest::~RegressionTest()
+{
+ // Important! Delete comparison config *first* as saver config
+ // might point to the same physical file.
+ delete m_failureComp;
+ delete m_failureSave;
+}
+
+void RegressionTest::setFailureSnapshotConfig(KConfig *cfg, const QString &sname)
+{
+ Q_ASSERT(cfg);
+ m_failureComp = cfg;
+ m_failureComp->setGroup(sname);
+}
+
+void RegressionTest::setFailureSnapshotSaver(KConfig *cfg, const QString &sname)
+{
+ Q_ASSERT(cfg);
+ m_failureSave = cfg;
+ m_failureSave->setGroup(sname);
+}
+
+QStringList RegressionTest::concatListFiles(const QString &relPath, const QString &filename)
+{
+ QStringList cmds;
+ int pos = relPath.findRev('/');
+ if (pos >= 0)
+ cmds += concatListFiles(relPath.left(pos), filename);
+ cmds += readListFile(m_baseDir + "/tests/" + relPath + "/" + filename);
+ return cmds;
+}
+
+bool RegressionTest::runTests(QString relPath, bool mustExist, int known_failure)
+{
+ m_currentOutput = QString::null;
+
+ if (!QFile(m_baseDir + "/tests/"+relPath).exists()) {
+ fprintf(stderr,"%s: No such file or directory\n",relPath.latin1());
+ return false;
+ }
+
+ QString fullPath = m_baseDir + "/tests/"+relPath;
+ QFileInfo info(fullPath);
+
+ if (!info.exists() && mustExist) {
+ fprintf(stderr,"%s: No such file or directory\n",relPath.latin1());
+ return false;
+ }
+
+ if (!info.isReadable() && mustExist) {
+ fprintf(stderr,"%s: Access denied\n",relPath.latin1());
+ return false;
+ }
+
+ if (info.isDir()) {
+ QStringList ignoreFiles = readListFile( m_baseDir + "/tests/"+relPath+"/ignore" );
+ QStringList failureFiles = readListFile( m_baseDir + "/tests/"+relPath+"/KNOWN_FAILURES" );
+
+ // Run each test in this directory, recusively
+ QDir sourceDir(m_baseDir + "/tests/"+relPath);
+ for (uint fileno = 0; fileno < sourceDir.count(); fileno++) {
+ QString filename = sourceDir[fileno];
+ QString relFilename = relPath.isEmpty() ? filename : relPath+"/"+filename;
+
+ if (filename.startsWith(".") || ignoreFiles.contains(filename) )
+ continue;
+ int failure_type = NoFailure;
+ if ( failureFiles.contains( filename ) )
+ failure_type |= AllFailure;
+ if ( failureFiles.contains ( filename + "-result" ) )
+ failure_type |= ResultFailure;
+ runTests(relFilename, false, failure_type);
+ }
+ }
+ else if (info.isFile()) {
+
+ QString relativeDir = QFileInfo(relPath).dirPath();
+ QString filename = info.fileName();
+ m_currentBase = m_baseDir + "/tests/"+relativeDir;
+ m_currentCategory = relativeDir;
+ m_currentTest = filename;
+ m_known_failures = known_failure;
+ m_outputCustomised = false;
+ // gather commands
+ // directory-specific commands
+ QStringList commands = concatListFiles(relPath, ".kateconfig-commands");
+ // testcase-specific commands
+ commands += readListFile(m_currentBase + "/" + filename + "-commands");
+
+ rereadConfig(); // reset options to default
+ if ( filename.endsWith(".txt") ) {
+#if 0
+ if ( relPath.startsWith( "domts/" ) && !m_runJS )
+ return true;
+ if ( relPath.startsWith( "ecma/" ) && !m_runJS )
+ return true;
+#endif
+// if ( m_runHTML )
+ testStaticFile(relPath, commands);
+ }
+ else if (mustExist) {
+ fprintf(stderr,"%s: Not a valid test file (must be .txt)\n",relPath.latin1());
+ return false;
+ }
+ } else if (mustExist) {
+ fprintf(stderr,"%s: Not a regular file\n",relPath.latin1());
+ return false;
+ }
+
+ return true;
+}
+
+void RegressionTest::createLink( const QString& test, int failures )
+{
+ createMissingDirs( m_outputDir + "/" + test + "-compare.html" );
+
+ QFile list( m_outputDir + "/links.html" );
+ list.open( IO_WriteOnly|IO_Append );
+ QString link;
+ link = QString( "<a href=\"%1\" target=\"content\" title=\"%2\">" )
+ .arg( test + "-compare.html" )
+ .arg( test );
+ link += m_currentTest;
+ link += "</a> ";
+ if (failures & NewFailure)
+ link += "<span style=\"font-weight:bold;color:red\">";
+ link += "[";
+ if ( failures & ResultFailure )
+ link += "R";
+ link += "]";
+ if (failures & NewFailure)
+ link += "</span>";
+ link += "<br>\n";
+ list.writeBlock( link.latin1(), link.length() );
+ list.close();
+}
+
+/** returns the path in a way that is relatively reachable from base.
+ * @param base base directory (must not include trailing slash)
+ * @param path directory/file to be relatively reached by base
+ * @return path with all elements replaced by .. and concerning path elements
+ * to be relatively reachable from base.
+ */
+static QString makeRelativePath(const QString &base, const QString &path)
+{
+ QString absBase = QFileInfo(base).absFilePath();
+ QString absPath = QFileInfo(path).absFilePath();
+// kdDebug() << "absPath: \"" << absPath << "\"" << endl;
+// kdDebug() << "absBase: \"" << absBase << "\"" << endl;
+
+ // walk up to common ancestor directory
+ int pos = 0;
+ do {
+ pos++;
+ int newpos = absBase.find('/', pos);
+ if (newpos == -1) newpos = absBase.length();
+ QConstString cmpPathComp(absPath.unicode() + pos, newpos - pos);
+ QConstString cmpBaseComp(absBase.unicode() + pos, newpos - pos);
+// kdDebug() << "cmpPathComp: \"" << cmpPathComp.string() << "\"" << endl;
+// kdDebug() << "cmpBaseComp: \"" << cmpBaseComp.string() << "\"" << endl;
+// kdDebug() << "pos: " << pos << " newpos: " << newpos << endl;
+ if (cmpPathComp.string() != cmpBaseComp.string()) { pos--; break; }
+ pos = newpos;
+ } while (pos < (int)absBase.length() && pos < (int)absPath.length());
+ int basepos = pos < (int)absBase.length() ? pos + 1 : pos;
+ int pathpos = pos < (int)absPath.length() ? pos + 1 : pos;
+
+// kdDebug() << "basepos " << basepos << " pathpos " << pathpos << endl;
+
+ QString rel;
+ {
+ QConstString relBase(absBase.unicode() + basepos, absBase.length() - basepos);
+ QConstString relPath(absPath.unicode() + pathpos, absPath.length() - pathpos);
+ // generate as many .. as there are path elements in relBase
+ if (relBase.string().length() > 0) {
+ for (int i = relBase.string().contains('/'); i > 0; --i)
+ rel += "../";
+ rel += "..";
+ if (relPath.string().length() > 0) rel += "/";
+ }
+ rel += relPath.string();
+ }
+ return rel;
+}
+
+/** processes events for at least \c msec milliseconds */
+static void pause(int msec)
+{
+ QTime t;
+ t.start();
+ do {
+ kapp->processEvents();
+ } while (t.elapsed() < msec);
+}
+
+void RegressionTest::doFailureReport( const QString& test, int failures )
+{
+ if ( failures == NoFailure ) {
+ ::unlink( QFile::encodeName( m_outputDir + "/" + test + "-compare.html" ) );
+ return;
+ }
+
+ createLink( test, failures );
+
+ QFile compare( m_outputDir + "/" + test + "-compare.html" );
+
+ QString testFile = QFileInfo(test).fileName();
+
+ QString renderDiff;
+ QString domDiff;
+
+ QString relOutputDir = makeRelativePath(m_baseDir, m_outputDir);
+
+ // are blocking reads possible with KProcess?
+ char pwd[PATH_MAX];
+ (void) getcwd( pwd, PATH_MAX );
+ chdir( QFile::encodeName( m_baseDir ) );
+
+ if ( failures & ResultFailure ) {
+ domDiff += "<pre>";
+ FILE *pipe = popen( QString::fromLatin1( "diff -u baseline/%1-result %3/%2-result" )
+ .arg ( test, test, relOutputDir ).latin1(), "r" );
+ QTextIStream *is = new QTextIStream( pipe );
+ for ( int line = 0; line < 100 && !is->eof(); ++line ) {
+ QString line = is->readLine();
+ line = line.replace( '<', "&lt;" );
+ line = line.replace( '>', "&gt;" );
+ domDiff += line + "\n";
+ }
+ delete is;
+ pclose( pipe );
+ domDiff += "</pre>";
+ }
+
+ chdir( pwd );
+
+ // create a relative path so that it works via web as well. ugly
+ QString relpath = makeRelativePath(m_outputDir + "/"
+ + QFileInfo(test).dirPath(), m_baseDir);
+
+ compare.open( IO_WriteOnly|IO_Truncate );
+ QString cl;
+ cl = QString( "<html><head><title>%1</title>" ).arg( test );
+ cl += QString( "<script>\n"
+ "var pics = new Array();\n"
+ "pics[0]=new Image();\n"
+ "pics[0].src = '%1';\n"
+ "pics[1]=new Image();\n"
+ "pics[1].src = '%2';\n"
+ "var doflicker = 1;\n"
+ "var t = 1;\n"
+ "var lastb=0;\n" )
+ .arg( relpath+"/baseline/"+test+"-dump.png" )
+ .arg( testFile+"-dump.png" );
+ cl += QString( "function toggleVisible(visible) {\n"
+ " document.getElementById('render').style.visibility= visible == 'render' ? 'visible' : 'hidden';\n"
+ " document.getElementById('image').style.visibility= visible == 'image' ? 'visible' : 'hidden';\n"
+ " document.getElementById('dom').style.visibility= visible == 'dom' ? 'visible' : 'hidden';\n"
+ "}\n"
+ "function show() { document.getElementById('image').src = pics[t].src; "
+ "document.getElementById('image').style.borderColor = t && !doflicker ? 'red' : 'gray';\n"
+ "toggleVisible('image');\n"
+ "}" );
+ cl += QString ( "function runSlideShow(){\n"
+ " document.getElementById('image').src = pics[t].src;\n"
+ " if (doflicker)\n"
+ " t = 1 - t;\n"
+ " setTimeout('runSlideShow()', 200);\n"
+ "}\n"
+ "function m(b) { if (b == lastb) return; document.getElementById('b'+b).className='buttondown';\n"
+ " var e = document.getElementById('b'+lastb);\n"
+ " if(e) e.className='button';\n"
+ " lastb = b;\n"
+ "}\n"
+ "function showRender() { doflicker=0;toggleVisible('render')\n"
+ "}\n"
+ "function showDom() { doflicker=0;toggleVisible('dom')\n"
+ "}\n"
+ "</script>\n");
+
+ cl += QString ("<style>\n"
+ ".buttondown { cursor: pointer; padding: 0px 20px; color: white; background-color: blue; border: inset blue 2px;}\n"
+ ".button { cursor: pointer; padding: 0px 20px; color: black; background-color: white; border: outset blue 2px;}\n"
+ ".diff { position: absolute; left: 10px; top: 100px; visibility: hidden; border: 1px black solid; background-color: white; color: black; /* width: 800; height: 600; overflow: scroll; */ }\n"
+ "</style>\n" );
+
+ cl += QString( "<body onload=\"m(5); toggleVisible('dom');\"" );
+ cl += QString(" text=black bgcolor=gray>\n<h1>%3</h1>\n" ).arg( test );
+ if ( renderDiff.length() )
+ cl += "<span id='b4' class='button' onclick='showRender();m(4)'>R-DIFF</span>&nbsp;\n";
+ if ( domDiff.length() )
+ cl += "<span id='b5' class='button' onclick='showDom();m(5);'>D-DIFF</span>&nbsp;\n";
+ // The test file always exists - except for checkOutput called from *.js files
+ if ( QFile::exists( m_baseDir + "/tests/"+ test ) )
+ cl += QString( "<a class=button href=\"%1\">HTML</a>&nbsp;" )
+ .arg( relpath+"/tests/"+test );
+
+ cl += QString( "<hr>"
+ "<img style='border: solid 5px gray' src=\"%1\" id='image'>" )
+ .arg( relpath+"/baseline/"+test+"-dump.png" );
+
+ cl += "<div id='render' class='diff'>" + renderDiff + "</div>";
+ cl += "<div id='dom' class='diff'>" + domDiff + "</div>";
+
+ cl += "</body></html>";
+ compare.writeBlock( cl.latin1(), cl.length() );
+ compare.close();
+}
+
+void RegressionTest::testStaticFile(const QString & filename, const QStringList &commands)
+{
+ qApp->mainWidget()->resize( 800, 600); // restore size
+
+ // Set arguments
+ KParts::URLArgs args;
+ if (filename.endsWith(".txt")) args.serviceType = "text/plain";
+ m_part->browserExtension()->setURLArgs(args);
+ // load page
+ KURL url;
+ url.setProtocol("file");
+ url.setPath(QFileInfo(m_baseDir + "/tests/"+filename).absFilePath());
+ m_part->openURL(url);
+
+ // inject commands
+ for (QStringList::ConstIterator cit = commands.begin(); cit != commands.end(); ++cit) {
+ QString str = (*cit).stripWhiteSpace();
+ if (str.isEmpty() || str.startsWith("#")) continue;
+ Kate::Command *cmd = KateCmd::self()->queryCommand(str);
+ if (cmd) {
+ QString msg;
+ if (!cmd->exec(m_view, str, msg))
+ fprintf(stderr, "ERROR executing command '%s': %s\n", str.latin1(), msg.latin1());
+ }
+ }
+
+ pause(200);
+
+ Q_ASSERT(m_part->config()->configFlags() & KateDocumentConfig::cfDoxygenAutoTyping);
+
+ bool script_error = false;
+ {
+ // Execute script
+ TestJScriptEnv jsenv(m_part);
+ jsenv.output()->setChangedFlag(&m_outputCustomised);
+ jsenv.output()->setOutputString(&m_outputString);
+ script_error = evalJS(jsenv.interpreter(), m_baseDir + "/tests/"+QFileInfo(filename).dirPath()+"/.kateconfig-script", true)
+ && evalJS(jsenv.interpreter(), m_baseDir + "/tests/"+filename+"-script");
+ }
+
+ int back_known_failures = m_known_failures;
+
+ if (!script_error) goto bail_out;
+
+ if (m_showGui) kapp->processEvents();
+
+ if ( m_genOutput ) {
+ reportResult(checkOutput(filename+"-result"), "result");
+ } else {
+ int failures = NoFailure;
+
+ // compare with output file
+ if ( m_known_failures & ResultFailure)
+ m_known_failures = AllFailure;
+ bool newfail;
+ if ( !reportResult( checkOutput(filename+"-result"), "result", &newfail ) )
+ failures |= ResultFailure;
+ if (newfail)
+ failures |= NewFailure;
+
+ doFailureReport(filename, failures );
+ }
+
+bail_out:
+ m_known_failures = back_known_failures;
+ m_part->setModified(false);
+ m_part->closeURL();
+}
+
+bool RegressionTest::evalJS(Interpreter &interp, const QString &filename, bool ignore_nonexistent)
+{
+ QString fullSourceName = filename;
+ QFile sourceFile(fullSourceName);
+
+ if (!sourceFile.open(IO_ReadOnly)) {
+ if (!ignore_nonexistent) {
+ fprintf(stderr,"ERROR reading file %s\n",fullSourceName.latin1());
+ m_errors++;
+ }
+ return ignore_nonexistent;
+ }
+
+ QTextStream stream ( &sourceFile );
+ stream.setEncoding( QTextStream::UnicodeUTF8 );
+ QString code = stream.read();
+ sourceFile.close();
+
+ saw_failure = false;
+ ignore_errors = false;
+ Completion c = interp.evaluate(UString( code ) );
+
+ if ( /*report_result &&*/ !ignore_errors) {
+ if (c.complType() == Throw) {
+ QString errmsg = c.value().toString(interp.globalExec()).qstring();
+ printf( "ERROR: %s (%s)\n",filename.latin1(), errmsg.latin1());
+ m_errors++;
+ return false;
+ }
+ }
+ return true;
+}
+
+class GlobalImp : public ObjectImp {
+public:
+ virtual UString className() const { return "global"; }
+};
+
+RegressionTest::CheckResult RegressionTest::checkOutput(const QString &againstFilename)
+{
+ QString absFilename = QFileInfo(m_baseDir + "/baseline/" + againstFilename).absFilePath();
+ if ( svnIgnored( absFilename ) ) {
+ m_known_failures = NoFailure;
+ return Ignored;
+ }
+
+ CheckResult result = Success;
+
+ // compare result to existing file
+ QString outputFilename = QFileInfo(m_outputDir + "/" + againstFilename).absFilePath();
+ bool kf = false;
+ if ( m_known_failures & AllFailure )
+ kf = true;
+ if ( kf )
+ outputFilename += "-KF";
+
+ if ( m_genOutput )
+ outputFilename = absFilename;
+
+ // get existing content
+ QString data;
+ if (m_outputCustomised) {
+ data = m_outputString;
+ } else {
+ data = m_part->text();
+ }
+
+ QFile file(absFilename);
+ if (file.open(IO_ReadOnly)) {
+ QTextStream stream ( &file );
+ stream.setEncoding( QTextStream::UnicodeUTF8 );
+
+ QString fileData = stream.read();
+
+ result = ( fileData == data ) ? Success : Failure;
+ if ( !m_genOutput && result == Success && !m_keepOutput ) {
+ ::unlink( QFile::encodeName( outputFilename ) );
+ return Success;
+ }
+ } else if (!m_genOutput) {
+ fprintf(stderr, "Error reading file %s\n", absFilename.latin1());
+ result = Failure;
+ }
+
+ // generate result file
+ createMissingDirs( outputFilename );
+ QFile file2(outputFilename);
+ if (!file2.open(IO_WriteOnly)) {
+ fprintf(stderr,"Error writing to file %s\n",outputFilename.latin1());
+ exit(1);
+ }
+
+ QTextStream stream2(&file2);
+ stream2.setEncoding( QTextStream::UnicodeUTF8 );
+ stream2 << data;
+ if ( m_genOutput )
+ printf("Generated %s\n", outputFilename.latin1());
+
+ return result;
+}
+
+void RegressionTest::rereadConfig()
+{
+ m_baseConfig->setGroup("Kate Document Defaults");
+ m_part->config()->readConfig(m_baseConfig);
+ m_baseConfig->setGroup("Kate View Defaults");
+ m_view->config()->readConfig(m_baseConfig);
+}
+
+bool RegressionTest::reportResult(CheckResult result, const QString & description, bool *newfail)
+{
+ if ( result == Ignored ) {
+ //printf("IGNORED: ");
+ //printDescription( description );
+ return true; // no error
+ } else
+ return reportResult( result == Success, description, newfail );
+}
+
+bool RegressionTest::reportResult(bool passed, const QString & description, bool *newfail)
+{
+ if (newfail) *newfail = false;
+
+ if (m_genOutput)
+ return true;
+
+ QString filename(m_currentTest + "-" + description);
+ if (!m_currentCategory.isEmpty())
+ filename = m_currentCategory + "/" + filename;
+
+ const bool oldfailed = m_failureComp && m_failureComp->readNumEntry(filename);
+ if (passed) {
+ if ( m_known_failures & AllFailure ) {
+ printf("PASS (unexpected!)");
+ m_passes_fail++;
+ } else {
+ printf("PASS");
+ m_passes_work++;
+ }
+ if (oldfailed) {
+ printf(" (new)");
+ m_passes_new++;
+ }
+ if (m_failureSave)
+ m_failureSave->deleteEntry(filename);
+ }
+ else {
+ if ( m_known_failures & AllFailure ) {
+ printf("FAIL (known)");
+ m_failures_fail++;
+ passed = true; // we knew about it
+ } else {
+ printf("FAIL");
+ m_failures_work++;
+ }
+ if (!oldfailed && m_failureComp) {
+ printf(" (new)");
+ m_failures_new++;
+ if (newfail) *newfail = true;
+ }
+ if (m_failureSave)
+ m_failureSave->writeEntry(filename, 1);
+ }
+ printf(": ");
+
+ printDescription( description );
+ return passed;
+}
+
+void RegressionTest::printDescription(const QString& description)
+{
+ if (!m_currentCategory.isEmpty())
+ printf("%s/", m_currentCategory.latin1());
+
+ printf("%s", m_currentTest.latin1());
+
+ if (!description.isEmpty()) {
+ QString desc = description;
+ desc.replace( '\n', ' ' );
+ printf(" [%s]", desc.latin1());
+ }
+
+ printf("\n");
+ fflush(stdout);
+}
+
+void RegressionTest::createMissingDirs(const QString & filename)
+{
+ QFileInfo dif(filename);
+ QFileInfo dirInfo( dif.dirPath() );
+ if (dirInfo.exists())
+ return;
+
+ QStringList pathComponents;
+ QFileInfo parentDir = dirInfo;
+ pathComponents.prepend(parentDir.absFilePath());
+ while (!parentDir.exists()) {
+ QString parentPath = parentDir.absFilePath();
+ int slashPos = parentPath.findRev('/');
+ if (slashPos < 0)
+ break;
+ parentPath = parentPath.left(slashPos);
+ pathComponents.prepend(parentPath);
+ parentDir = QFileInfo(parentPath);
+ }
+ for (uint pathno = 1; pathno < pathComponents.count(); pathno++) {
+ if (!QFileInfo(pathComponents[pathno]).exists() &&
+ !QDir(pathComponents[pathno-1]).mkdir(pathComponents[pathno])) {
+ fprintf(stderr,"Error creating directory %s\n",pathComponents[pathno].latin1());
+ exit(1);
+ }
+ }
+}
+
+void RegressionTest::slotOpenURL(const KURL &url, const KParts::URLArgs &args)
+{
+ m_part->browserExtension()->setURLArgs( args );
+
+ m_part->openURL(url);
+}
+
+bool RegressionTest::svnIgnored( const QString &filename )
+{
+ QFileInfo fi( filename );
+ QString ignoreFilename = fi.dirPath() + "/svnignore";
+ QFile ignoreFile(ignoreFilename);
+ if (!ignoreFile.open(IO_ReadOnly))
+ return false;
+
+ QTextStream ignoreStream(&ignoreFile);
+ QString line;
+ while (!(line = ignoreStream.readLine()).isNull()) {
+ if ( line == fi.fileName() )
+ return true;
+ }
+ ignoreFile.close();
+ return false;
+}
+
+void RegressionTest::resizeTopLevelWidget( int w, int h )
+{
+ qApp->mainWidget()->resize( w, h );
+ // Since we're not visible, this doesn't have an immediate effect, QWidget posts the event
+ QApplication::sendPostedEvents( 0, QEvent::Resize );
+}
+
+#include "test_regression.moc"
+
+// kate: indent-width 4
diff --git a/kate/part/test_regression.h b/kate/part/test_regression.h
new file mode 100644
index 000000000..bc528b4bc
--- /dev/null
+++ b/kate/part/test_regression.h
@@ -0,0 +1,249 @@
+/**
+ * This file is part of the KDE project
+ *
+ * Copyright (C) 2001,2003 Peter Kelly (pmk@post.com)
+ * Copyright 2006 Leo Savernik (l.savernik@aon.at)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef TEST_REGRESSION_H
+#define TEST_REGRESSION_H
+
+#include <katejscript.h>
+#include <kateview.h>
+#include <kurl.h>
+#include <qobject.h>
+#include <qstringlist.h>
+#include <kjs/ustring.h>
+#include <kjs/object.h>
+#include <kjs/interpreter.h>
+
+class KateDocument;
+class KateView;
+class RegressionTest;
+class QTimer;
+
+namespace KParts {
+ class URLArgs;
+}
+
+class OutputObject;
+
+/**
+ * @internal
+ * The backbone of Kate's automatic regression tests.
+ */
+class TestJScriptEnv : public KateJScript
+{
+ public:
+ TestJScriptEnv(KateDocument *part);
+ virtual ~TestJScriptEnv();
+
+ /** returns the global scope */
+ KJS::Object global() const { return *m_global; }
+ /** returns the script interpreter */
+ KJS::Interpreter &interpreter() { return *m_interpreter; }
+ /** returns the document scope */
+ KJS::Object document() const { return *m_document; }
+ /** returns the view scope */
+ KJS::Object view() const { return *m_view; }
+ /** returns the output object */
+ OutputObject *output() const { return m_output; }
+
+ protected:
+ OutputObject *m_output;
+};
+
+/**
+ * @internal
+ */
+class KateViewObject : public KJS::ObjectImp
+{
+ public:
+ KateViewObject(KJS::ExecState *exec, KateView *v, KJS::ObjectImp *fallback);
+ virtual ~KateViewObject();
+
+ virtual const KJS::ClassInfo *classInfo() const;
+ virtual KJS::Value get(KJS::ExecState *exec, const KJS::Identifier &propertyName) const;
+
+ private:
+ // evil hack I: class layout of katejscript/KateJSView must be duplicated
+ // here, structurally as well as functionally
+ KateView *view;
+ // end evil hack
+ KJS::ObjectImp *fallback;
+};
+
+/**
+ * @internal
+ */
+class KateViewFunction : public KJS::ObjectImp
+{
+ public:
+ KateViewFunction(KJS::ExecState *exec, KateView *v, int _id, int length);
+
+ bool implementsCall() const;
+ KJS::Value call(KJS::ExecState *exec, KJS::Object &thisObj, const KJS::List &args);
+
+ enum { KeyReturn, Type, Backspace, DeleteWordLeft, KeyDelete,
+ DeleteWordRight, Transpose, CursorLeft, ShiftCursorLeft, CursorRight,
+ ShiftCursorRight, WordLeft, ShiftWordLeft, WordRight, ShiftWordRight,
+ Home, ShiftHome, End, ShiftEnd, Up, ShiftUp, Down, ShiftDown, ScrollUp,
+ ScrollDown, TopOfView, ShiftTopOfView, BottomOfView, ShiftBottomOfView,
+ PageUp, ShiftPageUp, PageDown, ShiftPageDown, Top, ShiftTop, Bottom,
+ ShiftBottom, ToMatchingBracket, ShiftToMatchingBracket };
+ private:
+ KateView *m_view;
+ int id;
+};
+
+class OutputFunction;
+
+/**
+ * Customizing output to result-files. Writing any output into result files
+ * inhibits outputting the content of the katepart after script execution, enabling one to check for coordinates and the like.
+ * @internal
+ */
+class OutputObject : public KJS::ObjectImp
+{
+ public:
+ OutputObject(KJS::ExecState *exec, KateDocument *d, KateView *v);
+ virtual ~OutputObject();
+
+ virtual KJS::UString className() const;
+
+ void setChangedFlag(bool *flag) { changed = flag; }
+ void setOutputString(QString *s) { outstr = s; }
+
+ private:
+ KateDocument *doc;
+ KateView *view;
+ bool *changed;
+ QString *outstr;
+
+ friend class OutputFunction;
+};
+
+/**
+ * Customizing output to result-files.
+ * @internal
+ */
+class OutputFunction : public KJS::ObjectImp
+{
+ public:
+ OutputFunction(KJS::ExecState *exec, OutputObject *obj, int _id, int length);
+
+ bool implementsCall() const;
+ virtual KJS::Value call(KJS::ExecState *exec, KJS::Object &thisObj, const KJS::List &args);
+
+ enum { Write, Writeln, WriteCursorPosition, WriteCursorPositionln };
+ private:
+ OutputObject *o;
+ int id;
+};
+
+/**
+ * @internal
+ */
+class RegressionTest : public QObject
+{
+ Q_OBJECT
+public:
+
+ RegressionTest(KateDocument *part, KConfig *baseConfig,
+ const QString &baseDir, const QString &outputDir,
+ bool _genOutput);
+ ~RegressionTest();
+
+ enum OutputType { ResultDocument };
+ void testStaticFile(const QString& filename, const QStringList &commands);
+ enum CheckResult { Failure = 0, Success = 1, Ignored = 2 };
+ CheckResult checkOutput(const QString& againstFilename);
+ enum FailureType { NoFailure = 0, AllFailure = 1, ResultFailure = 4, NewFailure = 65536 };
+ bool runTests(QString relPath = QString::null, bool mustExist = false, int known_failure = NoFailure);
+ bool reportResult( bool passed, const QString & description = QString::null, bool *newfailure = 0 );
+ bool reportResult(CheckResult result, const QString & description = QString::null, bool *newfailure = 0 );
+ void rereadConfig();
+ static void createMissingDirs(const QString &path);
+
+ void setFailureSnapshotConfig(KConfig *cfg, const QString &snapshotname);
+ void setFailureSnapshotSaver(KConfig *cfg, const QString &snapshotname);
+
+ void createLink( const QString& test, int failures );
+ void doFailureReport( const QString& test, int failures );
+
+ KateDocument *m_part;
+ KateView *m_view;
+ KConfig *m_baseConfig;
+ QString m_baseDir;
+ QString m_outputDir;
+ bool m_genOutput;
+ QString m_currentBase;
+ KConfig *m_failureComp;
+ KConfig *m_failureSave;
+
+ QString m_currentOutput;
+ QString m_currentCategory;
+ QString m_currentTest;
+
+ bool m_keepOutput;
+ bool m_getOutput;
+ bool m_showGui;
+ int m_passes_work;
+ int m_passes_fail;
+ int m_passes_new;
+ int m_failures_work;
+ int m_failures_fail;
+ int m_failures_new;
+ int m_errors;
+ bool saw_failure;
+ bool ignore_errors;
+ int m_known_failures;
+ bool m_outputCustomised;
+ QString m_outputString;
+
+ static RegressionTest *curr;
+
+private:
+ void printDescription(const QString& description);
+
+ static bool svnIgnored( const QString &filename );
+
+private:
+ /**
+ * evaluate script given by \c filename within the context of \c interp.
+ * @param ignore if \c true don't evaluate if script does not exist but
+ * return true nonetheless.
+ * @return true if script was valid, false otherwise
+ */
+ bool evalJS( KJS::Interpreter &interp, const QString &filename, bool ignore = false);
+ /**
+ * concatenate contents of all list files down to but not including the
+ * tests directory.
+ * @param relPath relative path against tests-directory
+ * @param filename file name of the list files
+ */
+ QStringList concatListFiles(const QString &relPath, const QString &filename);
+
+private slots:
+ void slotOpenURL(const KURL &url, const KParts::URLArgs &args);
+ void resizeTopLevelWidget( int, int );
+
+};
+
+#endif