/* 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, TQT_SIGNAL(timeout()), TQT_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, TQT_SIGNAL(tagLines(int,int)), this, TQT_SLOT(tagLines(int,int)));
|
|
connect(m_buffer, TQT_SIGNAL(codeFoldingUpdated()),this,TQT_SIGNAL(codeFoldingUpdated()));
|
|
|
|
// if the user changes the highlight with the dialog, notify the doc
|
|
connect(KateHlManager::self(),TQT_SIGNAL(changed()),TQT_SLOT(internalHlChanged()));
|
|
|
|
// signal for the arbitrary HL
|
|
connect(m_arbitraryHL, TQT_SIGNAL(tagLines(KateView*, KateSuperRange*)), TQT_SLOT(tagArbitraryLines(KateView*, KateSuperRange*)));
|
|
|
|
// signals for mod on hd
|
|
connect( KateFactory::self()->dirWatch(), TQT_SIGNAL(dirty (const TQString &)),
|
|
this, TQT_SLOT(slotModOnHdDirty (const TQString &)) );
|
|
|
|
connect( KateFactory::self()->dirWatch(), TQT_SIGNAL(created (const TQString &)),
|
|
this, TQT_SLOT(slotModOnHdCreated (const TQString &)) );
|
|
|
|
connect( KateFactory::self()->dirWatch(), TQT_SIGNAL(deleted (const TQString &)),
|
|
this, TQT_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,TQT_SIGNAL(sigQueryClose(bool *, bool*)),this,TQT_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, TQT_SIGNAL(cursorPositionChanged()), TQT_SLOT(undoCancel()));
|
|
if ( s_fileChangedDialogsActivated )
|
|
connect( newView, TQT_SIGNAL(gotFocus( Kate::View * )), this, TQT_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("rightjust", size);
|
|
|
|
case 5:
|
|
return BarIcon("filesave", size);
|
|
|
|
case 6:
|
|
return BarIcon("source", size);
|
|
|
|
case 7:
|
|
return BarIcon("edit", size);
|
|
|
|
case 8:
|
|
return BarIcon("key_enter", size);
|
|
|
|
case 9:
|
|
return BarIcon("connect_established", size);
|
|
|
|
default:
|
|
return BarIcon("edit", size);
|
|
}
|
|
|
|
return BarIcon("edit", size);
|
|
}
|
|
//END
|
|
|
|
//BEGIN KTextEditor::EditInterface stuff
|
|
|
|
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, TQT_SIGNAL( data( TDEIO::Job*, const TQByteArray& ) ),
|
|
TQT_SLOT( slotDataKate( TDEIO::Job*, const TQByteArray& ) ) );
|
|
|
|
connect( m_job, TQT_SIGNAL( result( TDEIO::Job* ) ),
|
|
TQT_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 KIO
|
|
// we prevent file type and document variables from changing it
|
|
bool encodingSticky = m_encodingSticky;
|
|
m_encodingSticky = m_config->isSetEncoding();
|
|
|
|
// Try getting the filetype here, so that variables does not have to be reset.
|
|
int fileTypeFound = KateFactory::self()->fileTypeManager()->fileType (this);
|
|
if ( fileTypeFound > -1 )
|
|
updateFileType( fileTypeFound );
|
|
|
|
// 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 |