summaryrefslogtreecommitdiffstats
path: root/kate/part/katedocument.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kate/part/katedocument.cpp')
-rw-r--r--kate/part/katedocument.cpp5174
1 files changed, 5174 insertions, 0 deletions
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;