TDE core libraries
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 
 
 
 

5184 rader
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, 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 &regexp, 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.