You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5183 lines
129 KiB
5183 lines
129 KiB
/* 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 <tdetexteditor/plugin.h>
|
|
|
|
#include <tdeio/job.h>
|
|
#include <tdeio/netaccess.h>
|
|
#include <tdeio/tdefileitem.h>
|
|
|
|
|
|
#include <tdeparts/event.h>
|
|
|
|
#include <tdelocale.h>
|
|
#include <tdeglobal.h>
|
|
#include <tdeapplication.h>
|
|
#include <tdepopupmenu.h>
|
|
#include <tdeconfig.h>
|
|
#include <tdefiledialog.h>
|
|
#include <tdemessagebox.h>
|
|
#include <kstdaction.h>
|
|
#include <kiconloader.h>
|
|
#include <kxmlguifactory.h>
|
|
#include <kdialogbase.h>
|
|
#include <kdebug.h>
|
|
#include <tdeglobalsettings.h>
|
|
#include <klibloader.h>
|
|
#include <kdirwatch.h>
|
|
#include <twin.h>
|
|
#include <kencodingfiledialog.h>
|
|
#include <tdetempfile.h>
|
|
#include <kmdcodec.h>
|
|
#include <kstandarddirs.h>
|
|
|
|
#include <tqtimer.h>
|
|
#include <tqfile.h>
|
|
#include <tqclipboard.h>
|
|
#include <tqtextstream.h>
|
|
#include <tqtextcodec.h>
|
|
#include <tqmap.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, TQWidget *parentWidget,
|
|
const char *widgetName, TQObject *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());
|
|
|
|
// tdetexteditor 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 TQTimer(this);
|
|
connect(m_undoMergeTimer, TQ_SIGNAL(timeout()), TQ_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, TQ_SIGNAL(tagLines(int,int)), this, TQ_SLOT(tagLines(int,int)));
|
|
connect(m_buffer, TQ_SIGNAL(codeFoldingUpdated()),this,TQ_SIGNAL(codeFoldingUpdated()));
|
|
|
|
// if the user changes the highlight with the dialog, notify the doc
|
|
connect(KateHlManager::self(),TQ_SIGNAL(changed()),TQ_SLOT(internalHlChanged()));
|
|
|
|
// signal for the arbitrary HL
|
|
connect(m_arbitraryHL, TQ_SIGNAL(tagLines(KateView*, KateSuperRange*)), TQ_SLOT(tagArbitraryLines(KateView*, KateSuperRange*)));
|
|
|
|
// signals for mod on hd
|
|
connect( KateFactory::self()->dirWatch(), TQ_SIGNAL(dirty (const TQString &)),
|
|
this, TQ_SLOT(slotModOnHdDirty (const TQString &)) );
|
|
|
|
connect( KateFactory::self()->dirWatch(), TQ_SIGNAL(created (const TQString &)),
|
|
this, TQ_SLOT(slotModOnHdCreated (const TQString &)) );
|
|
|
|
connect( KateFactory::self()->dirWatch(), TQ_SIGNAL(deleted (const TQString &)),
|
|
this, TQ_SLOT(slotModOnHdDeleted (const TQString &)) );
|
|
|
|
// 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,TQ_SIGNAL(sigQueryClose(bool *, bool*)),this,TQ_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 (TQFile::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( TQWidget *parent, const char *name )
|
|
{
|
|
KateView* newView = new KateView( this, parent, name);
|
|
connect(newView, TQ_SIGNAL(cursorPositionChanged()), TQ_SLOT(undoCancel()));
|
|
if ( s_fileChangedDialogsActivated )
|
|
connect( newView, TQ_SIGNAL(gotFocus( Kate::View * )), this, TQ_SLOT(slotModifiedOnDisk()) );
|
|
return newView;
|
|
}
|
|
|
|
TQPtrList<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, TQWidget *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;
|
|
}
|
|
|
|
TQString 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 TQString ("");
|
|
}
|
|
|
|
return TQString ("");
|
|
}
|
|
|
|
TQString 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 TQString ("");
|
|
}
|
|
|
|
return TQString ("");
|
|
}
|
|
|
|
TQPixmap 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("format-justify-right", size);
|
|
|
|
case 5:
|
|
return BarIcon("document-save", size);
|
|
|
|
case 6:
|
|
return BarIcon("text-x-src", 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
|
|
|
|
TQString KateDocument::text() const
|
|
{
|
|
TQString 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;
|
|
}
|
|
|
|
TQString KateDocument::text ( uint startLine, uint startCol, uint endLine, uint endCol ) const
|
|
{
|
|
return text(startLine, startCol, endLine, endCol, false);
|
|
}
|
|
|
|
TQString KateDocument::text ( uint startLine, uint startCol, uint endLine, uint endCol, bool blockwise) const
|
|
{
|
|
if ( blockwise && (startCol > endCol) )
|
|
return TQString ();
|
|
|
|
TQString s;
|
|
|
|
if (startLine == endLine)
|
|
{
|
|
if (startCol > endCol)
|
|
return TQString ();
|
|
|
|
KateTextLine::Ptr textLine = m_buffer->plainLine(startLine);
|
|
|
|
if ( !textLine )
|
|
return TQString ();
|
|
|
|
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;
|
|
}
|
|
|
|
TQString KateDocument::textLine( uint line ) const
|
|
{
|
|
KateTextLine::Ptr l = m_buffer->plainLine(line);
|
|
|
|
if (!l)
|
|
return TQString();
|
|
|
|
return l->string();
|
|
}
|
|
|
|
bool KateDocument::setText(const TQString &s)
|
|
{
|
|
if (!isReadWrite())
|
|
return false;
|
|
|
|
TQPtrList<KTextEditor::Mark> m = marks ();
|
|
TQValueList<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 TQString &s)
|
|
{
|
|
return insertText (line, col, s, false);
|
|
}
|
|
|
|
bool KateDocument::insertText( uint line, uint col, const TQString &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();
|
|
|
|
TQString 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++)
|
|
{
|
|
TQChar 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 TQString &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 TQChar *text = l->text();
|
|
uint eolPosition = l->length()-1;
|
|
|
|
// take tabs into account here, too
|
|
uint x = 0;
|
|
const TQString & t = l->string();
|
|
uint z2 = 0;
|
|
for ( ; z2 < l->length(); z2++)
|
|
{
|
|
if (t[z2] == TQChar('\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, TQString (" "));
|
|
|
|
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 TQString &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 TQString &str )
|
|
{
|
|
if (!isReadWrite())
|
|
return false;
|
|
|
|
TQString 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, TQString().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( TQPtrListIterator<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( TQPtrListIterator<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, TQString::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);
|
|
|
|
TQPtrList<KTextEditor::Mark> list;
|
|
for( TQIntDictIterator<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( TQPtrListIterator<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( TQPtrListIterator<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);
|
|
}
|
|
|
|
TQPtrList<KTextEditor::Mark> list;
|
|
for( TQIntDictIterator<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( TQPtrListIterator<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( TQPtrListIterator<KateSuperCursor> it (m_superCursors); it.current(); ++it )
|
|
it.current()->editLineUnWrapped (line, col, removeLine, length);
|
|
|
|
editEnd ();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool KateDocument::editInsertLine ( uint line, const TQString &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
|
|
|
|
TQPtrList<KTextEditor::Mark> list;
|
|
for( TQIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it )
|
|
{
|
|
if( it.current()->line >= line )
|
|
list.append( it.current() );
|
|
}
|
|
|
|
for( TQPtrListIterator<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( TQPtrListIterator<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);
|
|
|
|
TQPtrList<KTextEditor::Mark> list;
|
|
KTextEditor::Mark* rmark = 0;
|
|
for( TQIntDictIterator<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( TQPtrListIterator<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( TQPtrListIterator<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 ();
|
|
}
|
|
|
|
TQPtrList<KTextEditor::Cursor> KateDocument::cursors () const
|
|
{
|
|
return myCursors;
|
|
}
|
|
//END
|
|
|
|
//BEGIN KTextEditor::SearchInterface stuff
|
|
|
|
bool KateDocument::searchText (unsigned int startLine, unsigned int startCol, const TQString &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 TQRegExp ®exp, unsigned int *foundAtLine, unsigned int *foundAtCol, unsigned int *matchLen, bool backwards)
|
|
{
|
|
kdDebug(13020)<<"KateDocument::searchText( "<<startLine<<", "<<startCol<<", "<<TQString(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();
|
|
}
|
|
|
|
TQString KateDocument::hlModeName (uint mode)
|
|
{
|
|
return KateHlManager::self()->hlName (mode);
|
|
}
|
|
|
|
TQString KateDocument::hlModeSectionName (uint mode)
|
|
{
|
|
return KateHlManager::self()->hlSection (mode);
|
|
}
|
|
|
|
void KateDocument::setDontChangeHlOnSave()
|
|
{
|
|
hlSetByUser = true;
|
|
}
|
|
//END
|
|
|
|
//BEGIN KTextEditor::ConfigInterface stuff
|
|
void KateDocument::readConfig(TDEConfig *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(TDEConfig *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()
|
|
{
|
|
TDEConfig *config = kapp->config();
|
|
readConfig (config);
|
|
}
|
|
|
|
void KateDocument::writeConfig()
|
|
{
|
|
TDEConfig *config = kapp->config();
|
|
writeConfig (config);
|
|
config->sync();
|
|
}
|
|
|
|
void KateDocument::readSessionConfig(TDEConfig *tdeconfig)
|
|
{
|
|
// restore the url
|
|
KURL url (tdeconfig->readEntry("URL"));
|
|
|
|
// get the encoding
|
|
TQString tmpenc=tdeconfig->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(tdeconfig->readEntry("Highlighting")));
|
|
|
|
if (hlMode() > 0)
|
|
hlSetByUser = true;
|
|
|
|
// indent mode
|
|
config()->setIndentationMode( (uint)tdeconfig->readNumEntry("Indentation Mode", config()->indentationMode() ) );
|
|
|
|
// Restore Bookmarks
|
|
TQValueList<int> marks = tdeconfig->readIntListEntry("Bookmarks");
|
|
for( uint i = 0; i < marks.count(); i++ )
|
|
addMark( marks[i], KateDocument::markType01 );
|
|
}
|
|
|
|
void KateDocument::writeSessionConfig(TDEConfig *tdeconfig)
|
|
{
|
|
if ( m_url.isLocalFile() && !TDEGlobal::dirs()->relativeLocation("tmp", m_url.path()).startsWith("/"))
|
|
return;
|
|
// save url
|
|
tdeconfig->writeEntry("URL", m_url.prettyURL() );
|
|
|
|
// save encoding
|
|
tdeconfig->writeEntry("Encoding",encoding());
|
|
|
|
// save hl
|
|
tdeconfig->writeEntry("Highlighting", highlight()->name());
|
|
|
|
tdeconfig->writeEntry("Indentation Mode", config()->indentationMode() );
|
|
|
|
// Save Bookmarks
|
|
TQValueList<int> marks;
|
|
for( TQIntDictIterator<KTextEditor::Mark> it( m_marks );
|
|
it.current() && it.current()->type & KTextEditor::MarkInterface::markType01;
|
|
++it )
|
|
marks << it.current()->line;
|
|
|
|
tdeconfig->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
|
|
|
|
TQPtrList<KTextEditor::ConfigPage> editorPages;
|
|
|
|
for (uint i = 0; i < KTextEditor::configInterfaceExtension (this)->configPages (); i++)
|
|
{
|
|
TQStringList path;
|
|
path.clear();
|
|
path << KTextEditor::configInterfaceExtension (this)->configPageName (i);
|
|
TQVBox *page = kd->addVBoxPage(path, KTextEditor::configInterfaceExtension (this)->configPageFullName (i),
|
|
KTextEditor::configInterfaceExtension (this)->configPagePixmap(i, TDEIcon::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);
|
|
}
|
|
|
|
TQPtrList<KTextEditor::Mark> KateDocument::marks()
|
|
{
|
|
TQPtrList<KTextEditor::Mark> list;
|
|
|
|
for( TQIntDictIterator<KTextEditor::Mark> it( m_marks );
|
|
it.current(); ++it ) {
|
|
list.append( it.current() );
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
void KateDocument::clearMarks()
|
|
{
|
|
for( TQIntDictIterator<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 TQPixmap& pixmap )
|
|
{
|
|
m_markPixmaps.replace( type, new TQPixmap( pixmap ) );
|
|
}
|
|
|
|
void KateDocument::setDescription( MarkInterface::MarkTypes type, const TQString& description )
|
|
{
|
|
m_markDescriptions.replace( type, new TQString( description ) );
|
|
}
|
|
|
|
TQPixmap *KateDocument::markPixmap( MarkInterface::MarkTypes type )
|
|
{
|
|
return m_markPixmaps[type];
|
|
}
|
|
|
|
TQColor 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 TQColor();
|
|
}
|
|
}
|
|
|
|
TQString KateDocument::markDescription( MarkInterface::MarkTypes type )
|
|
{
|
|
if( m_markDescriptions[type] )
|
|
return *m_markDescriptions[type];
|
|
return TQString::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)
|
|
TQString 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
|
|
TQString KateDocument::niceFileSize()
|
|
{
|
|
return "UNKNOWN";
|
|
}
|
|
|
|
KMimeType::Ptr KateDocument::mimeTypeForContent()
|
|
{
|
|
TQByteArray buf (1024);
|
|
uint bufpos = 0;
|
|
|
|
for (uint i=0; i < numLines(); i++)
|
|
{
|
|
TQString 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 = TDEIO::get ( url, false, isProgressInfoEnabled() );
|
|
|
|
// connect to slots
|
|
connect( m_job, TQ_SIGNAL( data( TDEIO::Job*, const TQByteArray& ) ),
|
|
TQ_SLOT( slotDataKate( TDEIO::Job*, const TQByteArray& ) ) );
|
|
|
|
connect( m_job, TQ_SIGNAL( result( TDEIO::Job* ) ),
|
|
TQ_SLOT( slotFinishedKate( TDEIO::Job* ) ) );
|
|
|
|
TQWidget *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 ( TDEIO::Job *, const TQByteArray &data )
|
|
{
|
|
// kdDebug(13020) << "KateDocument::slotData" << endl;
|
|
|
|
if (!m_tempFile || !m_tempFile->file())
|
|
return;
|
|
|
|
m_tempFile->file()->writeBlock (data);
|
|
}
|
|
|
|
void KateDocument::slotFinishedKate ( TDEIO::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(TDEIO::Job * job)
|
|
{
|
|
m_loading = true;
|
|
// add new m_file to dirwatch
|
|
activateDirWatch ();
|
|
|
|
//
|
|
// use metadata
|
|
//
|
|
if (job)
|
|
{
|
|
TQString 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
|
|
//
|
|
TQString 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 TDEIO
|
|
// 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 );
|
|
|
|
// read dir config (if possible and wanted)
|
|
if (!m_reloading)
|
|
readDirConfig ();
|
|
|
|
// 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 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 (TQString::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;
|
|
TDEIO::UDSEntry fentry;
|
|
if (TDEIO::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 ( (!TDEIO::NetAccess::exists( u, false, kapp->mainWidget() ) || TDEIO::NetAccess::del( u, kapp->mainWidget() ))
|
|
&& TDEIO::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)
|
|
{
|
|
TQString 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 )
|
|
{
|
|
TQString oldDir = url().directory();
|
|
|
|
if ( KParts::ReadWritePart::saveAs( u ) )
|
|
{
|
|
// null means base on filename
|
|
setDocName( TQString::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))
|
|
{
|
|
TQString currentDir = TQFileInfo (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
|
|
TQFile f (currentDir + "/.kateconfig");
|
|
|
|
if (f.open (IO_ReadOnly))
|
|
{
|
|
TQTextStream stream (&f);
|
|
|
|
uint linesRead = 0;
|
|
TQString line = stream.readLine();
|
|
while ((linesRead < 32) && !line.isNull())
|
|
{
|
|
readVariableLine( line );
|
|
|
|
line = stream.readLine();
|
|
|
|
linesRead++;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
TQString newDir = TQFileInfo (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 = TQString::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"),
|
|
TQString("kate_close_modonhd_%1").arg( m_modOnHdReason ) ) == KMessageBox::Continue))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//
|
|
// first call the normal tdeparts implementation
|
|
//
|
|
if (!KParts::ReadWritePart::closeURL ())
|
|
return false;
|
|
|
|
// remove file from dirwatch
|
|
deactivateDirWatch ();
|
|
|
|
//
|
|
// empty url + filename
|
|
//
|
|
m_url = KURL ();
|
|
m_file = TQString::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 TQWidget::scroll()ed.
|
|
view->setCursorPositionInternal(0, 0, 1, false);
|
|
view->clearSelection();
|
|
view->updateView(true);
|
|
}
|
|
|
|
// uh, filename changed
|
|
emit fileNameChanged ();
|
|
|
|
// update doc name
|
|
setDocName (TQString::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 TQString &chars )
|
|
{
|
|
KateTextLine::Ptr textLine = m_buffer->plainLine(view->cursorLine ());
|
|
|
|
if (!textLine)
|
|
return false;
|
|
|
|
bool bracketInserted = false;
|
|
TQString buf;
|
|
TQChar c;
|
|
|
|
for( uint z = 0; z < chars.length(); z++ )
|
|
{
|
|
TQChar ch = c = chars[z];
|
|
if (ch.isPrint() || ch == '\t')
|
|
{
|
|
buf.append (ch);
|
|
|
|
if (!bracketInserted && (config()->configFlags() & KateDocument::cfAutoBrackets))
|
|
{
|
|
TQChar end_ch;
|
|
bool complete = true;
|
|
TQChar prevChar = textLine->getChar(view->cursorColumnReal()-1);
|
|
TQChar 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();
|
|
TQString 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;
|
|
TQChar prevChar = tl->getChar(col-1);
|
|
TQChar 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(TQString::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 )
|
|
{
|
|
TQString s = TQApplication::clipboard()->text();
|
|
|
|
if (s.isEmpty())
|
|
return;
|
|
|
|
uint lines = s.contains (TQChar ('\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 ();
|
|
|
|
TQString 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;
|
|
TQString 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);
|
|
|
|
TQString 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, TQString &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, TQString &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)
|
|
{
|
|
TQString commentLineMark = highlight()->getCommentSingleLineStart( attrib ) + " ";
|
|
insertText (line, 0, commentLineMark);
|
|
}
|
|
else
|
|
{
|
|
TQString 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 )
|
|
{
|
|
TQString shortCommentMark = highlight()->getCommentSingleLineStart( attrib );
|
|
TQString 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 )
|
|
{
|
|
TQString startCommentMark = highlight()->getCommentStart( attrib ) + " ";
|
|
TQString 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 )
|
|
{
|
|
TQString shortStartCommentMark = highlight()->getCommentStart( attrib );
|
|
TQString longStartCommentMark = shortStartCommentMark + " ";
|
|
TQString shortStopCommentMark = highlight()->getCommentEnd( attrib );
|
|
TQString 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 )
|
|
{
|
|
TQString startComment = highlight()->getCommentStart( attrib );
|
|
TQString 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 )
|
|
{
|
|
TQString 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 )
|
|
{
|
|
TQString startComment = highlight()->getCommentStart( attrib );
|
|
TQString 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)
|
|
{
|
|
TQString startComment = highlight()->getCommentStart( attrib );
|
|
TQString 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 )
|
|
{
|
|
TQString shortCommentMark = highlight()->getCommentSingleLineStart( attrib );
|
|
TQString 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;
|
|
}
|
|
TQString s = text( ln, start, ln, end );
|
|
TQString 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
|
|
TQString o = text( cl, cc, cl, cc + 1 );
|
|
TQString 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();
|
|
}
|
|
|
|
TQString 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 TQString("");
|
|
|
|
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 TQString(&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 TQChar& c ) { return c == '{' || c == '[' || c == '('; }
|
|
inline bool isEndBracket ( const TQChar& c ) { return c == '}' || c == ']' || c == ')'; }
|
|
inline bool isBracket ( const TQChar& 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;
|
|
|
|
TQChar right = textLine->getChar( start.col() );
|
|
TQChar left = textLine->getChar( start.col() - 1 );
|
|
TQChar 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;
|
|
}
|
|
|
|
TQChar 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 */
|
|
TQChar 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 (TQString 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 = TQString(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(),TQString::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:
|
|
TQString 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;
|
|
}
|
|
}
|
|
|
|
TQValueList<KateDocumentTmpMark> tmp;
|
|
|
|
for( TQIntDictIterator<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;
|
|
|
|
TQValueList<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 ( TQValueList<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 TQString &e)
|
|
{
|
|
if ( m_encodingSticky )
|
|
return;
|
|
|
|
TQString ce = m_config->encoding().lower();
|
|
if ( e.lower() == ce )
|
|
return;
|
|
|
|
m_config->setEncoding( e );
|
|
if ( ! m_loading )
|
|
reloadFile();
|
|
}
|
|
|
|
TQString 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
|
|
*/
|
|
TQRegExp KateDocument::kvLine = TQRegExp("kate:(.*)");
|
|
TQRegExp KateDocument::kvLineWildcard = TQRegExp("kate-wildcard\\((.*)\\):(.*)");
|
|
TQRegExp KateDocument::kvLineMime = TQRegExp("kate-mimetype\\((.*)\\):(.*)");
|
|
TQRegExp KateDocument::kvVar = TQRegExp("([\\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( TQString t, bool onlyViewAndRenderer )
|
|
{
|
|
// simple check first, no regex
|
|
// no kate inside, no vars, simple...
|
|
if (t.find("kate") < 0)
|
|
return;
|
|
|
|
// found vars, if any
|
|
TQString 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
|
|
{
|
|
TQStringList wildcards (TQStringList::split(';', kvLineWildcard.cap(1)));
|
|
TQString nameOfFile = url().fileName();
|
|
|
|
bool found = false;
|
|
for (TQStringList::size_type i = 0; !found && i < wildcards.size(); ++i)
|
|
{
|
|
TQRegExp wildcard (wildcards[i], true/*TQt::CaseSensitive*/, true/*TQRegExp::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
|
|
{
|
|
TQStringList types (TQStringList::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;
|
|
}
|
|
|
|
TQStringList 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 );
|
|
|
|
TQString var, val;
|
|
while ( (p = kvVar.search( s, p )) > -1 )
|
|
{
|
|
p += kvVar.matchedLength();
|
|
var = kvVar.cap( 1 );
|
|
val = TQString(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" )
|
|
{
|
|
TQStringList 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( TQString var, TQString val )
|
|
{
|
|
KateView *v;
|
|
bool state;
|
|
int n;
|
|
TQColor 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 ) ) )
|
|
{
|
|
TQFont _f( *v->renderer()->config()->font( ) );
|
|
|
|
if ( var == "font" )
|
|
{
|
|
_f.setFamily( val );
|
|
_f.setFixedPitch( TQFont( 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( TQString val, bool *result )
|
|
{
|
|
val = val.stripWhiteSpace().lower();
|
|
TQStringList 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( TQString val, int *result )
|
|
{
|
|
bool ret( false );
|
|
*result = val.toInt( &ret );
|
|
return ret;
|
|
}
|
|
|
|
bool KateDocument::checkColorValue( TQString val, TQColor &c )
|
|
{
|
|
c.setNamedColor( val );
|
|
return c.isValid();
|
|
}
|
|
|
|
// KTextEditor::variable
|
|
TQString KateDocument::variable( const TQString &name ) const
|
|
{
|
|
if ( m_storedVariables.contains( name ) )
|
|
return m_storedVariables[ name ];
|
|
|
|
return "";
|
|
}
|
|
|
|
//END
|
|
|
|
void KateDocument::slotModOnHdDirty (const TQString &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() )
|
|
{
|
|
TQCString 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 TQString &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 TQString &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( TQCString &result )
|
|
{
|
|
bool ret = false;
|
|
result = "";
|
|
if ( url().isLocalFile() )
|
|
{
|
|
TQFile f ( url().path() );
|
|
if ( f.open( IO_ReadOnly) )
|
|
{
|
|
KMD5 md5;
|
|
ret = md5.update( f );
|
|
md5.hexDigest( result );
|
|
f.close();
|
|
ret = true;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
TQString 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 TQString();
|
|
}
|
|
}
|
|
|
|
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 ();
|
|
}
|
|
|
|
long KateDocument::documentListPosition () const
|
|
{
|
|
return KTextEditor::Document::documentListPosition ();
|
|
}
|
|
|
|
void KateDocument::setDocumentListPosition (long pos)
|
|
{
|
|
KTextEditor::Document::setDocumentListPosition (pos);
|
|
}
|
|
|
|
|
|
|
|
void KateDocument::slotQueryClose_save(bool *handled, bool* abortClosing) {
|
|
*handled=true;
|
|
*abortClosing=true;
|
|
if (m_url.isEmpty())
|
|
{
|
|
KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveURLAndEncoding(config()->encoding(),
|
|
TQString::null,TQString::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;
|
|
|
|
TQFileInfo 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 TQString &encoding)
|
|
{
|
|
s_defaultEncoding = encoding;
|
|
}
|
|
|
|
//BEGIN KTextEditor::TemplateInterface
|
|
bool KateDocument::insertTemplateTextImplementation ( uint line, uint column, const TQString &templateString, const TQMap<TQString,TQString> &initialValues, TQWidget *) {
|
|
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}",TQMap<TQString,TQString>());
|
|
}
|
|
|
|
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; }
|
|
|
|
TQString KateDocument::selection () const
|
|
{ if (m_activeView) return m_activeView->selection (); return TQString(""); }
|
|
|
|
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
|