• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • kate
 

kate

  • kate
  • part
katedocument.cpp
1/* This file is part of the KDE libraries
2 Copyright (C) 2001-2004 Christoph Cullmann <cullmann@kde.org>
3 Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
4 Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License version 2 as published by the Free Software Foundation.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02111-13020, USA.
19*/
20
21//BEGIN includes
22#include "katedocument.h"
23#include "katedocument.moc"
24#include "katekeyinterceptorfunctor.h"
25#include "katefactory.h"
26#include "katedialogs.h"
27#include "katehighlight.h"
28#include "kateview.h"
29#include "katesearch.h"
30#include "kateautoindent.h"
31#include "katetextline.h"
32#include "katedocumenthelpers.h"
33#include "kateprinter.h"
34#include "katelinerange.h"
35#include "katesupercursor.h"
36#include "katearbitraryhighlight.h"
37#include "katerenderer.h"
38#include "kateattribute.h"
39#include "kateconfig.h"
40#include "katefiletype.h"
41#include "kateschema.h"
42#include "katetemplatehandler.h"
43#include <tdetexteditor/plugin.h>
44
45#include <tdeio/job.h>
46#include <tdeio/netaccess.h>
47#include <tdeio/tdefileitem.h>
48
49
50#include <tdeparts/event.h>
51
52#include <tdelocale.h>
53#include <tdeglobal.h>
54#include <tdeapplication.h>
55#include <tdepopupmenu.h>
56#include <tdeconfig.h>
57#include <tdefiledialog.h>
58#include <tdemessagebox.h>
59#include <kstdaction.h>
60#include <kiconloader.h>
61#include <kxmlguifactory.h>
62#include <kdialogbase.h>
63#include <kdebug.h>
64#include <tdeglobalsettings.h>
65#include <klibloader.h>
66#include <kdirwatch.h>
67#include <twin.h>
68#include <kencodingfiledialog.h>
69#include <tdetempfile.h>
70#include <kmdcodec.h>
71#include <kstandarddirs.h>
72
73#include <tqtimer.h>
74#include <tqfile.h>
75#include <tqclipboard.h>
76#include <tqtextstream.h>
77#include <tqtextcodec.h>
78#include <tqmap.h>
79//END includes
80
81//BEGIN PRIVATE CLASSES
82class KatePartPluginItem
83{
84 public:
85 KTextEditor::Plugin *plugin;
86};
87//END PRIVATE CLASSES
88
89//BEGIN d'tor, c'tor
90//
91// KateDocument Constructor
92//
93KateDocument::KateDocument ( bool bSingleViewMode, bool bBrowserView,
94 bool bReadOnly, TQWidget *parentWidget,
95 const char *widgetName, TQObject *parent, const char *name)
96: Kate::Document(parent, name),
97 m_plugins (KateFactory::self()->plugins().count()),
98 m_undoDontMerge(false),
99 m_undoIgnoreCancel(false),
100 lastUndoGroupWhenSaved( 0 ),
101 lastRedoGroupWhenSaved( 0 ),
102 docWasSavedWhenUndoWasEmpty( true ),
103 docWasSavedWhenRedoWasEmpty( true ),
104 m_modOnHd (false),
105 m_modOnHdReason (0),
106 m_job (0),
107 m_tempFile (0),
108 m_tabInterceptor(0)
109{
110 m_undoComplexMerge=false;
111 m_isInUndo = false;
112 // my dcop object
113 setObjId ("KateDocument#"+documentDCOPSuffix());
114
115 // tdetexteditor interfaces
116 setBlockSelectionInterfaceDCOPSuffix (documentDCOPSuffix());
117 setConfigInterfaceDCOPSuffix (documentDCOPSuffix());
118 setConfigInterfaceExtensionDCOPSuffix (documentDCOPSuffix());
119 setCursorInterfaceDCOPSuffix (documentDCOPSuffix());
120 setEditInterfaceDCOPSuffix (documentDCOPSuffix());
121 setEncodingInterfaceDCOPSuffix (documentDCOPSuffix());
122 setHighlightingInterfaceDCOPSuffix (documentDCOPSuffix());
123 setMarkInterfaceDCOPSuffix (documentDCOPSuffix());
124 setMarkInterfaceExtensionDCOPSuffix (documentDCOPSuffix());
125 setPrintInterfaceDCOPSuffix (documentDCOPSuffix());
126 setSearchInterfaceDCOPSuffix (documentDCOPSuffix());
127 setSelectionInterfaceDCOPSuffix (documentDCOPSuffix());
128 setSelectionInterfaceExtDCOPSuffix (documentDCOPSuffix());
129 setSessionConfigInterfaceDCOPSuffix (documentDCOPSuffix());
130 setUndoInterfaceDCOPSuffix (documentDCOPSuffix());
131 setWordWrapInterfaceDCOPSuffix (documentDCOPSuffix());
132
133 // init local plugin array
134 m_plugins.fill (0);
135
136 // register doc at factory
137 KateFactory::self()->registerDocument (this);
138
139 m_reloading = false;
140 m_loading = false;
141 m_encodingSticky = false;
142
143 m_buffer = new KateBuffer (this);
144
145 // init the config object, be careful not to use it
146 // until the initial readConfig() call is done
147 m_config = new KateDocumentConfig (this);
148
149 // init some more vars !
150 m_activeView = 0L;
151
152 hlSetByUser = false;
153 m_fileType = -1;
154 m_fileTypeSetByUser = false;
155 setInstance( KateFactory::self()->instance() );
156
157 editSessionNumber = 0;
158 editIsRunning = false;
159 m_editCurrentUndo = 0L;
160 editWithUndo = false;
161
162 m_docNameNumber = 0;
163
164 m_bSingleViewMode = bSingleViewMode;
165 m_bBrowserView = bBrowserView;
166 m_bReadOnly = bReadOnly;
167
168 m_marks.setAutoDelete( true );
169 m_markPixmaps.setAutoDelete( true );
170 m_markDescriptions.setAutoDelete( true );
171 setMarksUserChangable( markType01 );
172
173 m_undoMergeTimer = new TQTimer(this);
174 connect(m_undoMergeTimer, TQ_SIGNAL(timeout()), TQ_SLOT(undoCancel()));
175
176 clearMarks ();
177 clearUndo ();
178 clearRedo ();
179 setModified (false);
180 docWasSavedWhenUndoWasEmpty = true;
181
182 // normal hl
183 m_buffer->setHighlight (0);
184
185 m_extension = new KateBrowserExtension( this );
186 m_arbitraryHL = new KateArbitraryHighlight();
187 m_indenter = KateAutoIndent::createIndenter ( this, 0 );
188
189 m_indenter->updateConfig ();
190
191 // some nice signals from the buffer
192 connect(m_buffer, TQ_SIGNAL(tagLines(int,int)), this, TQ_SLOT(tagLines(int,int)));
193 connect(m_buffer, TQ_SIGNAL(codeFoldingUpdated()),this,TQ_SIGNAL(codeFoldingUpdated()));
194
195 // if the user changes the highlight with the dialog, notify the doc
196 connect(KateHlManager::self(),TQ_SIGNAL(changed()),TQ_SLOT(internalHlChanged()));
197
198 // signal for the arbitrary HL
199 connect(m_arbitraryHL, TQ_SIGNAL(tagLines(KateView*, KateSuperRange*)), TQ_SLOT(tagArbitraryLines(KateView*, KateSuperRange*)));
200
201 // signals for mod on hd
202 connect( KateFactory::self()->dirWatch(), TQ_SIGNAL(dirty (const TQString &)),
203 this, TQ_SLOT(slotModOnHdDirty (const TQString &)) );
204
205 connect( KateFactory::self()->dirWatch(), TQ_SIGNAL(created (const TQString &)),
206 this, TQ_SLOT(slotModOnHdCreated (const TQString &)) );
207
208 connect( KateFactory::self()->dirWatch(), TQ_SIGNAL(deleted (const TQString &)),
209 this, TQ_SLOT(slotModOnHdDeleted (const TQString &)) );
210
211 // update doc name
212 setDocName ("");
213
214 // if single view mode, like in the konqui embedding, create a default view ;)
215 if ( m_bSingleViewMode )
216 {
217 KTextEditor::View *view = createView( parentWidget, widgetName );
218 insertChildClient( view );
219 view->show();
220 setWidget( view );
221 }
222
223 connect(this,TQ_SIGNAL(sigQueryClose(bool *, bool*)),this,TQ_SLOT(slotQueryClose_save(bool *, bool*)));
224
225 m_isasking = 0;
226
227 // plugins
228 for (uint i=0; i<KateFactory::self()->plugins().count(); i++)
229 {
230 if (config()->plugin (i))
231 loadPlugin (i);
232 }
233}
234
235//
236// KateDocument Destructor
237//
238KateDocument::~KateDocument()
239{
240 // remove file from dirwatch
241 deactivateDirWatch ();
242
243 if (!singleViewMode())
244 {
245 // clean up remaining views
246 m_views.setAutoDelete( true );
247 m_views.clear();
248 }
249
250 delete m_editCurrentUndo;
251
252 delete m_arbitraryHL;
253
254 // cleanup the undo items, very important, truee :/
255 undoItems.setAutoDelete(true);
256 undoItems.clear();
257
258 // clean up plugins
259 unloadAllPlugins ();
260
261 delete m_config;
262 delete m_indenter;
263 KateFactory::self()->deregisterDocument (this);
264}
265//END
266
267//BEGIN Plugins
268void KateDocument::unloadAllPlugins ()
269{
270 for (uint i=0; i<m_plugins.count(); i++)
271 unloadPlugin (i);
272}
273
274void KateDocument::enableAllPluginsGUI (KateView *view)
275{
276 for (uint i=0; i<m_plugins.count(); i++)
277 enablePluginGUI (m_plugins[i], view);
278}
279
280void KateDocument::disableAllPluginsGUI (KateView *view)
281{
282 for (uint i=0; i<m_plugins.count(); i++)
283 disablePluginGUI (m_plugins[i], view);
284}
285
286void KateDocument::loadPlugin (uint pluginIndex)
287{
288 if (m_plugins[pluginIndex]) return;
289
290 m_plugins[pluginIndex] = KTextEditor::createPlugin (TQFile::encodeName((KateFactory::self()->plugins())[pluginIndex]->library()), this);
291
292 enablePluginGUI (m_plugins[pluginIndex]);
293}
294
295void KateDocument::unloadPlugin (uint pluginIndex)
296{
297 if (!m_plugins[pluginIndex]) return;
298
299 disablePluginGUI (m_plugins[pluginIndex]);
300
301 delete m_plugins[pluginIndex];
302 m_plugins[pluginIndex] = 0L;
303}
304
305void KateDocument::enablePluginGUI (KTextEditor::Plugin *plugin, KateView *view)
306{
307 if (!plugin) return;
308 if (!KTextEditor::pluginViewInterface(plugin)) return;
309
310 KXMLGUIFactory *factory = view->factory();
311 if ( factory )
312 factory->removeClient( view );
313
314 KTextEditor::pluginViewInterface(plugin)->addView(view);
315
316 if ( factory )
317 factory->addClient( view );
318}
319
320void KateDocument::enablePluginGUI (KTextEditor::Plugin *plugin)
321{
322 if (!plugin) return;
323 if (!KTextEditor::pluginViewInterface(plugin)) return;
324
325 for (uint i=0; i< m_views.count(); i++)
326 enablePluginGUI (plugin, m_views.at(i));
327}
328
329void KateDocument::disablePluginGUI (KTextEditor::Plugin *plugin, KateView *view)
330{
331 if (!plugin) return;
332 if (!KTextEditor::pluginViewInterface(plugin)) return;
333
334 KXMLGUIFactory *factory = view->factory();
335 if ( factory )
336 factory->removeClient( view );
337
338 KTextEditor::pluginViewInterface( plugin )->removeView( view );
339
340 if ( factory )
341 factory->addClient( view );
342}
343
344void KateDocument::disablePluginGUI (KTextEditor::Plugin *plugin)
345{
346 if (!plugin) return;
347 if (!KTextEditor::pluginViewInterface(plugin)) return;
348
349 for (uint i=0; i< m_views.count(); i++)
350 disablePluginGUI (plugin, m_views.at(i));
351}
352//END
353
354//BEGIN KTextEditor::Document stuff
355
356KTextEditor::View *KateDocument::createView( TQWidget *parent, const char *name )
357{
358 KateView* newView = new KateView( this, parent, name);
359 connect(newView, TQ_SIGNAL(cursorPositionChanged()), TQ_SLOT(undoCancel()));
360 if ( s_fileChangedDialogsActivated )
361 connect( newView, TQ_SIGNAL(gotFocus( Kate::View * )), this, TQ_SLOT(slotModifiedOnDisk()) );
362 return newView;
363}
364
365TQPtrList<KTextEditor::View> KateDocument::views () const
366{
367 return m_textEditViews;
368}
369
370void KateDocument::setActiveView( KateView *view )
371{
372 if ( m_activeView == view ) return;
373
374 m_activeView = view;
375}
376//END
377
378//BEGIN KTextEditor::ConfigInterfaceExtension stuff
379
380uint KateDocument::configPages () const
381{
382 return 10;
383}
384
385KTextEditor::ConfigPage *KateDocument::configPage (uint number, TQWidget *parent, const char * )
386{
387 switch( number )
388 {
389 case 0:
390 return new KateViewDefaultsConfig (parent);
391
392 case 1:
393 return new KateSchemaConfigPage (parent, this);
394
395 case 2:
396 return new KateSelectConfigTab (parent);
397
398 case 3:
399 return new KateEditConfigTab (parent);
400
401 case 4:
402 return new KateIndentConfigTab (parent);
403
404 case 5:
405 return new KateSaveConfigTab (parent);
406
407 case 6:
408 return new KateHlConfigPage (parent, this);
409
410 case 7:
411 return new KateFileTypeConfigTab (parent);
412
413 case 8:
414 return new KateEditKeyConfiguration (parent, this);
415
416 case 9:
417 return new KatePartPluginConfigPage (parent);
418
419 default:
420 return 0;
421 }
422
423 return 0;
424}
425
426TQString KateDocument::configPageName (uint number) const
427{
428 switch( number )
429 {
430 case 0:
431 return i18n ("Appearance");
432
433 case 1:
434 return i18n ("Fonts & Colors");
435
436 case 2:
437 return i18n ("Cursor & Selection");
438
439 case 3:
440 return i18n ("Editing");
441
442 case 4:
443 return i18n ("Indentation");
444
445 case 5:
446 return i18n("Open/Save");
447
448 case 6:
449 return i18n ("Highlighting");
450
451 case 7:
452 return i18n("Filetypes");
453
454 case 8:
455 return i18n ("Shortcuts");
456
457 case 9:
458 return i18n ("Plugins");
459
460 default:
461 return TQString ("");
462 }
463
464 return TQString ("");
465}
466
467TQString KateDocument::configPageFullName (uint number) const
468{
469 switch( number )
470 {
471 case 0:
472 return i18n("Appearance");
473
474 case 1:
475 return i18n ("Font & Color Schemas");
476
477 case 2:
478 return i18n ("Cursor & Selection Behavior");
479
480 case 3:
481 return i18n ("Editing Options");
482
483 case 4:
484 return i18n ("Indentation Rules");
485
486 case 5:
487 return i18n("File Opening & Saving");
488
489 case 6:
490 return i18n ("Highlighting Rules");
491
492 case 7:
493 return i18n("Filetype Specific Settings");
494
495 case 8:
496 return i18n ("Shortcuts Configuration");
497
498 case 9:
499 return i18n ("Plugin Manager");
500
501 default:
502 return TQString ("");
503 }
504
505 return TQString ("");
506}
507
508TQPixmap KateDocument::configPagePixmap (uint number, int size) const
509{
510 switch( number )
511 {
512 case 0:
513 return BarIcon("view_text",size);
514
515 case 1:
516 return BarIcon("colorize", size);
517
518 case 2:
519 return BarIcon("frame_edit", size);
520
521 case 3:
522 return BarIcon("edit", size);
523
524 case 4:
525 return BarIcon("format-justify-right", size);
526
527 case 5:
528 return BarIcon("document-save", size);
529
530 case 6:
531 return BarIcon("text-x-src", size);
532
533 case 7:
534 return BarIcon("edit", size);
535
536 case 8:
537 return BarIcon("key_enter", size);
538
539 case 9:
540 return BarIcon("connect_established", size);
541
542 default:
543 return BarIcon("edit", size);
544 }
545
546 return BarIcon("edit", size);
547}
548//END
549
550//BEGIN KTextEditor::EditInterface stuff
551
552TQString KateDocument::text() const
553{
554 TQString s;
555
556 for (uint i = 0; i < m_buffer->count(); i++)
557 {
558 KateTextLine::Ptr textLine = m_buffer->plainLine(i);
559
560 if (textLine)
561 {
562 s.append (textLine->string());
563
564 if ((i+1) < m_buffer->count())
565 s.append('\n');
566 }
567 }
568
569 return s;
570}
571
572TQString KateDocument::text ( uint startLine, uint startCol, uint endLine, uint endCol ) const
573{
574 return text(startLine, startCol, endLine, endCol, false);
575}
576
577TQString KateDocument::text ( uint startLine, uint startCol, uint endLine, uint endCol, bool blockwise) const
578{
579 if ( blockwise && (startCol > endCol) )
580 return TQString ();
581
582 TQString s;
583
584 if (startLine == endLine)
585 {
586 if (startCol > endCol)
587 return TQString ();
588
589 KateTextLine::Ptr textLine = m_buffer->plainLine(startLine);
590
591 if ( !textLine )
592 return TQString ();
593
594 return textLine->string(startCol, endCol-startCol);
595 }
596 else
597 {
598
599 for (uint i = startLine; (i <= endLine) && (i < m_buffer->count()); i++)
600 {
601 KateTextLine::Ptr textLine = m_buffer->plainLine(i);
602
603 if ( !blockwise )
604 {
605 if (i == startLine)
606 s.append (textLine->string(startCol, textLine->length()-startCol));
607 else if (i == endLine)
608 s.append (textLine->string(0, endCol));
609 else
610 s.append (textLine->string());
611 }
612 else
613 {
614 s.append( textLine->string( startCol, endCol-startCol));
615 }
616
617 if ( i < endLine )
618 s.append('\n');
619 }
620 }
621
622 return s;
623}
624
625TQString KateDocument::textLine( uint line ) const
626{
627 KateTextLine::Ptr l = m_buffer->plainLine(line);
628
629 if (!l)
630 return TQString();
631
632 return l->string();
633}
634
635bool KateDocument::setText(const TQString &s)
636{
637 if (!isReadWrite())
638 return false;
639
640 TQPtrList<KTextEditor::Mark> m = marks ();
641 TQValueList<KTextEditor::Mark> msave;
642
643 for (uint i=0; i < m.count(); i++)
644 msave.append (*m.at(i));
645
646 editStart ();
647
648 // delete the text
649 clear();
650
651 // insert the new text
652 insertText (0, 0, s);
653
654 editEnd ();
655
656 for (uint i=0; i < msave.count(); i++)
657 setMark (msave[i].line, msave[i].type);
658
659 return true;
660}
661
662bool KateDocument::clear()
663{
664 if (!isReadWrite())
665 return false;
666
667 for (KateView * view = m_views.first(); view != 0L; view = m_views.next() ) {
668 view->clear();
669 view->tagAll();
670 view->update();
671 }
672
673 clearMarks ();
674
675 return removeText (0,0,lastLine()+1, 0);
676}
677
678bool KateDocument::insertText( uint line, uint col, const TQString &s)
679{
680 return insertText (line, col, s, false);
681}
682
683bool KateDocument::insertText( uint line, uint col, const TQString &s, bool blockwise )
684{
685 if (!isReadWrite())
686 return false;
687
688 if (s.isEmpty())
689 return true;
690
691 if (line == numLines())
692 editInsertLine(line,"");
693 else if (line > lastLine())
694 return false;
695
696 editStart ();
697
698 uint insertPos = col;
699 uint len = s.length();
700
701 TQString buf;
702
703 bool replacetabs = ( config()->configFlags() & KateDocumentConfig::cfReplaceTabsDyn && ! m_isInUndo );
704 uint tw = config()->tabWidth();
705 uint insertPosExpanded = insertPos;
706 KateTextLine::Ptr l = m_buffer->line( line );
707 if (l != 0)
708 insertPosExpanded = l->cursorX( insertPos, tw );
709
710 for (uint pos = 0; pos < len; pos++)
711 {
712 TQChar ch = s[pos];
713
714 if (ch == '\n')
715 {
716 editInsertText (line, insertPos, buf);
717
718 if ( !blockwise )
719 {
720 editWrapLine (line, insertPos + buf.length());
721 insertPos = insertPosExpanded = 0;
722 }
723 else
724 {
725 if ( line == lastLine() )
726 editWrapLine (line, insertPos + buf.length());
727 }
728
729 line++;
730 buf.truncate(0);
731 l = m_buffer->line( line );
732 if (l)
733 insertPosExpanded = l->cursorX( insertPos, tw );
734 }
735 else
736 {
737 if ( replacetabs && ch == '\t' )
738 {
739 uint tr = tw - ( insertPosExpanded+buf.length() )%tw;
740 for ( uint i=0; i < tr; i++ )
741 buf += ' ';
742 }
743 else
744 buf += ch; // append char to buffer
745 }
746 }
747
748 editInsertText (line, insertPos, buf);
749
750 editEnd ();
751 emit textInserted(line,insertPos);
752 return true;
753}
754
755bool KateDocument::removeText ( uint startLine, uint startCol, uint endLine, uint endCol )
756{
757 return removeText (startLine, startCol, endLine, endCol, false);
758}
759
760bool KateDocument::removeText ( uint startLine, uint startCol, uint endLine, uint endCol, bool blockwise)
761{
762 if (!isReadWrite())
763 return false;
764
765 if ( blockwise && (startCol > endCol) )
766 return false;
767
768 if ( startLine > endLine )
769 return false;
770
771 if ( startLine > lastLine() )
772 return false;
773
774 if (!blockwise) {
775 emit aboutToRemoveText(KateTextRange(startLine,startCol,endLine,endCol));
776 }
777 editStart ();
778
779 if ( !blockwise )
780 {
781 if ( endLine > lastLine() )
782 {
783 endLine = lastLine()+1;
784 endCol = 0;
785 }
786
787 if (startLine == endLine)
788 {
789 editRemoveText (startLine, startCol, endCol-startCol);
790 }
791 else if ((startLine+1) == endLine)
792 {
793 if ( (m_buffer->plainLine(startLine)->length()-startCol) > 0 )
794 editRemoveText (startLine, startCol, m_buffer->plainLine(startLine)->length()-startCol);
795
796 editRemoveText (startLine+1, 0, endCol);
797 editUnWrapLine (startLine);
798 }
799 else
800 {
801 for (uint line = endLine; line >= startLine; line--)
802 {
803 if ((line > startLine) && (line < endLine))
804 {
805 editRemoveLine (line);
806 }
807 else
808 {
809 if (line == endLine)
810 {
811 if ( endLine <= lastLine() )
812 editRemoveText (line, 0, endCol);
813 }
814 else
815 {
816 if ( (m_buffer->plainLine(line)->length()-startCol) > 0 )
817 editRemoveText (line, startCol, m_buffer->plainLine(line)->length()-startCol);
818
819 editUnWrapLine (startLine);
820 }
821 }
822
823 if ( line == 0 )
824 break;
825 }
826 }
827 } // if ( ! blockwise )
828 else
829 {
830 if ( endLine > lastLine() )
831 endLine = lastLine ();
832
833 for (uint line = endLine; line >= startLine; line--)
834 {
835
836 editRemoveText (line, startCol, endCol-startCol);
837
838 if ( line == 0 )
839 break;
840 }
841 }
842
843 editEnd ();
844 emit textRemoved();
845 return true;
846}
847
848bool KateDocument::insertLine( uint l, const TQString &str )
849{
850 if (!isReadWrite())
851 return false;
852
853 if (l > numLines())
854 return false;
855
856 return editInsertLine (l, str);
857}
858
859bool KateDocument::removeLine( uint line )
860{
861 if (!isReadWrite())
862 return false;
863
864 if (line > lastLine())
865 return false;
866
867 return editRemoveLine (line);
868}
869
870uint KateDocument::length() const
871{
872 uint l = 0;
873
874 for (uint i = 0; i < m_buffer->count(); i++)
875 {
876 KateTextLine::Ptr line = m_buffer->plainLine(i);
877
878 if (line)
879 l += line->length();
880 }
881
882 return l;
883}
884
885uint KateDocument::numLines() const
886{
887 return m_buffer->count();
888}
889
890uint KateDocument::numVisLines() const
891{
892 return m_buffer->countVisible ();
893}
894
895int KateDocument::lineLength ( uint line ) const
896{
897 KateTextLine::Ptr l = m_buffer->plainLine(line);
898
899 if (!l)
900 return -1;
901
902 return l->length();
903}
904//END
905
906//BEGIN KTextEditor::EditInterface internal stuff
907//
908// Starts an edit session with (or without) undo, update of view disabled during session
909//
910void KateDocument::editStart (bool withUndo)
911{
912 editSessionNumber++;
913
914 if (editSessionNumber > 1)
915 return;
916
917 editIsRunning = true;
918 editWithUndo = withUndo;
919
920 if (editWithUndo)
921 undoStart();
922 else
923 undoCancel();
924
925 for (uint z = 0; z < m_views.count(); z++)
926 {
927 m_views.at(z)->editStart ();
928 }
929
930 m_buffer->editStart ();
931}
932
933void KateDocument::undoStart()
934{
935 if (m_editCurrentUndo || (m_activeView && m_activeView->imComposeEvent())) return;
936
937 // Make sure the buffer doesn't get bigger than requested
938 if ((config()->undoSteps() > 0) && (undoItems.count() > config()->undoSteps()))
939 {
940 undoItems.setAutoDelete(true);
941 undoItems.removeFirst();
942 undoItems.setAutoDelete(false);
943 docWasSavedWhenUndoWasEmpty = false;
944 }
945
946 // new current undo item
947 m_editCurrentUndo = new KateUndoGroup(this);
948}
949
950void KateDocument::undoEnd()
951{
952 if (m_activeView && m_activeView->imComposeEvent())
953 return;
954
955 if (m_editCurrentUndo)
956 {
957 bool changedUndo = false;
958
959 if (m_editCurrentUndo->isEmpty())
960 delete m_editCurrentUndo;
961 else if (!m_undoDontMerge && undoItems.last() && undoItems.last()->merge(m_editCurrentUndo,m_undoComplexMerge))
962 delete m_editCurrentUndo;
963 else
964 {
965 undoItems.append(m_editCurrentUndo);
966 changedUndo = true;
967 }
968
969 m_undoDontMerge = false;
970 m_undoIgnoreCancel = true;
971
972 m_editCurrentUndo = 0L;
973
974 // (Re)Start the single-shot timer to cancel the undo merge
975 // the user has 5 seconds to input more data, or undo merging gets canceled for the current undo item.
976 m_undoMergeTimer->start(5000, true);
977
978 if (changedUndo)
979 emit undoChanged();
980 }
981}
982
983void KateDocument::undoCancel()
984{
985 if (m_undoIgnoreCancel) {
986 m_undoIgnoreCancel = false;
987 return;
988 }
989
990 m_undoDontMerge = true;
991
992 Q_ASSERT(!m_editCurrentUndo);
993
994 // As you can see by the above assert, neither of these should really be required
995 delete m_editCurrentUndo;
996 m_editCurrentUndo = 0L;
997}
998
999void KateDocument::undoSafePoint() {
1000 Q_ASSERT(m_editCurrentUndo);
1001 if (!m_editCurrentUndo) return;
1002 m_editCurrentUndo->safePoint();
1003}
1004
1005//
1006// End edit session and update Views
1007//
1008void KateDocument::editEnd ()
1009{
1010 if (editSessionNumber == 0)
1011 return;
1012
1013 // wrap the new/changed text, if something really changed!
1014 if (m_buffer->editChanged() && (editSessionNumber == 1))
1015 if (editWithUndo && config()->wordWrap())
1016 wrapText (m_buffer->editTagStart(), m_buffer->editTagEnd());
1017
1018 editSessionNumber--;
1019
1020 if (editSessionNumber > 0)
1021 return;
1022
1023 // end buffer edit, will trigger hl update
1024 // this will cause some possible adjustment of tagline start/end
1025 m_buffer->editEnd ();
1026
1027 if (editWithUndo)
1028 undoEnd();
1029
1030 // edit end for all views !!!!!!!!!
1031 for (uint z = 0; z < m_views.count(); z++)
1032 m_views.at(z)->editEnd (m_buffer->editTagStart(), m_buffer->editTagEnd(), m_buffer->editTagFrom());
1033
1034 if (m_buffer->editChanged())
1035 {
1036 setModified(true);
1037 emit textChanged ();
1038 }
1039
1040 editIsRunning = false;
1041}
1042
1043bool KateDocument::wrapText (uint startLine, uint endLine)
1044{
1045 uint col = config()->wordWrapAt();
1046
1047 if (col == 0)
1048 return false;
1049
1050 editStart ();
1051
1052 for (uint line = startLine; (line <= endLine) && (line < numLines()); line++)
1053 {
1054 KateTextLine::Ptr l = m_buffer->line(line);
1055
1056 if (!l)
1057 return false;
1058
1059 kdDebug (13020) << "try wrap line: " << line << endl;
1060
1061 if (l->lengthWithTabs(m_buffer->tabWidth()) > col)
1062 {
1063 KateTextLine::Ptr nextl = m_buffer->line(line+1);
1064
1065 kdDebug (13020) << "do wrap line: " << line << endl;
1066
1067 const TQChar *text = l->text();
1068 uint eolPosition = l->length()-1;
1069
1070 // take tabs into account here, too
1071 uint x = 0;
1072 const TQString & t = l->string();
1073 uint z2 = 0;
1074 for ( ; z2 < l->length(); z2++)
1075 {
1076 if (t[z2] == TQChar('\t'))
1077 x += m_buffer->tabWidth() - (x % m_buffer->tabWidth());
1078 else
1079 x++;
1080
1081 if (x > col)
1082 break;
1083 }
1084
1085 uint searchStart = kMin (z2, l->length()-1);
1086
1087 // If where we are wrapping is an end of line and is a space we don't
1088 // want to wrap there
1089 if (searchStart == eolPosition && text[searchStart].isSpace())
1090 searchStart--;
1091
1092 // Scan backwards looking for a place to break the line
1093 // We are not interested in breaking at the first char
1094 // of the line (if it is a space), but we are at the second
1095 // anders: if we can't find a space, try breaking on a word
1096 // boundry, using KateHighlight::canBreakAt().
1097 // This could be a priority (setting) in the hl/filetype/document
1098 int z = 0;
1099 uint nw = 0; // alternative position, a non word character
1100 for (z=searchStart; z > 0; z--)
1101 {
1102 if (text[z].isSpace()) break;
1103 if ( ! nw && highlight()->canBreakAt( text[z] , l->attribute(z) ) )
1104 nw = z;
1105 }
1106
1107 if (z > 0)
1108 {
1109 // cu space
1110 editRemoveText (line, z, 1);
1111 }
1112 else
1113 {
1114 // There was no space to break at so break at a nonword character if
1115 // found, or at the wrapcolumn ( that needs be configurable )
1116 // Don't try and add any white space for the break
1117 if ( nw && nw < col ) nw++; // break on the right side of the character
1118 z = nw ? nw : col;
1119 }
1120
1121 if (nextl && !nextl->isAutoWrapped())
1122 {
1123 editWrapLine (line, z, true);
1124 editMarkLineAutoWrapped (line+1, true);
1125
1126 endLine++;
1127 }
1128 else
1129 {
1130 if (nextl && (nextl->length() > 0) && !nextl->getChar(0).isSpace() && ((l->length() < 1) || !l->getChar(l->length()-1).isSpace()))
1131 editInsertText (line+1, 0, TQString (" "));
1132
1133 bool newLineAdded = false;
1134 editWrapLine (line, z, false, &newLineAdded);
1135
1136 editMarkLineAutoWrapped (line+1, true);
1137
1138 endLine++;
1139 }
1140 }
1141 }
1142
1143 editEnd ();
1144
1145 return true;
1146}
1147
1148void KateDocument::editAddUndo (KateUndoGroup::UndoType type, uint line, uint col, uint len, const TQString &text)
1149{
1150 if (editIsRunning && editWithUndo && m_editCurrentUndo) {
1151 m_editCurrentUndo->addItem(type, line, col, len, text);
1152
1153 // Clear redo buffer
1154 if (redoItems.count()) {
1155 redoItems.setAutoDelete(true);
1156 redoItems.clear();
1157 redoItems.setAutoDelete(false);
1158 }
1159 }
1160}
1161
1162bool KateDocument::editInsertText ( uint line, uint col, const TQString &str )
1163{
1164 if (!isReadWrite())
1165 return false;
1166
1167 TQString s = str;
1168
1169 KateTextLine::Ptr l = m_buffer->line(line);
1170
1171 if (!l)
1172 return false;
1173
1174 if ( config()->configFlags() & KateDocumentConfig::cfReplaceTabsDyn && ! m_isInUndo )
1175 {
1176 uint tw = config()->tabWidth();
1177 int pos = 0;
1178 uint l = 0;
1179 while ( (pos = s.find('\t')) > -1 )
1180 {
1181 l = tw - ( (col + pos)%tw );
1182 s.replace( pos, 1, TQString().fill( ' ', l ) );
1183 }
1184 }
1185
1186 editStart ();
1187
1188 editAddUndo (KateUndoGroup::editInsertText, line, col, s.length(), s);
1189
1190 l->insertText (col, s.length(), s.unicode());
1191// removeTrailingSpace(line); // ### nessecary?
1192
1193 m_buffer->changeLine(line);
1194
1195 for( TQPtrListIterator<KateSuperCursor> it (m_superCursors); it.current(); ++it )
1196 it.current()->editTextInserted (line, col, s.length());
1197
1198 editEnd ();
1199
1200 return true;
1201}
1202
1203bool KateDocument::editRemoveText ( uint line, uint col, uint len )
1204{
1205 if (!isReadWrite())
1206 return false;
1207
1208 KateTextLine::Ptr l = m_buffer->line(line);
1209
1210 if (!l)
1211 return false;
1212
1213 editStart ();
1214
1215 editAddUndo (KateUndoGroup::editRemoveText, line, col, len, l->string().mid(col, len));
1216
1217 l->removeText (col, len);
1218 removeTrailingSpace( line );
1219
1220 m_buffer->changeLine(line);
1221
1222 for( TQPtrListIterator<KateSuperCursor> it (m_superCursors); it.current(); ++it )
1223 it.current()->editTextRemoved (line, col, len);
1224
1225 editEnd ();
1226
1227 return true;
1228}
1229
1230bool KateDocument::editMarkLineAutoWrapped ( uint line, bool autowrapped )
1231{
1232 if (!isReadWrite())
1233 return false;
1234
1235 KateTextLine::Ptr l = m_buffer->line(line);
1236
1237 if (!l)
1238 return false;
1239
1240 editStart ();
1241
1242 editAddUndo (KateUndoGroup::editMarkLineAutoWrapped, line, autowrapped ? 1 : 0, 0, TQString::null);
1243
1244 l->setAutoWrapped (autowrapped);
1245
1246 m_buffer->changeLine(line);
1247
1248 editEnd ();
1249
1250 return true;
1251}
1252
1253bool KateDocument::editWrapLine ( uint line, uint col, bool newLine, bool *newLineAdded)
1254{
1255 if (!isReadWrite())
1256 return false;
1257
1258 KateTextLine::Ptr l = m_buffer->line(line);
1259
1260 if (!l)
1261 return false;
1262
1263 editStart ();
1264
1265 KateTextLine::Ptr nextLine = m_buffer->line(line+1);
1266
1267 int pos = l->length() - col;
1268
1269 if (pos < 0)
1270 pos = 0;
1271
1272 editAddUndo (KateUndoGroup::editWrapLine, line, col, pos, (!nextLine || newLine) ? "1" : "0");
1273
1274 if (!nextLine || newLine)
1275 {
1276 KateTextLine::Ptr textLine = new KateTextLine();
1277
1278 textLine->insertText (0, pos, l->text()+col, l->attributes()+col);
1279 l->truncate(col);
1280
1281 m_buffer->insertLine (line+1, textLine);
1282 m_buffer->changeLine(line);
1283
1284 TQPtrList<KTextEditor::Mark> list;
1285 for( TQIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it )
1286 {
1287 if( it.current()->line >= line )
1288 {
1289 if ((col == 0) || (it.current()->line > line))
1290 list.append( it.current() );
1291 }
1292 }
1293
1294 for( TQPtrListIterator<KTextEditor::Mark> it( list ); it.current(); ++it )
1295 {
1296 KTextEditor::Mark* mark = m_marks.take( it.current()->line );
1297 mark->line++;
1298 m_marks.insert( mark->line, mark );
1299 }
1300
1301 if( !list.isEmpty() )
1302 emit marksChanged();
1303
1304 // yes, we added a new line !
1305 if (newLineAdded)
1306 (*newLineAdded) = true;
1307 }
1308 else
1309 {
1310 nextLine->insertText (0, pos, l->text()+col, l->attributes()+col);
1311 l->truncate(col);
1312
1313 m_buffer->changeLine(line);
1314 m_buffer->changeLine(line+1);
1315
1316 // no, no new line added !
1317 if (newLineAdded)
1318 (*newLineAdded) = false;
1319 }
1320
1321 for( TQPtrListIterator<KateSuperCursor> it (m_superCursors); it.current(); ++it )
1322 it.current()->editLineWrapped (line, col, !nextLine || newLine);
1323
1324 editEnd ();
1325
1326 return true;
1327}
1328
1329bool KateDocument::editUnWrapLine ( uint line, bool removeLine, uint length )
1330{
1331 if (!isReadWrite())
1332 return false;
1333
1334 KateTextLine::Ptr l = m_buffer->line(line);
1335 KateTextLine::Ptr nextLine = m_buffer->line(line+1);
1336
1337 if (!l || !nextLine)
1338 return false;
1339
1340 editStart ();
1341
1342 uint col = l->length ();
1343
1344 editAddUndo (KateUndoGroup::editUnWrapLine, line, col, length, removeLine ? "1" : "0");
1345
1346 if (removeLine)
1347 {
1348 l->insertText (col, nextLine->length(), nextLine->text(), nextLine->attributes());
1349
1350 m_buffer->changeLine(line);
1351 m_buffer->removeLine(line+1);
1352 }
1353 else
1354 {
1355 l->insertText (col, (nextLine->length() < length) ? nextLine->length() : length,
1356 nextLine->text(), nextLine->attributes());
1357 nextLine->removeText (0, (nextLine->length() < length) ? nextLine->length() : length);
1358
1359 m_buffer->changeLine(line);
1360 m_buffer->changeLine(line+1);
1361 }
1362
1363 TQPtrList<KTextEditor::Mark> list;
1364 for( TQIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it )
1365 {
1366 if( it.current()->line >= line+1 )
1367 list.append( it.current() );
1368
1369 if ( it.current()->line == line+1 )
1370 {
1371 KTextEditor::Mark* mark = m_marks.take( line );
1372
1373 if (mark)
1374 {
1375 it.current()->type |= mark->type;
1376 }
1377 }
1378 }
1379
1380 for( TQPtrListIterator<KTextEditor::Mark> it( list ); it.current(); ++it )
1381 {
1382 KTextEditor::Mark* mark = m_marks.take( it.current()->line );
1383 mark->line--;
1384 m_marks.insert( mark->line, mark );
1385 }
1386
1387 if( !list.isEmpty() )
1388 emit marksChanged();
1389
1390 for( TQPtrListIterator<KateSuperCursor> it (m_superCursors); it.current(); ++it )
1391 it.current()->editLineUnWrapped (line, col, removeLine, length);
1392
1393 editEnd ();
1394
1395 return true;
1396}
1397
1398bool KateDocument::editInsertLine ( uint line, const TQString &s )
1399{
1400 if (!isReadWrite())
1401 return false;
1402
1403 if ( line > numLines() )
1404 return false;
1405
1406 editStart ();
1407
1408 editAddUndo (KateUndoGroup::editInsertLine, line, 0, s.length(), s);
1409
1410 removeTrailingSpace( line ); // old line
1411
1412 KateTextLine::Ptr tl = new KateTextLine();
1413 tl->insertText (0, s.length(), s.unicode(), 0);
1414 m_buffer->insertLine(line, tl);
1415 m_buffer->changeLine(line);
1416
1417 removeTrailingSpace( line ); // new line
1418
1419 TQPtrList<KTextEditor::Mark> list;
1420 for( TQIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it )
1421 {
1422 if( it.current()->line >= line )
1423 list.append( it.current() );
1424 }
1425
1426 for( TQPtrListIterator<KTextEditor::Mark> it( list ); it.current(); ++it )
1427 {
1428 KTextEditor::Mark* mark = m_marks.take( it.current()->line );
1429 mark->line++;
1430 m_marks.insert( mark->line, mark );
1431 }
1432
1433 if( !list.isEmpty() )
1434 emit marksChanged();
1435
1436 for( TQPtrListIterator<KateSuperCursor> it (m_superCursors); it.current(); ++it )
1437 it.current()->editLineInserted (line);
1438
1439 editEnd ();
1440
1441 return true;
1442}
1443
1444bool KateDocument::editRemoveLine ( uint line )
1445{
1446 if (!isReadWrite())
1447 return false;
1448
1449 if ( line > lastLine() )
1450 return false;
1451
1452 if ( numLines() == 1 )
1453 return editRemoveText (0, 0, m_buffer->line(0)->length());
1454
1455 editStart ();
1456
1457 editAddUndo (KateUndoGroup::editRemoveLine, line, 0, lineLength(line), textLine(line));
1458
1459 m_buffer->removeLine(line);
1460
1461 TQPtrList<KTextEditor::Mark> list;
1462 KTextEditor::Mark* rmark = 0;
1463 for( TQIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it )
1464 {
1465 if ( (it.current()->line > line) )
1466 list.append( it.current() );
1467 else if ( (it.current()->line == line) )
1468 rmark = it.current();
1469 }
1470
1471 if (rmark)
1472 delete (m_marks.take (rmark->line));
1473
1474 for( TQPtrListIterator<KTextEditor::Mark> it( list ); it.current(); ++it )
1475 {
1476 KTextEditor::Mark* mark = m_marks.take( it.current()->line );
1477 mark->line--;
1478 m_marks.insert( mark->line, mark );
1479 }
1480
1481 if( !list.isEmpty() )
1482 emit marksChanged();
1483
1484 for( TQPtrListIterator<KateSuperCursor> it (m_superCursors); it.current(); ++it )
1485 it.current()->editLineRemoved (line);
1486
1487 editEnd();
1488
1489 return true;
1490}
1491//END
1492
1493//BEGIN KTextEditor::UndoInterface stuff
1494
1495uint KateDocument::undoCount () const
1496{
1497 return undoItems.count ();
1498}
1499
1500uint KateDocument::redoCount () const
1501{
1502 return redoItems.count ();
1503}
1504
1505uint KateDocument::undoSteps () const
1506{
1507 return m_config->undoSteps();
1508}
1509
1510void KateDocument::setUndoSteps(uint steps)
1511{
1512 m_config->setUndoSteps (steps);
1513}
1514
1515void KateDocument::undo()
1516{
1517 m_isInUndo = true;
1518 if ((undoItems.count() > 0) && undoItems.last())
1519 {
1520 clearSelection ();
1521
1522 undoItems.last()->undo();
1523 redoItems.append (undoItems.last());
1524 undoItems.removeLast ();
1525 updateModified();
1526
1527 emit undoChanged ();
1528 }
1529 m_isInUndo = false;
1530}
1531
1532void KateDocument::redo()
1533{
1534 m_isInUndo = true;
1535 if ((redoItems.count() > 0) && redoItems.last())
1536 {
1537 clearSelection ();
1538
1539 redoItems.last()->redo();
1540 undoItems.append (redoItems.last());
1541 redoItems.removeLast ();
1542 updateModified();
1543
1544 emit undoChanged ();
1545 }
1546 m_isInUndo = false;
1547}
1548
1549void KateDocument::updateModified()
1550{
1551 /*
1552 How this works:
1553
1554 After noticing that there where to many scenarios to take into
1555 consideration when using 'if's to toggle the "Modified" flag
1556 I came up with this baby, flexible and repetitive calls are
1557 minimal.
1558
1559 A numeric unique pattern is generated by toggleing a set of bits,
1560 each bit symbolizes a different state in the Undo Redo structure.
1561
1562 undoItems.isEmpty() != null BIT 1
1563 redoItems.isEmpty() != null BIT 2
1564 docWasSavedWhenUndoWasEmpty == true BIT 3
1565 docWasSavedWhenRedoWasEmpty == true BIT 4
1566 lastUndoGroupWhenSavedIsLastUndo BIT 5
1567 lastUndoGroupWhenSavedIsLastRedo BIT 6
1568 lastRedoGroupWhenSavedIsLastUndo BIT 7
1569 lastRedoGroupWhenSavedIsLastRedo BIT 8
1570
1571 If you find a new pattern, please add it to the patterns array
1572 */
1573
1574 unsigned char currentPattern = 0;
1575 const unsigned char patterns[] = {5,16,24,26,88,90,93,133,144,149,165};
1576 const unsigned char patternCount = sizeof(patterns);
1577 KateUndoGroup* undoLast = 0;
1578 KateUndoGroup* redoLast = 0;
1579
1580 if (undoItems.isEmpty())
1581 {
1582 currentPattern |= 1;
1583 }
1584 else
1585 {
1586 undoLast = undoItems.last();
1587 }
1588
1589 if (redoItems.isEmpty())
1590 {
1591 currentPattern |= 2;
1592 }
1593 else
1594 {
1595 redoLast = redoItems.last();
1596 }
1597
1598 if (docWasSavedWhenUndoWasEmpty) currentPattern |= 4;
1599 if (docWasSavedWhenRedoWasEmpty) currentPattern |= 8;
1600 if (lastUndoGroupWhenSaved == undoLast) currentPattern |= 16;
1601 if (lastUndoGroupWhenSaved == redoLast) currentPattern |= 32;
1602 if (lastRedoGroupWhenSaved == undoLast) currentPattern |= 64;
1603 if (lastRedoGroupWhenSaved == redoLast) currentPattern |= 128;
1604
1605 // This will print out the pattern information
1606
1607 kdDebug(13020) << k_funcinfo
1608 << "Pattern:" << static_cast<unsigned int>(currentPattern) << endl;
1609
1610 for (uint patternIndex = 0; patternIndex < patternCount; ++patternIndex)
1611 {
1612 if ( currentPattern == patterns[patternIndex] )
1613 {
1614 setModified( false );
1615 kdDebug(13020) << k_funcinfo << "setting modified to false!" << endl;
1616 break;
1617 }
1618 }
1619}
1620
1621void KateDocument::clearUndo()
1622{
1623 undoItems.setAutoDelete (true);
1624 undoItems.clear ();
1625 undoItems.setAutoDelete (false);
1626
1627 lastUndoGroupWhenSaved = 0;
1628 docWasSavedWhenUndoWasEmpty = false;
1629
1630 emit undoChanged ();
1631}
1632
1633void KateDocument::clearRedo()
1634{
1635 redoItems.setAutoDelete (true);
1636 redoItems.clear ();
1637 redoItems.setAutoDelete (false);
1638
1639 lastRedoGroupWhenSaved = 0;
1640 docWasSavedWhenRedoWasEmpty = false;
1641
1642 emit undoChanged ();
1643}
1644
1645TQPtrList<KTextEditor::Cursor> KateDocument::cursors () const
1646{
1647 return myCursors;
1648}
1649//END
1650
1651//BEGIN KTextEditor::SearchInterface stuff
1652
1653bool KateDocument::searchText (unsigned int startLine, unsigned int startCol, const TQString &text, unsigned int *foundAtLine, unsigned int *foundAtCol, unsigned int *matchLen, bool casesensitive, bool backwards)
1654{
1655 if (text.isEmpty())
1656 return false;
1657
1658 int line = startLine;
1659 int col = startCol;
1660
1661 if (!backwards)
1662 {
1663 int searchEnd = lastLine();
1664
1665 while (line <= searchEnd)
1666 {
1667 KateTextLine::Ptr textLine = m_buffer->plainLine(line);
1668
1669 if (!textLine)
1670 return false;
1671
1672 uint foundAt, myMatchLen;
1673 bool found = textLine->searchText (col, text, &foundAt, &myMatchLen, casesensitive, false);
1674
1675 if (found)
1676 {
1677 (*foundAtLine) = line;
1678 (*foundAtCol) = foundAt;
1679 (*matchLen) = myMatchLen;
1680 return true;
1681 }
1682
1683 col = 0;
1684 line++;
1685 }
1686 }
1687 else
1688 {
1689 // backward search
1690 int searchEnd = 0;
1691
1692 while (line >= searchEnd)
1693 {
1694 KateTextLine::Ptr textLine = m_buffer->plainLine(line);
1695
1696 if (!textLine)
1697 return false;
1698
1699 uint foundAt, myMatchLen;
1700 bool found = textLine->searchText (col, text, &foundAt, &myMatchLen, casesensitive, true);
1701
1702 if (found)
1703 {
1704 /* if ((uint) line == startLine && foundAt + myMatchLen >= (uint) col
1705 && line == selectStart.line() && foundAt == (uint) selectStart.col()
1706 && line == selectEnd.line() && foundAt + myMatchLen == (uint) selectEnd.col())
1707 {
1708 // To avoid getting stuck at one match we skip a match if it is already
1709 // selected (most likely because it has just been found).
1710 if (foundAt > 0)
1711 col = foundAt - 1;
1712 else {
1713 if (--line >= 0)
1714 col = lineLength(line);
1715 }
1716 continue;
1717 }*/
1718
1719 (*foundAtLine) = line;
1720 (*foundAtCol) = foundAt;
1721 (*matchLen) = myMatchLen;
1722 return true;
1723 }
1724
1725 if (line >= 1)
1726 col = lineLength(line-1);
1727
1728 line--;
1729 }
1730 }
1731
1732 return false;
1733}
1734
1735bool KateDocument::searchText (unsigned int startLine, unsigned int startCol, const TQRegExp &regexp, unsigned int *foundAtLine, unsigned int *foundAtCol, unsigned int *matchLen, bool backwards)
1736{
1737 kdDebug(13020)<<"KateDocument::searchText( "<<startLine<<", "<<startCol<<", "<<TQString(regexp.pattern())<<", "<<backwards<<" )"<<endl;
1738 if (regexp.isEmpty() || !regexp.isValid())
1739 return false;
1740
1741 int line = startLine;
1742 int col = startCol;
1743
1744 if (!backwards)
1745 {
1746 int searchEnd = lastLine();
1747
1748 while (line <= searchEnd)
1749 {
1750 KateTextLine::Ptr textLine = m_buffer->plainLine(line);
1751
1752 if (!textLine)
1753 return false;
1754
1755 uint foundAt, myMatchLen;
1756 bool found = textLine->searchText (col, regexp, &foundAt, &myMatchLen, false);
1757
1758 if (found)
1759 {
1760 // A special case which can only occur when searching with a regular expression consisting
1761 // only of a lookahead (e.g. ^(?=\{) for a function beginning without selecting '{').
1762 if (myMatchLen == 0 && (uint) line == startLine && foundAt == (uint) col)
1763 {
1764 if (col < lineLength(line))
1765 col++;
1766 else {
1767 line++;
1768 col = 0;
1769 }
1770 continue;
1771 }
1772
1773 (*foundAtLine) = line;
1774 (*foundAtCol) = foundAt;
1775 (*matchLen) = myMatchLen;
1776 return true;
1777 }
1778
1779 col = 0;
1780 line++;
1781 }
1782 }
1783 else
1784 {
1785 // backward search
1786 int searchEnd = 0;
1787
1788 while (line >= searchEnd)
1789 {
1790 KateTextLine::Ptr textLine = m_buffer->plainLine(line);
1791
1792 if (!textLine)
1793 return false;
1794
1795 uint foundAt, myMatchLen;
1796 bool found = textLine->searchText (col, regexp, &foundAt, &myMatchLen, true);
1797
1798 if (found)
1799 {
1800 /*if ((uint) line == startLine && foundAt + myMatchLen >= (uint) col
1801 && line == selectStart.line() && foundAt == (uint) selectStart.col()
1802 && line == selectEnd.line() && foundAt + myMatchLen == (uint) selectEnd.col())
1803 {
1804 // To avoid getting stuck at one match we skip a match if it is already
1805 // selected (most likely because it has just been found).
1806 if (foundAt > 0)
1807 col = foundAt - 1;
1808 else {
1809 if (--line >= 0)
1810 col = lineLength(line);
1811 }
1812 continue;
1813 }*/
1814
1815 (*foundAtLine) = line;
1816 (*foundAtCol) = foundAt;
1817 (*matchLen) = myMatchLen;
1818 return true;
1819 }
1820
1821 if (line >= 1)
1822 col = lineLength(line-1);
1823
1824 line--;
1825 }
1826 }
1827
1828 return false;
1829}
1830//END
1831
1832//BEGIN KTextEditor::HighlightingInterface stuff
1833
1834uint KateDocument::hlMode ()
1835{
1836 return KateHlManager::self()->findHl(highlight());
1837}
1838
1839bool KateDocument::setHlMode (uint mode)
1840{
1841 m_buffer->setHighlight (mode);
1842
1843 if (true)
1844 {
1845 setDontChangeHlOnSave();
1846 return true;
1847 }
1848
1849 return false;
1850}
1851
1852void KateDocument::bufferHlChanged ()
1853{
1854 // update all views
1855 makeAttribs(false);
1856
1857 emit hlChanged();
1858}
1859
1860uint KateDocument::hlModeCount ()
1861{
1862 return KateHlManager::self()->highlights();
1863}
1864
1865TQString KateDocument::hlModeName (uint mode)
1866{
1867 return KateHlManager::self()->hlName (mode);
1868}
1869
1870TQString KateDocument::hlModeSectionName (uint mode)
1871{
1872 return KateHlManager::self()->hlSection (mode);
1873}
1874
1875void KateDocument::setDontChangeHlOnSave()
1876{
1877 hlSetByUser = true;
1878}
1879//END
1880
1881//BEGIN KTextEditor::ConfigInterface stuff
1882void KateDocument::readConfig(TDEConfig *config)
1883{
1884 config->setGroup("Kate Document Defaults");
1885
1886 // read max loadable blocks, more blocks will be swapped out
1887 KateBuffer::setMaxLoadedBlocks (config->readNumEntry("Maximal Loaded Blocks", KateBuffer::maxLoadedBlocks()));
1888
1889 KateDocumentConfig::global()->readConfig (config);
1890
1891 config->setGroup("Kate View Defaults");
1892 KateViewConfig::global()->readConfig (config);
1893
1894 config->setGroup("Kate Renderer Defaults");
1895 KateRendererConfig::global()->readConfig (config);
1896}
1897
1898void KateDocument::writeConfig(TDEConfig *config)
1899{
1900 config->setGroup("Kate Document Defaults");
1901
1902 // write max loadable blocks, more blocks will be swapped out
1903 config->writeEntry("Maximal Loaded Blocks", KateBuffer::maxLoadedBlocks());
1904
1905 KateDocumentConfig::global()->writeConfig (config);
1906
1907 config->setGroup("Kate View Defaults");
1908 KateViewConfig::global()->writeConfig (config);
1909
1910 config->setGroup("Kate Renderer Defaults");
1911 KateRendererConfig::global()->writeConfig (config);
1912}
1913
1914void KateDocument::readConfig()
1915{
1916 TDEConfig *config = kapp->config();
1917 readConfig (config);
1918}
1919
1920void KateDocument::writeConfig()
1921{
1922 TDEConfig *config = kapp->config();
1923 writeConfig (config);
1924 config->sync();
1925}
1926
1927void KateDocument::readSessionConfig(TDEConfig *tdeconfig)
1928{
1929 // restore the url
1930 KURL url (tdeconfig->readEntry("URL"));
1931
1932 // get the encoding
1933 TQString tmpenc=tdeconfig->readEntry("Encoding");
1934 if (!tmpenc.isEmpty() && (tmpenc != encoding()))
1935 setEncoding(tmpenc);
1936
1937 // open the file if url valid
1938 if (!url.isEmpty() && url.isValid())
1939 openURL (url);
1940
1941 // restore the hl stuff
1942 m_buffer->setHighlight(KateHlManager::self()->nameFind(tdeconfig->readEntry("Highlighting")));
1943
1944 if (hlMode() > 0)
1945 hlSetByUser = true;
1946
1947 // indent mode
1948 config()->setIndentationMode( (uint)tdeconfig->readNumEntry("Indentation Mode", config()->indentationMode() ) );
1949
1950 // Restore Bookmarks
1951 TQValueList<int> marks = tdeconfig->readIntListEntry("Bookmarks");
1952 for( uint i = 0; i < marks.count(); i++ )
1953 addMark( marks[i], KateDocument::markType01 );
1954}
1955
1956void KateDocument::writeSessionConfig(TDEConfig *tdeconfig)
1957{
1958 if ( m_url.isLocalFile() && !TDEGlobal::dirs()->relativeLocation("tmp", m_url.path()).startsWith("/"))
1959 return;
1960 // save url
1961 tdeconfig->writeEntry("URL", m_url.prettyURL() );
1962
1963 // save encoding
1964 tdeconfig->writeEntry("Encoding",encoding());
1965
1966 // save hl
1967 tdeconfig->writeEntry("Highlighting", highlight()->name());
1968
1969 tdeconfig->writeEntry("Indentation Mode", config()->indentationMode() );
1970
1971 // Save Bookmarks
1972 TQValueList<int> marks;
1973 for( TQIntDictIterator<KTextEditor::Mark> it( m_marks );
1974 it.current() && it.current()->type & KTextEditor::MarkInterface::markType01;
1975 ++it )
1976 marks << it.current()->line;
1977
1978 tdeconfig->writeEntry( "Bookmarks", marks );
1979}
1980
1981void KateDocument::configDialog()
1982{
1983 KDialogBase *kd = new KDialogBase ( KDialogBase::IconList,
1984 i18n("Configure"),
1985 KDialogBase::Ok | KDialogBase::Cancel | KDialogBase::Help,
1986 KDialogBase::Ok,
1987 kapp->mainWidget() );
1988
1989#ifndef TQ_WS_WIN //TODO: reenable
1990 KWin::setIcons( kd->winId(), kapp->icon(), kapp->miniIcon() );
1991#endif
1992
1993 TQPtrList<KTextEditor::ConfigPage> editorPages;
1994
1995 for (uint i = 0; i < KTextEditor::configInterfaceExtension (this)->configPages (); i++)
1996 {
1997 TQStringList path;
1998 path.clear();
1999 path << KTextEditor::configInterfaceExtension (this)->configPageName (i);
2000 TQVBox *page = kd->addVBoxPage(path, KTextEditor::configInterfaceExtension (this)->configPageFullName (i),
2001 KTextEditor::configInterfaceExtension (this)->configPagePixmap(i, TDEIcon::SizeMedium) );
2002
2003 editorPages.append (KTextEditor::configInterfaceExtension (this)->configPage(i, page));
2004 }
2005
2006 if (kd->exec())
2007 {
2008 KateDocumentConfig::global()->configStart ();
2009 KateViewConfig::global()->configStart ();
2010 KateRendererConfig::global()->configStart ();
2011
2012 for (uint i=0; i<editorPages.count(); i++)
2013 {
2014 editorPages.at(i)->apply();
2015 }
2016
2017 KateDocumentConfig::global()->configEnd ();
2018 KateViewConfig::global()->configEnd ();
2019 KateRendererConfig::global()->configEnd ();
2020
2021 writeConfig ();
2022 }
2023
2024 delete kd;
2025}
2026
2027uint KateDocument::mark( uint line )
2028{
2029 if( !m_marks[line] )
2030 return 0;
2031 return m_marks[line]->type;
2032}
2033
2034void KateDocument::setMark( uint line, uint markType )
2035{
2036 clearMark( line );
2037 addMark( line, markType );
2038}
2039
2040void KateDocument::clearMark( uint line )
2041{
2042 if( line > lastLine() )
2043 return;
2044
2045 if( !m_marks[line] )
2046 return;
2047
2048 KTextEditor::Mark* mark = m_marks.take( line );
2049 emit markChanged( *mark, MarkRemoved );
2050 emit marksChanged();
2051 delete mark;
2052 tagLines( line, line );
2053 repaintViews(true);
2054}
2055
2056void KateDocument::addMark( uint line, uint markType )
2057{
2058 if( line > lastLine())
2059 return;
2060
2061 if( markType == 0 )
2062 return;
2063
2064 if( m_marks[line] ) {
2065 KTextEditor::Mark* mark = m_marks[line];
2066
2067 // Remove bits already set
2068 markType &= ~mark->type;
2069
2070 if( markType == 0 )
2071 return;
2072
2073 // Add bits
2074 mark->type |= markType;
2075 } else {
2076 KTextEditor::Mark *mark = new KTextEditor::Mark;
2077 mark->line = line;
2078 mark->type = markType;
2079 m_marks.insert( line, mark );
2080 }
2081
2082 // Emit with a mark having only the types added.
2083 KTextEditor::Mark temp;
2084 temp.line = line;
2085 temp.type = markType;
2086 emit markChanged( temp, MarkAdded );
2087
2088 emit marksChanged();
2089 tagLines( line, line );
2090 repaintViews(true);
2091}
2092
2093void KateDocument::removeMark( uint line, uint markType )
2094{
2095 if( line > lastLine() )
2096 return;
2097 if( !m_marks[line] )
2098 return;
2099
2100 KTextEditor::Mark* mark = m_marks[line];
2101
2102 // Remove bits not set
2103 markType &= mark->type;
2104
2105 if( markType == 0 )
2106 return;
2107
2108 // Subtract bits
2109 mark->type &= ~markType;
2110
2111 // Emit with a mark having only the types removed.
2112 KTextEditor::Mark temp;
2113 temp.line = line;
2114 temp.type = markType;
2115 emit markChanged( temp, MarkRemoved );
2116
2117 if( mark->type == 0 )
2118 m_marks.remove( line );
2119
2120 emit marksChanged();
2121 tagLines( line, line );
2122 repaintViews(true);
2123}
2124
2125TQPtrList<KTextEditor::Mark> KateDocument::marks()
2126{
2127 TQPtrList<KTextEditor::Mark> list;
2128
2129 for( TQIntDictIterator<KTextEditor::Mark> it( m_marks );
2130 it.current(); ++it ) {
2131 list.append( it.current() );
2132 }
2133
2134 return list;
2135}
2136
2137void KateDocument::clearMarks()
2138{
2139 for( TQIntDictIterator<KTextEditor::Mark> it( m_marks );
2140 it.current(); ++it ) {
2141 KTextEditor::Mark* mark = it.current();
2142 emit markChanged( *mark, MarkRemoved );
2143 tagLines( mark->line, mark->line );
2144 }
2145
2146 m_marks.clear();
2147
2148 emit marksChanged();
2149 repaintViews(true);
2150}
2151
2152void KateDocument::setPixmap( MarkInterface::MarkTypes type, const TQPixmap& pixmap )
2153{
2154 m_markPixmaps.replace( type, new TQPixmap( pixmap ) );
2155}
2156
2157void KateDocument::setDescription( MarkInterface::MarkTypes type, const TQString& description )
2158{
2159 m_markDescriptions.replace( type, new TQString( description ) );
2160}
2161
2162TQPixmap *KateDocument::markPixmap( MarkInterface::MarkTypes type )
2163{
2164 return m_markPixmaps[type];
2165}
2166
2167TQColor KateDocument::markColor( MarkInterface::MarkTypes type )
2168{
2169 uint reserved = (0x1 << KTextEditor::MarkInterface::reservedMarkersCount()) - 1;
2170 if ((uint)type >= (uint)markType01 && (uint)type <= reserved) {
2171 return KateRendererConfig::global()->lineMarkerColor(type);
2172 } else {
2173 return TQColor();
2174 }
2175}
2176
2177TQString KateDocument::markDescription( MarkInterface::MarkTypes type )
2178{
2179 if( m_markDescriptions[type] )
2180 return *m_markDescriptions[type];
2181 return TQString::null;
2182}
2183
2184void KateDocument::setMarksUserChangable( uint markMask )
2185{
2186 m_editableMarks = markMask;
2187}
2188
2189uint KateDocument::editableMarks()
2190{
2191 return m_editableMarks;
2192}
2193//END
2194
2195//BEGIN KTextEditor::PrintInterface stuff
2196bool KateDocument::printDialog ()
2197{
2198 return KatePrinter::print (this);
2199}
2200
2201bool KateDocument::print ()
2202{
2203 return KatePrinter::print (this);
2204}
2205//END
2206
2207//BEGIN KTextEditor::DocumentInfoInterface (### unfinished)
2208TQString KateDocument::mimeType()
2209{
2210 KMimeType::Ptr result = KMimeType::defaultMimeTypePtr();
2211
2212 // if the document has a URL, try KMimeType::findByURL
2213 if ( ! m_url.isEmpty() )
2214 result = KMimeType::findByURL( m_url );
2215
2216 else if ( m_url.isEmpty() || ! m_url.isLocalFile() )
2217 result = mimeTypeForContent();
2218
2219 return result->name();
2220}
2221
2222// TODO implement this -- how to calculate?
2223long KateDocument::fileSize()
2224{
2225 return 0;
2226}
2227
2228// TODO implement this
2229TQString KateDocument::niceFileSize()
2230{
2231 return "UNKNOWN";
2232}
2233
2234KMimeType::Ptr KateDocument::mimeTypeForContent()
2235{
2236 TQByteArray buf (1024);
2237 uint bufpos = 0;
2238
2239 for (uint i=0; i < numLines(); i++)
2240 {
2241 TQString line = textLine( i );
2242 uint len = line.length() + 1;
2243
2244 if (bufpos + len > 1024)
2245 len = 1024 - bufpos;
2246
2247 memcpy(&buf[bufpos], (line + "\n").latin1(), len);
2248
2249 bufpos += len;
2250
2251 if (bufpos >= 1024)
2252 break;
2253 }
2254 buf.resize( bufpos );
2255
2256 int accuracy = 0;
2257 return KMimeType::findByContent( buf, &accuracy );
2258}
2259//END KTextEditor::DocumentInfoInterface
2260
2261
2262//BEGIN KParts::ReadWrite stuff
2263
2264bool KateDocument::openURL( const KURL &url )
2265{
2266// kdDebug(13020)<<"KateDocument::openURL( "<<url.prettyURL()<<")"<<endl;
2267 // no valid URL
2268 if ( !url.isValid() )
2269 return false;
2270
2271 // could not close old one
2272 if ( !closeURL() )
2273 return false;
2274
2275 // set my url
2276 m_url = url;
2277
2278 if ( m_url.isLocalFile() )
2279 {
2280 // local mode, just like in kpart
2281
2282 m_file = m_url.path();
2283
2284 emit started( 0 );
2285
2286 if (openFile())
2287 {
2288 emit completed();
2289 emit setWindowCaption( m_url.prettyURL() );
2290
2291 return true;
2292 }
2293
2294 return false;
2295 }
2296 else
2297 {
2298 // remote mode
2299
2300 m_bTemp = true;
2301
2302 m_tempFile = new KTempFile ();
2303 m_file = m_tempFile->name();
2304
2305 m_job = TDEIO::get ( url, false, isProgressInfoEnabled() );
2306
2307 // connect to slots
2308 connect( m_job, TQ_SIGNAL( data( TDEIO::Job*, const TQByteArray& ) ),
2309 TQ_SLOT( slotDataKate( TDEIO::Job*, const TQByteArray& ) ) );
2310
2311 connect( m_job, TQ_SIGNAL( result( TDEIO::Job* ) ),
2312 TQ_SLOT( slotFinishedKate( TDEIO::Job* ) ) );
2313
2314 TQWidget *w = widget ();
2315 if (!w && !m_views.isEmpty ())
2316 w = m_views.first();
2317
2318 if (w)
2319 m_job->setWindow (w->topLevelWidget());
2320
2321 emit started( m_job );
2322
2323 return true;
2324 }
2325}
2326
2327void KateDocument::slotDataKate ( TDEIO::Job *, const TQByteArray &data )
2328{
2329// kdDebug(13020) << "KateDocument::slotData" << endl;
2330
2331 if (!m_tempFile || !m_tempFile->file())
2332 return;
2333
2334 m_tempFile->file()->writeBlock (data);
2335}
2336
2337void KateDocument::slotFinishedKate ( TDEIO::Job * job )
2338{
2339// kdDebug(13020) << "KateDocument::slotJobFinished" << endl;
2340
2341 if (!m_tempFile)
2342 return;
2343
2344 delete m_tempFile;
2345 m_tempFile = 0;
2346 m_job = 0;
2347
2348 if (job->error())
2349 emit canceled( job->errorString() );
2350 else
2351 {
2352 if ( openFile(job) )
2353 emit setWindowCaption( m_url.prettyURL() );
2354 emit completed();
2355 }
2356}
2357
2358void KateDocument::abortLoadKate()
2359{
2360 if ( m_job )
2361 {
2362 kdDebug(13020) << "Aborting job " << m_job << endl;
2363 m_job->kill();
2364 m_job = 0;
2365 }
2366
2367 delete m_tempFile;
2368 m_tempFile = 0;
2369}
2370
2371bool KateDocument::openFile()
2372{
2373 return openFile (0);
2374}
2375
2376bool KateDocument::openFile(TDEIO::Job * job)
2377{
2378 m_loading = true;
2379 // add new m_file to dirwatch
2380 activateDirWatch ();
2381
2382 //
2383 // use metadata
2384 //
2385 if (job)
2386 {
2387 TQString metaDataCharset = job->queryMetaData("charset");
2388
2389 // only overwrite config if nothing set
2390 if (!metaDataCharset.isEmpty () && (!m_config->isSetEncoding() || m_config->encoding().isEmpty()))
2391 setEncoding (metaDataCharset);
2392 }
2393
2394 //
2395 // service type magic to get encoding right
2396 //
2397 TQString serviceType = m_extension->urlArgs().serviceType.simplifyWhiteSpace();
2398 int pos = serviceType.find(';');
2399 if (pos != -1)
2400 setEncoding (serviceType.mid(pos+1));
2401
2402 // if the encoding is set here - on the command line/from the dialog/from TDEIO
2403 // we prevent file type and document variables from changing it
2404 bool encodingSticky = m_encodingSticky;
2405 m_encodingSticky = m_config->isSetEncoding();
2406
2407 // Try getting the filetype here, so that variables does not have to be reset.
2408 int fileTypeFound = KateFactory::self()->fileTypeManager()->fileType (this);
2409 if ( fileTypeFound > -1 )
2410 updateFileType( fileTypeFound );
2411
2412 // read dir config (if possible and wanted)
2413 if (!m_reloading)
2414 readDirConfig ();
2415
2416 // do we have success ?
2417 bool success = m_buffer->openFile (m_file);
2418 //
2419 // yeah, success
2420 //
2421 m_loading = false; // done reading file.
2422 if (success)
2423 {
2424 /*if (highlight() && !m_url.isLocalFile()) {
2425 // The buffer's highlighting gets nuked by KateBuffer::clear()
2426 m_buffer->setHighlight(m_highlight);
2427 }*/
2428
2429 // update our hl type if needed
2430 if (!hlSetByUser)
2431 {
2432 int hl (KateHlManager::self()->detectHighlighting (this));
2433
2434 if (hl >= 0)
2435 m_buffer->setHighlight(hl);
2436 }
2437
2438 // update file type if we haven't allready done so.
2439 if ( fileTypeFound < 0 )
2440 updateFileType (KateFactory::self()->fileTypeManager()->fileType (this));
2441
2442 // read vars
2443 readVariables();
2444
2445 // update the md5 digest
2446 createDigest( m_digest );
2447 }
2448
2449 //
2450 // update views
2451 //
2452 for (KateView * view = m_views.first(); view != 0L; view = m_views.next() )
2453 {
2454 view->updateView(true);
2455 }
2456
2457 //
2458 // emit the signal we need for example for kate app
2459 //
2460 emit fileNameChanged ();
2461
2462 //
2463 // set doc name, dummy value as arg, don't need it
2464 //
2465 setDocName (TQString::null);
2466
2467 //
2468 // to houston, we are not modified
2469 //
2470 if (m_modOnHd)
2471 {
2472 m_modOnHd = false;
2473 m_modOnHdReason = 0;
2474 emit modifiedOnDisc (this, m_modOnHd, 0);
2475 }
2476
2477 //
2478 // display errors
2479 //
2480 if (s_openErrorDialogsActivated)
2481 {
2482 if (!success && m_buffer->loadingBorked())
2483 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()));
2484 else if (!success)
2485 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()));
2486 }
2487
2488 // warn -> opened binary file!!!!!!!
2489 if (m_buffer->binary())
2490 {
2491 // this file can't be saved again without killing it
2492 setReadWrite( false );
2493
2494 KMessageBox::information (widget()
2495 , i18n ("The file %1 is a binary, saving it will result in a corrupt file.").arg(m_url.url())
2496 , i18n ("Binary File Opened")
2497 , "Binary File Opened Warning");
2498 }
2499
2500 m_encodingSticky = encodingSticky;
2501
2502 //
2503 // return the success
2504 //
2505 return success;
2506}
2507
2508bool KateDocument::save()
2509{
2510 bool l ( url().isLocalFile() );
2511
2512 if ( ( l && config()->backupFlags() & KateDocumentConfig::LocalFiles )
2513 || ( ! l && config()->backupFlags() & KateDocumentConfig::RemoteFiles ) )
2514 {
2515 KURL u( url() );
2516 u.setFileName( config()->backupPrefix() + url().fileName() + config()->backupSuffix() );
2517
2518 kdDebug () << "backup src file name: " << url() << endl;
2519 kdDebug () << "backup dst file name: " << u << endl;
2520
2521 // get the right permissions, start with safe default
2522 mode_t perms = 0600;
2523 TDEIO::UDSEntry fentry;
2524 if (TDEIO::NetAccess::stat (url(), fentry, kapp->mainWidget()))
2525 {
2526 kdDebug () << "stating succesfull: " << url() << endl;
2527 KFileItem item (fentry, url());
2528 perms = item.permissions();
2529 }
2530
2531 // first del existing file if any, than copy over the file we have
2532 // failure if a: the existing file could not be deleted, b: the file could not be copied
2533 if ( (!TDEIO::NetAccess::exists( u, false, kapp->mainWidget() ) || TDEIO::NetAccess::del( u, kapp->mainWidget() ))
2534 && TDEIO::NetAccess::file_copy( url(), u, perms, true, false, kapp->mainWidget() ) )
2535 {
2536 kdDebug(13020)<<"backing up successfull ("<<url().prettyURL()<<" -> "<<u.prettyURL()<<")"<<endl;
2537 }
2538 else
2539 {
2540 kdDebug(13020)<<"backing up failed ("<<url().prettyURL()<<" -> "<<u.prettyURL()<<")"<<endl;
2541 // FIXME: notify user for real ;)
2542 }
2543 }
2544
2545 return KParts::ReadWritePart::save();
2546}
2547
2548bool KateDocument::saveFile()
2549{
2550 //
2551 // we really want to save this file ?
2552 //
2553 if (m_buffer->loadingBorked() && (KMessageBox::warningContinueCancel(widget(),
2554 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))
2555 return false;
2556
2557 //
2558 // warn -> try to save binary file!!!!!!!
2559 //
2560 if (m_buffer->binary() && (KMessageBox::warningContinueCancel (widget()
2561 , i18n ("The file %1 is a binary, saving it will result in a corrupt file.").arg(m_url.url())
2562 , i18n ("Trying to Save Binary File")
2563 , i18n("Save Nevertheless"), "Binary File Save Warning") != KMessageBox::Continue))
2564 return false;
2565
2566 if ( !url().isEmpty() )
2567 {
2568 if (s_fileChangedDialogsActivated && m_modOnHd)
2569 {
2570 TQString str = reasonedMOHString() + "\n\n";
2571
2572 if (!isModified())
2573 {
2574 if (KMessageBox::warningContinueCancel(0,
2575 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)
2576 return false;
2577 }
2578 else
2579 {
2580 if (KMessageBox::warningContinueCancel(0,
2581 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)
2582 return false;
2583 }
2584 }
2585 }
2586
2587 //
2588 // can we encode it if we want to save it ?
2589 //
2590 if (!m_buffer->canEncode ()
2591 && (KMessageBox::warningContinueCancel(0,
2592 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))
2593 {
2594 return false;
2595 }
2596
2597 // remove file from dirwatch
2598 deactivateDirWatch ();
2599
2600 //
2601 // try to save
2602 //
2603 bool success = m_buffer->saveFile (m_file);
2604
2605 // update the md5 digest
2606 createDigest( m_digest );
2607
2608 // add m_file again to dirwatch
2609 activateDirWatch ();
2610
2611 //
2612 // hurray, we had success, do stuff we need
2613 //
2614 if (success)
2615 {
2616 // update our hl type if needed
2617 if (!hlSetByUser)
2618 {
2619 int hl (KateHlManager::self()->detectHighlighting (this));
2620
2621 if (hl >= 0)
2622 m_buffer->setHighlight(hl);
2623 }
2624
2625 // read our vars
2626 readVariables();
2627 }
2628
2629 //
2630 // we are not modified
2631 //
2632 if (success && m_modOnHd)
2633 {
2634 m_modOnHd = false;
2635 m_modOnHdReason = 0;
2636 emit modifiedOnDisc (this, m_modOnHd, 0);
2637 }
2638
2639 //
2640 // display errors
2641 //
2642 if (!success)
2643 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()));
2644
2645 //
2646 // return success
2647 //
2648 return success;
2649}
2650
2651bool KateDocument::saveAs( const KURL &u )
2652{
2653 TQString oldDir = url().directory();
2654
2655 if ( KParts::ReadWritePart::saveAs( u ) )
2656 {
2657 // null means base on filename
2658 setDocName( TQString::null );
2659
2660 if ( u.directory() != oldDir )
2661 readDirConfig();
2662
2663 emit fileNameChanged();
2664 emit nameChanged((Kate::Document *) this);
2665
2666 return true;
2667 }
2668
2669 return false;
2670}
2671
2672void KateDocument::readDirConfig ()
2673{
2674 int depth = config()->searchDirConfigDepth ();
2675
2676 if (m_url.isLocalFile() && (depth > -1))
2677 {
2678 TQString currentDir = TQFileInfo (m_file).dirPath();
2679
2680 // only search as deep as specified or not at all ;)
2681 while (depth > -1)
2682 {
2683 kdDebug (13020) << "search for config file in path: " << currentDir << endl;
2684
2685 // try to open config file in this dir
2686 TQFile f (currentDir + "/.kateconfig");
2687
2688 if (f.open (IO_ReadOnly))
2689 {
2690 TQTextStream stream (&f);
2691
2692 uint linesRead = 0;
2693 TQString line = stream.readLine();
2694 while ((linesRead < 32) && !line.isNull())
2695 {
2696 readVariableLine( line );
2697
2698 line = stream.readLine();
2699
2700 linesRead++;
2701 }
2702
2703 break;
2704 }
2705
2706 TQString newDir = TQFileInfo (currentDir).dirPath();
2707
2708 // bail out on looping (for example reached /)
2709 if (currentDir == newDir)
2710 break;
2711
2712 currentDir = newDir;
2713 --depth;
2714 }
2715 }
2716}
2717
2718void KateDocument::activateDirWatch ()
2719{
2720 // same file as we are monitoring, return
2721 if (m_file == m_dirWatchFile)
2722 return;
2723
2724 // remove the old watched file
2725 deactivateDirWatch ();
2726
2727 // add new file if needed
2728 if (m_url.isLocalFile() && !m_file.isEmpty())
2729 {
2730 KateFactory::self()->dirWatch ()->addFile (m_file);
2731 m_dirWatchFile = m_file;
2732 }
2733}
2734
2735void KateDocument::deactivateDirWatch ()
2736{
2737 if (!m_dirWatchFile.isEmpty())
2738 KateFactory::self()->dirWatch ()->removeFile (m_dirWatchFile);
2739
2740 m_dirWatchFile = TQString::null;
2741}
2742
2743bool KateDocument::closeURL()
2744{
2745 abortLoadKate();
2746
2747 //
2748 // file mod on hd
2749 //
2750 if ( !m_reloading && !url().isEmpty() )
2751 {
2752 if (s_fileChangedDialogsActivated && m_modOnHd)
2753 {
2754 if (!(KMessageBox::warningContinueCancel(
2755 widget(),
2756 reasonedMOHString() + "\n\n" + i18n("Do you really want to continue to close this file? Data loss may occur."),
2757 i18n("Possible Data Loss"), i18n("Close Nevertheless"),
2758 TQString("kate_close_modonhd_%1").arg( m_modOnHdReason ) ) == KMessageBox::Continue))
2759 return false;
2760 }
2761 }
2762
2763 //
2764 // first call the normal tdeparts implementation
2765 //
2766 if (!KParts::ReadWritePart::closeURL ())
2767 return false;
2768
2769 // remove file from dirwatch
2770 deactivateDirWatch ();
2771
2772 //
2773 // empty url + filename
2774 //
2775 m_url = KURL ();
2776 m_file = TQString::null;
2777
2778 // we are not modified
2779 if (m_modOnHd)
2780 {
2781 m_modOnHd = false;
2782 m_modOnHdReason = 0;
2783 emit modifiedOnDisc (this, m_modOnHd, 0);
2784 }
2785
2786 // clear the buffer
2787 m_buffer->clear();
2788
2789 // remove all marks
2790 clearMarks ();
2791
2792 // clear undo/redo history
2793 clearUndo();
2794 clearRedo();
2795
2796 // no, we are no longer modified
2797 setModified(false);
2798
2799 // we have no longer any hl
2800 m_buffer->setHighlight(0);
2801
2802 // update all our views
2803 for (KateView * view = m_views.first(); view != 0L; view = m_views.next() )
2804 {
2805 // Explicitly call the internal version because we don't want this to look like
2806 // an external request (and thus have the view not TQWidget::scroll()ed.
2807 view->setCursorPositionInternal(0, 0, 1, false);
2808 view->clearSelection();
2809 view->updateView(true);
2810 }
2811
2812 // uh, filename changed
2813 emit fileNameChanged ();
2814
2815 // update doc name
2816 setDocName (TQString::null);
2817
2818 // success
2819 return true;
2820}
2821
2822void KateDocument::setReadWrite( bool rw )
2823{
2824 if (isReadWrite() != rw)
2825 {
2826 KParts::ReadWritePart::setReadWrite (rw);
2827
2828 for( KateView* view = m_views.first(); view != 0L; view = m_views.next() )
2829 {
2830 view->slotUpdate();
2831 view->slotReadWriteChanged ();
2832 }
2833 }
2834}
2835
2836void KateDocument::setModified(bool m) {
2837
2838 if (isModified() != m) {
2839 KParts::ReadWritePart::setModified (m);
2840
2841 for( KateView* view = m_views.first(); view != 0L; view = m_views.next() )
2842 {
2843 view->slotUpdate();
2844 }
2845
2846 emit modifiedChanged ();
2847 emit modStateChanged ((Kate::Document *)this);
2848 }
2849 if ( m == false )
2850 {
2851 if ( ! undoItems.isEmpty() )
2852 {
2853 lastUndoGroupWhenSaved = undoItems.last();
2854 }
2855
2856 if ( ! redoItems.isEmpty() )
2857 {
2858 lastRedoGroupWhenSaved = redoItems.last();
2859 }
2860
2861 docWasSavedWhenUndoWasEmpty = undoItems.isEmpty();
2862 docWasSavedWhenRedoWasEmpty = redoItems.isEmpty();
2863 }
2864}
2865//END
2866
2867//BEGIN Kate specific stuff ;)
2868
2869void KateDocument::makeAttribs(bool needInvalidate)
2870{
2871 for (uint z = 0; z < m_views.count(); z++)
2872 m_views.at(z)->renderer()->updateAttributes ();
2873
2874 if (needInvalidate)
2875 m_buffer->invalidateHighlighting();
2876
2877 tagAll ();
2878}
2879
2880// the attributes of a hl have changed, update
2881void KateDocument::internalHlChanged()
2882{
2883 makeAttribs();
2884}
2885
2886void KateDocument::addView(KTextEditor::View *view) {
2887 if (!view)
2888 return;
2889
2890 m_views.append( (KateView *) view );
2891 m_textEditViews.append( view );
2892
2893 // apply the view & renderer vars from the file type
2894 const KateFileType *t = 0;
2895 if ((m_fileType > -1) && (t = KateFactory::self()->fileTypeManager()->fileType(m_fileType)))
2896 readVariableLine (t->varLine, true);
2897
2898 // apply the view & renderer vars from the file
2899 readVariables (true);
2900
2901 m_activeView = (KateView *) view;
2902}
2903
2904void KateDocument::removeView(KTextEditor::View *view) {
2905 if (!view)
2906 return;
2907
2908 if (m_activeView == view)
2909 m_activeView = 0L;
2910
2911 m_views.removeRef( (KateView *) view );
2912 m_textEditViews.removeRef( view );
2913}
2914
2915void KateDocument::addSuperCursor(KateSuperCursor *cursor, bool privateC) {
2916 if (!cursor)
2917 return;
2918
2919 m_superCursors.append( cursor );
2920
2921 if (!privateC)
2922 myCursors.append( cursor );
2923}
2924
2925void KateDocument::removeSuperCursor(KateSuperCursor *cursor, bool privateC) {
2926 if (!cursor)
2927 return;
2928
2929 if (!privateC)
2930 myCursors.removeRef( cursor );
2931
2932 m_superCursors.removeRef( cursor );
2933}
2934
2935bool KateDocument::ownedView(KateView *view) {
2936 // do we own the given view?
2937 return (m_views.containsRef(view) > 0);
2938}
2939
2940bool KateDocument::isLastView(int numViews) {
2941 return ((int) m_views.count() == numViews);
2942}
2943
2944uint KateDocument::currentColumn( const KateTextCursor& cursor )
2945{
2946 KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
2947
2948 if (textLine)
2949 return textLine->cursorX(cursor.col(), config()->tabWidth());
2950 else
2951 return 0;
2952}
2953
2954bool KateDocument::typeChars ( KateView *view, const TQString &chars )
2955{
2956 KateTextLine::Ptr textLine = m_buffer->plainLine(view->cursorLine ());
2957
2958 if (!textLine)
2959 return false;
2960
2961 bool bracketInserted = false;
2962 TQString buf;
2963 TQChar c;
2964
2965 for( uint z = 0; z < chars.length(); z++ )
2966 {
2967 TQChar ch = c = chars[z];
2968 if (ch.isPrint() || ch == '\t')
2969 {
2970 buf.append (ch);
2971
2972 if (!bracketInserted && (config()->configFlags() & KateDocument::cfAutoBrackets))
2973 {
2974 TQChar end_ch;
2975 bool complete = true;
2976 TQChar prevChar = textLine->getChar(view->cursorColumnReal()-1);
2977 TQChar nextChar = textLine->getChar(view->cursorColumnReal());
2978 switch(ch) {
2979 case '(': end_ch = ')'; break;
2980 case '[': end_ch = ']'; break;
2981 case '{': end_ch = '}'; break;
2982 case '\'':end_ch = '\'';break;
2983 case '"': end_ch = '"'; break;
2984 default: complete = false;
2985 }
2986 if (complete)
2987 {
2988 if (view->hasSelection())
2989 { // there is a selection, enclose the selection
2990 buf.append (view->selection());
2991 buf.append (end_ch);
2992 bracketInserted = true;
2993 }
2994 else
2995 { // no selection, check whether we should better refuse to complete
2996 if ( ( (ch == '\'' || ch == '"') &&
2997 (prevChar.isLetterOrNumber() || prevChar == ch) )
2998 || nextChar.isLetterOrNumber()
2999 || (nextChar == end_ch && prevChar != ch) )
3000 {
3001 kdDebug(13020) << "AutoBracket refused before: " << nextChar << "\n";
3002 }
3003 else
3004 {
3005 buf.append (end_ch);
3006 bracketInserted = true;
3007 }
3008 }
3009 }
3010 }
3011 }
3012 }
3013
3014 if (buf.isEmpty())
3015 return false;
3016
3017 editStart ();
3018
3019 if (!view->config()->persistentSelection() && view->hasSelection() )
3020 view->removeSelectedText();
3021
3022 int oldLine = view->cursorLine ();
3023 int oldCol = view->cursorColumnReal ();
3024
3025
3026 if (config()->configFlags() & KateDocument::cfOvr)
3027 removeText (view->cursorLine(), view->cursorColumnReal(), view->cursorLine(), kMin( view->cursorColumnReal()+buf.length(), textLine->length() ) );
3028
3029 insertText (view->cursorLine(), view->cursorColumnReal(), buf);
3030 m_indenter->processChar(c);
3031
3032 editEnd ();
3033
3034 if (bracketInserted)
3035 view->setCursorPositionInternal (view->cursorLine(), view->cursorColumnReal()-1);
3036
3037 emit charactersInteractivelyInserted (oldLine, oldCol, chars);
3038
3039 return true;
3040}
3041
3042void KateDocument::newLine( KateTextCursor& c, KateViewInternal *v )
3043{
3044 editStart();
3045
3046 if( !v->view()->config()->persistentSelection() && v->view()->hasSelection() )
3047 v->view()->removeSelectedText();
3048
3049 // temporary hack to get the cursor pos right !!!!!!!!!
3050 c = v->getCursor ();
3051
3052 if (c.line() > (int)lastLine())
3053 c.setLine(lastLine());
3054
3055 if ( c.line() < 0 )
3056 c.setLine( 0 );
3057
3058 uint ln = c.line();
3059
3060 KateTextLine::Ptr textLine = kateTextLine(c.line());
3061
3062 if (c.col() > (int)textLine->length())
3063 c.setCol(textLine->length());
3064
3065 if (m_indenter->canProcessNewLine ())
3066 {
3067 int pos = textLine->firstChar();
3068
3069 // length should do the job better
3070 if (pos < 0)
3071 pos = textLine->length();
3072
3073 if (c.col() < pos)
3074 c.setCol(pos); // place cursor on first char if before
3075
3076 editWrapLine (c.line(), c.col());
3077
3078 KateDocCursor cursor (c.line() + 1, pos, this);
3079 m_indenter->processNewline(cursor, true);
3080
3081 c.setPos(cursor);
3082 }
3083 else
3084 {
3085 editWrapLine (c.line(), c.col());
3086 c.setPos(c.line() + 1, 0);
3087 }
3088
3089 removeTrailingSpace( ln );
3090
3091 editEnd();
3092}
3093
3094void KateDocument::transpose( const KateTextCursor& cursor)
3095{
3096 KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
3097
3098 if (!textLine || (textLine->length() < 2))
3099 return;
3100
3101 uint col = cursor.col();
3102
3103 if (col > 0)
3104 col--;
3105
3106 if ((textLine->length() - col) < 2)
3107 return;
3108
3109 uint line = cursor.line();
3110 TQString s;
3111
3112 //clever swap code if first character on the line swap right&left
3113 //otherwise left & right
3114 s.append (textLine->getChar(col+1));
3115 s.append (textLine->getChar(col));
3116 //do the swap
3117
3118 // do it right, never ever manipulate a textline
3119 editStart ();
3120 editRemoveText (line, col, 2);
3121 editInsertText (line, col, s);
3122 editEnd ();
3123}
3124
3125void KateDocument::backspace( KateView *view, const KateTextCursor& c )
3126{
3127 if ( !view->config()->persistentSelection() && view->hasSelection() ) {
3128 view->removeSelectedText();
3129 return;
3130 }
3131
3132 uint col = kMax( c.col(), 0 );
3133 uint line = kMax( c.line(), 0 );
3134
3135 if ((col == 0) && (line == 0))
3136 return;
3137
3138 int complement = 0;
3139 if (col > 0)
3140 {
3141 if (config()->configFlags() & KateDocument::cfAutoBrackets)
3142 {
3143 // if inside empty (), {}, [], '', "" delete both
3144 KateTextLine::Ptr tl = m_buffer->plainLine(line);
3145 if(!tl) return;
3146 TQChar prevChar = tl->getChar(col-1);
3147 TQChar nextChar = tl->getChar(col);
3148
3149 if ( (prevChar == '"' && nextChar == '"') ||
3150 (prevChar == '\'' && nextChar == '\'') ||
3151 (prevChar == '(' && nextChar == ')') ||
3152 (prevChar == '[' && nextChar == ']') ||
3153 (prevChar == '{' && nextChar == '}') )
3154 {
3155 complement = 1;
3156 }
3157 }
3158 if (!(config()->configFlags() & KateDocument::cfBackspaceIndents))
3159 {
3160 // ordinary backspace
3161 //c.cursor.col--;
3162 removeText(line, col-1, line, col+complement);
3163 }
3164 else
3165 {
3166 // backspace indents: erase to next indent position
3167 KateTextLine::Ptr textLine = m_buffer->plainLine(line);
3168
3169 // don't forget this check!!!! really!!!!
3170 if (!textLine)
3171 return;
3172
3173 int colX = textLine->cursorX(col, config()->tabWidth());
3174 int pos = textLine->firstChar();
3175 if (pos > 0)
3176 pos = textLine->cursorX(pos, config()->tabWidth());
3177
3178 if (pos < 0 || pos >= (int)colX)
3179 {
3180 // only spaces on left side of cursor
3181 indent( view, line, -1);
3182 }
3183 else
3184 removeText(line, col-1, line, col+complement);
3185 }
3186 }
3187 else
3188 {
3189 // col == 0: wrap to previous line
3190 if (line >= 1)
3191 {
3192 KateTextLine::Ptr textLine = m_buffer->plainLine(line-1);
3193
3194 // don't forget this check!!!! really!!!!
3195 if (!textLine)
3196 return;
3197
3198 if (config()->wordWrap() && textLine->endingWith(TQString::fromLatin1(" ")))
3199 {
3200 // gg: in hard wordwrap mode, backspace must also eat the trailing space
3201 removeText (line-1, textLine->length()-1, line, 0);
3202 }
3203 else
3204 removeText (line-1, textLine->length(), line, 0);
3205 }
3206 }
3207
3208 emit backspacePressed();
3209}
3210
3211void KateDocument::del( KateView *view, const KateTextCursor& c )
3212{
3213 if ( !view->config()->persistentSelection() && view->hasSelection() ) {
3214 view->removeSelectedText();
3215 return;
3216 }
3217
3218 if( c.col() < (int) m_buffer->plainLine(c.line())->length())
3219 {
3220 removeText(c.line(), c.col(), c.line(), c.col()+1);
3221 }
3222 else if ( (uint)c.line() < lastLine() )
3223 {
3224 removeText(c.line(), c.col(), c.line()+1, 0);
3225 }
3226}
3227
3228void KateDocument::paste ( KateView* view )
3229{
3230 TQString s = TQApplication::clipboard()->text();
3231
3232 if (s.isEmpty())
3233 return;
3234
3235 uint lines = s.contains (TQChar ('\n'));
3236
3237 m_undoDontMerge = true;
3238
3239 editStart ();
3240
3241 if (!view->config()->persistentSelection() && view->hasSelection() )
3242 view->removeSelectedText();
3243
3244 uint line = view->cursorLine ();
3245 uint column = view->cursorColumnReal ();
3246
3247 insertText ( line, column, s, view->blockSelectionMode() );
3248
3249 editEnd();
3250
3251 // move cursor right for block select, as the user is moved right internal
3252 // even in that case, but user expects other behavior in block selection
3253 // mode !
3254 if (view->blockSelectionMode())
3255 view->setCursorPositionInternal (line+lines, column);
3256
3257 if (m_indenter->canProcessLine()
3258 && config()->configFlags() & KateDocumentConfig::cfIndentPastedText)
3259 {
3260 editStart();
3261
3262 KateDocCursor begin(line, 0, this);
3263 KateDocCursor end(line + lines, 0, this);
3264
3265 m_indenter->processSection (begin, end);
3266
3267 editEnd();
3268 }
3269
3270 if (!view->blockSelectionMode()) emit charactersSemiInteractivelyInserted (line, column, s);
3271 m_undoDontMerge = true;
3272}
3273
3274void KateDocument::insertIndentChars ( KateView *view )
3275{
3276 editStart ();
3277
3278 TQString s;
3279 if (config()->configFlags() & KateDocument::cfSpaceIndent)
3280 {
3281 int width = config()->indentationWidth();
3282 s.fill (' ', width - (view->cursorColumnReal() % width));
3283 }
3284 else
3285 s.append ('\t');
3286
3287 insertText (view->cursorLine(), view->cursorColumnReal(), s);
3288
3289 editEnd ();
3290}
3291
3292void KateDocument::indent ( KateView *v, uint line, int change)
3293{
3294 editStart ();
3295
3296 if (!hasSelection())
3297 {
3298 // single line
3299 optimizeLeadingSpace(line, config()->configFlags(), change);
3300 }
3301 else
3302 {
3303 int sl = v->selStartLine();
3304 int el = v->selEndLine();
3305 int ec = v->selEndCol();
3306
3307 if ((ec == 0) && ((el-1) >= 0))
3308 {
3309 el--; /* */
3310 }
3311
3312 if (config()->configFlags() & KateDocument::cfKeepIndentProfile && change < 0) {
3313 // unindent so that the existing indent profile doesn't get screwed
3314 // if any line we may unindent is already full left, don't do anything
3315 int adjustedChange = -change;
3316
3317 for (line = sl; (int) line <= el && adjustedChange > 0; line++) {
3318 KateTextLine::Ptr textLine = m_buffer->plainLine(line);
3319 int firstChar = textLine->firstChar();
3320 if (firstChar >= 0 && (v->lineSelected(line) || v->lineHasSelected(line))) {
3321 int maxUnindent = textLine->cursorX(firstChar, config()->tabWidth()) / config()->indentationWidth();
3322 if (maxUnindent < adjustedChange)
3323 adjustedChange = maxUnindent;
3324 }
3325 }
3326
3327 change = -adjustedChange;
3328 }
3329
3330 const bool rts = config()->configFlags() & KateDocumentConfig::cfRemoveTrailingDyn;
3331 for (line = sl; (int) line <= el; line++) {
3332 if ((v->lineSelected(line) || v->lineHasSelected(line))
3333 && (!rts || lineLength(line) > 0)) {
3334 optimizeLeadingSpace(line, config()->configFlags(), change);
3335 }
3336 }
3337 }
3338
3339 editEnd ();
3340}
3341
3342void KateDocument::align(KateView *view, uint line)
3343{
3344 if (m_indenter->canProcessLine())
3345 {
3346 editStart ();
3347
3348 if (!view->hasSelection())
3349 {
3350 KateDocCursor curLine(line, 0, this);
3351 m_indenter->processLine (curLine);
3352 editEnd ();
3353 activeView()->setCursorPosition (line, curLine.col());
3354 }
3355 else
3356 {
3357 m_indenter->processSection (view->selStart(), view->selEnd());
3358 editEnd ();
3359 }
3360 }
3361}
3362
3363/*
3364 Optimize the leading whitespace for a single line.
3365 If change is > 0, it adds indentation units (indentationChars)
3366 if change is == 0, it only optimizes
3367 If change is < 0, it removes indentation units
3368 This will be used to indent, unindent, and optimal-fill a line.
3369 If excess space is removed depends on the flag cfKeepExtraSpaces
3370 which has to be set by the user
3371*/
3372void KateDocument::optimizeLeadingSpace(uint line, int flags, int change)
3373{
3374 KateTextLine::Ptr textline = m_buffer->plainLine(line);
3375
3376 int first_char = textline->firstChar();
3377
3378 int w = 0;
3379 if (flags & KateDocument::cfSpaceIndent)
3380 w = config()->indentationWidth();
3381 else
3382 w = config()->tabWidth();
3383
3384 if (first_char < 0)
3385 first_char = textline->length();
3386
3387 int space = textline->cursorX(first_char, config()->tabWidth()) + change * w;
3388 if (space < 0)
3389 space = 0;
3390
3391 if (!(flags & KateDocument::cfKeepExtraSpaces))
3392 {
3393 uint extra = space % w;
3394
3395 space -= extra;
3396 if (extra && change < 0) {
3397 // otherwise it unindents too much (e.g. 12 chars when indentation is 8 chars wide)
3398 space += w;
3399 }
3400 }
3401
3402 //kdDebug(13020) << "replace With Op: " << line << " " << first_char << " " << space << endl;
3403 replaceWithOptimizedSpace(line, first_char, space, flags);
3404}
3405
3406void KateDocument::replaceWithOptimizedSpace(uint line, uint upto_column, uint space, int flags)
3407{
3408 uint length;
3409 TQString new_space;
3410
3411 if (flags & KateDocument::cfSpaceIndent && ! (flags & KateDocumentConfig::cfMixedIndent) ) {
3412 length = space;
3413 new_space.fill(' ', length);
3414 }
3415 else {
3416 length = space / config()->tabWidth();
3417 new_space.fill('\t', length);
3418
3419 TQString extra_space;
3420 extra_space.fill(' ', space % config()->tabWidth());
3421 length += space % config()->tabWidth();
3422 new_space += extra_space;
3423 }
3424
3425 KateTextLine::Ptr textline = m_buffer->plainLine(line);
3426 uint change_from;
3427 for (change_from = 0; change_from < upto_column && change_from < length; change_from++) {
3428 if (textline->getChar(change_from) != new_space[change_from])
3429 break;
3430 }
3431
3432 editStart();
3433
3434 if (change_from < upto_column)
3435 removeText(line, change_from, line, upto_column);
3436
3437 if (change_from < length)
3438 insertText(line, change_from, new_space.right(length - change_from));
3439
3440 editEnd();
3441}
3442
3443/*
3444 Remove a given string at the begining
3445 of the current line.
3446*/
3447bool KateDocument::removeStringFromBegining(int line, TQString &str)
3448{
3449 KateTextLine::Ptr textline = m_buffer->plainLine(line);
3450
3451 int index = 0;
3452 bool there = false;
3453
3454 if (textline->startingWith(str))
3455 there = true;
3456 else
3457 {
3458 index = textline->firstChar ();
3459
3460 if ((index >= 0) && (textline->length() >= (index + str.length())) && (textline->string(index, str.length()) == str))
3461 there = true;
3462 }
3463
3464 if (there)
3465 {
3466 // Remove some chars
3467 removeText (line, index, line, index+str.length());
3468 }
3469
3470 return there;
3471}
3472
3473/*
3474 Remove a given string at the end
3475 of the current line.
3476*/
3477bool KateDocument::removeStringFromEnd(int line, TQString &str)
3478{
3479 KateTextLine::Ptr textline = m_buffer->plainLine(line);
3480
3481 int index = 0;
3482 bool there = false;
3483
3484 if(textline->endingWith(str))
3485 {
3486 index = textline->length() - str.length();
3487 there = true;
3488 }
3489 else
3490 {
3491 index = textline->lastChar ()-str.length()+1;
3492
3493 if ((index >= 0) && (textline->length() >= (index + str.length())) && (textline->string(index, str.length()) == str))
3494 there = true;
3495 }
3496
3497 if (there)
3498 {
3499 // Remove some chars
3500 removeText (line, index, line, index+str.length());
3501 }
3502
3503 return there;
3504}
3505
3506/*
3507 Add to the current line a comment line mark at
3508 the begining.
3509*/
3510void KateDocument::addStartLineCommentToSingleLine( int line, int attrib )
3511{
3512 if (highlight()->getCommentSingleLinePosition(attrib)==KateHighlighting::CSLPosColumn0)
3513 {
3514 TQString commentLineMark = highlight()->getCommentSingleLineStart( attrib ) + " ";
3515 insertText (line, 0, commentLineMark);
3516 }
3517 else
3518 {
3519 TQString commentLineMark=highlight()->getCommentSingleLineStart(attrib);
3520 KateTextLine::Ptr l = m_buffer->line(line);
3521 int pos=l->firstChar();
3522 if (pos >=0)
3523 insertText(line,pos,commentLineMark);
3524 }
3525}
3526
3527/*
3528 Remove from the current line a comment line mark at
3529 the begining if there is one.
3530*/
3531bool KateDocument::removeStartLineCommentFromSingleLine( int line, int attrib )
3532{
3533 TQString shortCommentMark = highlight()->getCommentSingleLineStart( attrib );
3534 TQString longCommentMark = shortCommentMark + " ";
3535
3536 editStart();
3537
3538 // Try to remove the long comment mark first
3539 bool removed = (removeStringFromBegining(line, longCommentMark)
3540 || removeStringFromBegining(line, shortCommentMark));
3541
3542 editEnd();
3543
3544 return removed;
3545}
3546
3547/*
3548 Add to the current line a start comment mark at the
3549 begining and a stop comment mark at the end.
3550*/
3551void KateDocument::addStartStopCommentToSingleLine( int line, int attrib )
3552{
3553 TQString startCommentMark = highlight()->getCommentStart( attrib ) + " ";
3554 TQString stopCommentMark = " " + highlight()->getCommentEnd( attrib );
3555
3556 editStart();
3557
3558 // Add the start comment mark
3559 insertText (line, 0, startCommentMark);
3560
3561 // Go to the end of the line
3562 int col = m_buffer->plainLine(line)->length();
3563
3564 // Add the stop comment mark
3565 insertText (line, col, stopCommentMark);
3566
3567 editEnd();
3568}
3569
3570/*
3571 Remove from the current line a start comment mark at
3572 the begining and a stop comment mark at the end.
3573*/
3574bool KateDocument::removeStartStopCommentFromSingleLine( int line, int attrib )
3575{
3576 TQString shortStartCommentMark = highlight()->getCommentStart( attrib );
3577 TQString longStartCommentMark = shortStartCommentMark + " ";
3578 TQString shortStopCommentMark = highlight()->getCommentEnd( attrib );
3579 TQString longStopCommentMark = " " + shortStopCommentMark;
3580
3581 editStart();
3582
3583#ifdef __GNUC__
3584#warning "that's a bad idea, can lead to stray endings, FIXME"
3585#endif
3586 // Try to remove the long start comment mark first
3587 bool removedStart = (removeStringFromBegining(line, longStartCommentMark)
3588 || removeStringFromBegining(line, shortStartCommentMark));
3589
3590 bool removedStop = false;
3591 if (removedStart)
3592 {
3593 // Try to remove the long stop comment mark first
3594 removedStop = (removeStringFromEnd(line, longStopCommentMark)
3595 || removeStringFromEnd(line, shortStopCommentMark));
3596 }
3597
3598 editEnd();
3599
3600 return (removedStart || removedStop);
3601}
3602
3603/*
3604 Add to the current selection a start comment
3605 mark at the begining and a stop comment mark
3606 at the end.
3607*/
3608void KateDocument::addStartStopCommentToSelection( KateView *view, int attrib )
3609{
3610 TQString startComment = highlight()->getCommentStart( attrib );
3611 TQString endComment = highlight()->getCommentEnd( attrib );
3612
3613 int sl = view->selStartLine();
3614 int el = view->selEndLine();
3615 int sc = view->selStartCol();
3616 int ec = view->selEndCol();
3617
3618 if ((ec == 0) && ((el-1) >= 0))
3619 {
3620 el--;
3621 ec = m_buffer->plainLine (el)->length();
3622 }
3623
3624 editStart();
3625
3626 insertText (el, ec, endComment);
3627 insertText (sl, sc, startComment);
3628
3629 editEnd ();
3630
3631 // Set the new selection
3632 ec += endComment.length() + ( (el == sl) ? startComment.length() : 0 );
3633 view->setSelection(sl, sc, el, ec);
3634}
3635
3636/*
3637 Add to the current selection a comment line
3638 mark at the begining of each line.
3639*/
3640void KateDocument::addStartLineCommentToSelection( KateView *view, int attrib )
3641{
3642 TQString commentLineMark = highlight()->getCommentSingleLineStart( attrib ) + " ";
3643
3644 int sl = view->selStartLine();
3645 int el = view->selEndLine();
3646
3647 if ((view->selEndCol() == 0) && ((el-1) >= 0))
3648 {
3649 el--;
3650 }
3651
3652 editStart();
3653
3654 // For each line of the selection
3655 for (int z = el; z >= sl; z--) {
3656 //insertText (z, 0, commentLineMark);
3657 addStartLineCommentToSingleLine(z, attrib );
3658 }
3659
3660 editEnd ();
3661
3662 // Set the new selection
3663
3664 KateDocCursor end (view->selEnd());
3665 end.setCol(view->selEndCol() + ((el == view->selEndLine()) ? commentLineMark.length() : 0) );
3666
3667 view->setSelection(view->selStartLine(), 0, end.line(), end.col());
3668}
3669
3670bool KateDocument::nextNonSpaceCharPos(int &line, int &col)
3671{
3672 for(; line < (int)m_buffer->count(); line++) {
3673 KateTextLine::Ptr textLine = m_buffer->plainLine(line);
3674
3675 if (!textLine)
3676 break;
3677
3678 col = textLine->nextNonSpaceChar(col);
3679 if(col != -1)
3680 return true; // Next non-space char found
3681 col = 0;
3682 }
3683 // No non-space char found
3684 line = -1;
3685 col = -1;
3686 return false;
3687}
3688
3689bool KateDocument::previousNonSpaceCharPos(int &line, int &col)
3690{
3691 while(true)
3692 {
3693 KateTextLine::Ptr textLine = m_buffer->plainLine(line);
3694
3695 if (!textLine)
3696 break;
3697
3698 col = textLine->previousNonSpaceChar(col);
3699 if(col != -1) return true;
3700 if(line == 0) return false;
3701 --line;
3702 col = textLine->length();
3703}
3704 // No non-space char found
3705 line = -1;
3706 col = -1;
3707 return false;
3708}
3709
3710/*
3711 Remove from the selection a start comment mark at
3712 the begining and a stop comment mark at the end.
3713*/
3714bool KateDocument::removeStartStopCommentFromSelection( KateView *view, int attrib )
3715{
3716 TQString startComment = highlight()->getCommentStart( attrib );
3717 TQString endComment = highlight()->getCommentEnd( attrib );
3718
3719 int sl = kMax<int> (0, view->selStartLine());
3720 int el = kMin<int> (view->selEndLine(), lastLine());
3721 int sc = view->selStartCol();
3722 int ec = view->selEndCol();
3723
3724 // The selection ends on the char before selectEnd
3725 if (ec != 0) {
3726 ec--;
3727 } else {
3728 if (el > 0) {
3729 el--;
3730 ec = m_buffer->plainLine(el)->length() - 1;
3731 }
3732 }
3733
3734 int startCommentLen = startComment.length();
3735 int endCommentLen = endComment.length();
3736
3737 // had this been perl or sed: s/^\s*$startComment(.+?)$endComment\s*/$1/
3738
3739 bool remove = nextNonSpaceCharPos(sl, sc)
3740 && m_buffer->plainLine(sl)->stringAtPos(sc, startComment)
3741 && previousNonSpaceCharPos(el, ec)
3742 && ( (ec - endCommentLen + 1) >= 0 )
3743 && m_buffer->plainLine(el)->stringAtPos(ec - endCommentLen + 1, endComment);
3744
3745 if (remove) {
3746 editStart();
3747
3748 removeText (el, ec - endCommentLen + 1, el, ec + 1);
3749 removeText (sl, sc, sl, sc + startCommentLen);
3750
3751 editEnd ();
3752 // set new selection not necessary, as the selection cursors are KateSuperCursors
3753 }
3754
3755 return remove;
3756}
3757
3758bool KateDocument::removeStartStopCommentFromRegion(const KateTextCursor &start,const KateTextCursor &end,int attrib)
3759{
3760 TQString startComment = highlight()->getCommentStart( attrib );
3761 TQString endComment = highlight()->getCommentEnd( attrib );
3762 int startCommentLen = startComment.length();
3763 int endCommentLen = endComment.length();
3764
3765 bool remove = m_buffer->plainLine(start.line())->stringAtPos(start.col(), startComment)
3766 && ( (end.col() - endCommentLen ) >= 0 )
3767 && m_buffer->plainLine(end.line())->stringAtPos(end.col() - endCommentLen , endComment);
3768 if (remove) {
3769 editStart();
3770 removeText(end.line(),end.col()-endCommentLen,end.line(),end.col());
3771 removeText(start.line(),start.col(),start.line(),start.col()+startCommentLen);
3772 editEnd();
3773 }
3774 return remove;
3775}
3776
3777/*
3778 Remove from the begining of each line of the
3779 selection a start comment line mark.
3780*/
3781bool KateDocument::removeStartLineCommentFromSelection( KateView *view, int attrib )
3782{
3783 TQString shortCommentMark = highlight()->getCommentSingleLineStart( attrib );
3784 TQString longCommentMark = shortCommentMark + " ";
3785
3786 int sl = view->selStartLine();
3787 int el = view->selEndLine();
3788
3789 if ((view->selEndCol() == 0) && ((el-1) >= 0))
3790 {
3791 el--;
3792 }
3793
3794 // Find out how many char will be removed from the last line
3795 int removeLength = 0;
3796 if (m_buffer->plainLine(el)->startingWith(longCommentMark))
3797 removeLength = longCommentMark.length();
3798 else if (m_buffer->plainLine(el)->startingWith(shortCommentMark))
3799 removeLength = shortCommentMark.length();
3800
3801 bool removed = false;
3802
3803 editStart();
3804
3805 // For each line of the selection
3806 for (int z = el; z >= sl; z--)
3807 {
3808 // Try to remove the long comment mark first
3809 removed = (removeStringFromBegining(z, longCommentMark)
3810 || removeStringFromBegining(z, shortCommentMark)
3811 || removed);
3812 }
3813
3814 editEnd();
3815 // updating selection already done by the KateSuperCursors
3816 return removed;
3817}
3818
3819/*
3820 Comment or uncomment the selection or the current
3821 line if there is no selection.
3822*/
3823void KateDocument::comment( KateView *v, uint line,uint column, int change)
3824{
3825 // We need to check that we can sanely comment the selectino or region.
3826 // It is if the attribute of the first and last character of the range to
3827 // comment belongs to the same language definition.
3828 // for lines with no text, we need the attribute for the lines context.
3829 bool hassel = v->hasSelection();
3830 int startAttrib, endAttrib;
3831 if ( hassel )
3832 {
3833 KateTextLine::Ptr ln = kateTextLine( v->selStartLine() );
3834 int l = v->selStartLine(), c = v->selStartCol();
3835 startAttrib = nextNonSpaceCharPos( l, c ) ? kateTextLine( l )->attribute( c ) : 0;
3836
3837 ln = kateTextLine( v->selEndLine() );
3838 l = v->selEndLine(), c = v->selEndCol();
3839 endAttrib = previousNonSpaceCharPos( l, c ) ? kateTextLine( l )->attribute( c ) : 0;
3840 }
3841 else
3842 {
3843 KateTextLine::Ptr ln = kateTextLine( line );
3844 if ( ln->length() )
3845 {
3846 startAttrib = ln->attribute( ln->firstChar() );
3847 endAttrib = ln->attribute( ln->lastChar() );
3848 }
3849 else
3850 {
3851 int l = line, c = 0;
3852 if ( nextNonSpaceCharPos( l, c ) || previousNonSpaceCharPos( l, c ) )
3853 startAttrib = endAttrib = kateTextLine( l )->attribute( c );
3854 else
3855 startAttrib = endAttrib = 0;
3856 }
3857 }
3858
3859 if ( ! highlight()->canComment( startAttrib, endAttrib ) )
3860 {
3861 kdDebug(13020)<<"canComment( "<<startAttrib<<", "<<endAttrib<<" ) returned false!"<<endl;
3862 return;
3863 }
3864
3865 bool hasStartLineCommentMark = !(highlight()->getCommentSingleLineStart( startAttrib ).isEmpty());
3866 bool hasStartStopCommentMark = ( !(highlight()->getCommentStart( startAttrib ).isEmpty())
3867 && !(highlight()->getCommentEnd( endAttrib ).isEmpty()) );
3868
3869 bool removed = false;
3870
3871 if (change > 0) // comment
3872 {
3873 if ( !hassel )
3874 {
3875 if ( hasStartLineCommentMark )
3876 addStartLineCommentToSingleLine( line, startAttrib );
3877 else if ( hasStartStopCommentMark )
3878 addStartStopCommentToSingleLine( line, startAttrib );
3879 }
3880 else
3881 {
3882 // anders: prefer single line comment to avoid nesting probs
3883 // If the selection starts after first char in the first line
3884 // or ends before the last char of the last line, we may use
3885 // multiline comment markers.
3886 // TODO We should try to detect nesting.
3887 // - if selection ends at col 0, most likely she wanted that
3888 // line ignored
3889 if ( hasStartStopCommentMark &&
3890 ( !hasStartLineCommentMark || (
3891 ( v->selStartCol() > m_buffer->plainLine( v->selStartLine() )->firstChar() ) ||
3892 ( v->selEndCol() < ((int)m_buffer->plainLine( v->selEndLine() )->length()) )
3893 ) ) )
3894 addStartStopCommentToSelection( v, startAttrib );
3895 else if ( hasStartLineCommentMark )
3896 addStartLineCommentToSelection( v, startAttrib );
3897 }
3898 }
3899 else // uncomment
3900 {
3901 if ( !hassel )
3902 {
3903 removed = ( hasStartLineCommentMark
3904 && removeStartLineCommentFromSingleLine( line, startAttrib ) )
3905 || ( hasStartStopCommentMark
3906 && removeStartStopCommentFromSingleLine( line, startAttrib ) );
3907 if ((!removed) && foldingTree()) {
3908 kdDebug(13020)<<"easy approach for uncommenting did not work, trying harder (folding tree)"<<endl;
3909 int commentRegion=(highlight()->commentRegion(startAttrib));
3910 if (commentRegion){
3911 KateCodeFoldingNode *n=foldingTree()->findNodeForPosition(line,column);
3912 if (n) {
3913 KateTextCursor start,end;
3914 if ((n->nodeType()==commentRegion) && n->getBegin(foldingTree(), &start) && n->getEnd(foldingTree(), &end)) {
3915 kdDebug(13020)<<"Enclosing region found:"<<start.col()<<"/"<<start.line()<<"-"<<end.col()<<"/"<<end.line()<<endl;
3916 removeStartStopCommentFromRegion(start,end,startAttrib);
3917 } else {
3918 kdDebug(13020)<<"Enclosing region found, but not valid"<<endl;
3919 kdDebug(13020)<<"Region found: "<<n->nodeType()<<" region needed: "<<commentRegion<<endl;
3920 }
3921 //perhaps nested regions should be hadled here too...
3922 } else kdDebug(13020)<<"No enclosing region found"<<endl;
3923 } else kdDebug(13020)<<"No comment region specified for current hl"<<endl;
3924 }
3925 }
3926 else
3927 {
3928 // anders: this seems like it will work with above changes :)
3929 removed = ( hasStartLineCommentMark
3930 && removeStartLineCommentFromSelection( v, startAttrib ) )
3931 || ( hasStartStopCommentMark
3932 && removeStartStopCommentFromSelection( v, startAttrib ) );
3933 }
3934 }
3935}
3936
3937void KateDocument::transform( KateView *v, const KateTextCursor &c,
3938 KateDocument::TextTransform t )
3939{
3940 editStart();
3941 uint cl( c.line() ), cc( c.col() );
3942 bool selectionRestored = false;
3943
3944 if ( hasSelection() )
3945 {
3946 // cache the selection and cursor, so we can be sure to restore.
3947 KateTextCursor selstart = v->selStart();
3948 KateTextCursor selend = v->selEnd();
3949
3950 int ln = v->selStartLine();
3951 while ( ln <= selend.line() )
3952 {
3953 uint start, end;
3954 start = (ln == selstart.line() || v->blockSelectionMode()) ?
3955 selstart.col() : 0;
3956 end = (ln == selend.line() || v->blockSelectionMode()) ?
3957 selend.col() : lineLength( ln );
3958 if ( start > end )
3959 {
3960 uint t = start;
3961 start = end;
3962 end = t;
3963 }
3964 TQString s = text( ln, start, ln, end );
3965 TQString o = s;
3966
3967 if ( t == Uppercase )
3968 s = s.upper();
3969 else if ( t == Lowercase )
3970 s = s.lower();
3971 else // Capitalize
3972 {
3973 KateTextLine::Ptr l = m_buffer->plainLine( ln );
3974 uint p ( 0 );
3975 while( p < s.length() )
3976 {
3977 // If bol or the character before is not in a word, up this one:
3978 // 1. if both start and p is 0, upper char.
3979 // 2. if blockselect or first line, and p == 0 and start-1 is not in a word, upper
3980 // 3. if p-1 is not in a word, upper.
3981 if ( ( ! start && ! p ) ||
3982 ( ( ln == selstart.line() || v->blockSelectionMode() ) &&
3983 ! p && ! highlight()->isInWord( l->getChar( start - 1 )) ) ||
3984 ( p && ! highlight()->isInWord( s.at( p-1 ) ) )
3985 )
3986 s[p] = s.at(p).upper();
3987 p++;
3988 }
3989 }
3990
3991 if ( o != s )
3992 {
3993 removeText( ln, start, ln, end );
3994 insertText( ln, start, s );
3995 }
3996
3997 ln++;
3998 }
3999
4000 // restore selection
4001 v->setSelection( selstart, selend );
4002 selectionRestored = true;
4003
4004 } else { // no selection
4005 TQString o = text( cl, cc, cl, cc + 1 );
4006 TQString s;
4007 int n ( cc );
4008 switch ( t ) {
4009 case Uppercase:
4010 s = o.upper();
4011 break;
4012 case Lowercase:
4013 s = o.lower();
4014 break;
4015 case Capitalize:
4016 {
4017 KateTextLine::Ptr l = m_buffer->plainLine( cl );
4018 while ( n > 0 && highlight()->isInWord( l->getChar( n-1 ), l->attribute( n-1 ) ) )
4019 n--;
4020 o = text( cl, n, cl, n + 1 );
4021 s = o.upper();
4022 }
4023 break;
4024 default:
4025 break;
4026 }
4027
4028 if ( s != o )
4029 {
4030 removeText( cl, n, cl, n+1 );
4031 insertText( cl, n, s );
4032 }
4033 }
4034 editEnd();
4035
4036 if ( ! selectionRestored )
4037 v->setCursorPosition( cl, cc );
4038}
4039
4040void KateDocument::joinLines( uint first, uint last )
4041{
4042// if ( first == last ) last += 1;
4043 editStart();
4044 int line( first );
4045 while ( first < last )
4046 {
4047 // Normalize the whitespace in the joined lines by making sure there's
4048 // always exactly one space between the joined lines
4049 // This cannot be done in editUnwrapLine, because we do NOT want this
4050 // behaviour when deleting from the start of a line, just when explicitly
4051 // calling the join command
4052 KateTextLine::Ptr l = m_buffer->line( line );
4053 KateTextLine::Ptr tl = m_buffer->line( line + 1 );
4054
4055 if ( !l || !tl )
4056 {
4057 editEnd();
4058 return;
4059 }
4060
4061 int pos = tl->firstChar();
4062 if ( pos >= 0 )
4063 {
4064 if (pos != 0)
4065 editRemoveText( line + 1, 0, pos );
4066 if ( !( l->length() == 0 || l->getChar( l->length() - 1 ).isSpace() ) )
4067 editInsertText( line + 1, 0, " " );
4068 }
4069 else
4070 {
4071 // Just remove the whitespace and let Kate handle the rest
4072 editRemoveText( line + 1, 0, tl->length() );
4073 }
4074
4075 editUnWrapLine( line );
4076 first++;
4077 }
4078 editEnd();
4079}
4080
4081TQString KateDocument::getWord( const KateTextCursor& cursor ) {
4082 int start, end, len;
4083
4084 KateTextLine::Ptr textLine = m_buffer->plainLine(cursor.line());
4085 len = textLine->length();
4086 start = end = cursor.col();
4087 if (start > len) // Probably because of non-wrapping cursor mode.
4088 return TQString("");
4089
4090 while (start > 0 && highlight()->isInWord(textLine->getChar(start - 1), textLine->attribute(start - 1))) start--;
4091 while (end < len && highlight()->isInWord(textLine->getChar(end), textLine->attribute(end))) end++;
4092 len = end - start;
4093 return TQString(&textLine->text()[start], len);
4094}
4095
4096void KateDocument::tagLines(int start, int end)
4097{
4098 for (uint z = 0; z < m_views.count(); z++)
4099 m_views.at(z)->tagLines (start, end, true);
4100}
4101
4102void KateDocument::tagLines(KateTextCursor start, KateTextCursor end)
4103{
4104 // May need to switch start/end cols if in block selection mode
4105 if (blockSelectionMode() && start.col() > end.col()) {
4106 int sc = start.col();
4107 start.setCol(end.col());
4108 end.setCol(sc);
4109 }
4110
4111 for (uint z = 0; z < m_views.count(); z++)
4112 m_views.at(z)->tagLines(start, end, true);
4113}
4114
4115void KateDocument::repaintViews(bool paintOnlyDirty)
4116{
4117 for (uint z = 0; z < m_views.count(); z++)
4118 m_views.at(z)->repaintText(paintOnlyDirty);
4119}
4120
4121void KateDocument::tagAll()
4122{
4123 for (uint z = 0; z < m_views.count(); z++)
4124 {
4125 m_views.at(z)->tagAll();
4126 m_views.at(z)->updateView (true);
4127 }
4128}
4129
4130uint KateDocument::configFlags ()
4131{
4132 return config()->configFlags();
4133}
4134
4135void KateDocument::setConfigFlags (uint flags)
4136{
4137 config()->setConfigFlags(flags);
4138}
4139
4140inline bool isStartBracket( const TQChar& c ) { return c == '{' || c == '[' || c == '('; }
4141inline bool isEndBracket ( const TQChar& c ) { return c == '}' || c == ']' || c == ')'; }
4142inline bool isBracket ( const TQChar& c ) { return isStartBracket( c ) || isEndBracket( c ); }
4143
4144/*
4145 Bracket matching uses the following algorithm:
4146 If in overwrite mode, match the bracket currently underneath the cursor.
4147 Otherwise, if the character to the right of the cursor is an starting bracket,
4148 match it. Otherwise if the character to the left of the cursor is a
4149 ending bracket, match it. Otherwise, if the the character to the left
4150 of the cursor is an starting bracket, match it. Otherwise, if the character
4151 to the right of the cursor is an ending bracket, match it. Otherwise, don't
4152 match anything.
4153*/
4154void KateDocument::newBracketMark( const KateTextCursor& cursor, KateBracketRange& bm, int maxLines )
4155{
4156 bm.setValid(false);
4157
4158 bm.start() = cursor;
4159
4160 if( !findMatchingBracket( bm.start(), bm.end(), maxLines ) )
4161 return;
4162
4163 bm.setValid(true);
4164
4165 const int tw = config()->tabWidth();
4166 const int indentStart = m_buffer->plainLine(bm.start().line())->indentDepth(tw);
4167 const int indentEnd = m_buffer->plainLine(bm.end().line())->indentDepth(tw);
4168 bm.setIndentMin(kMin(indentStart, indentEnd));
4169}
4170
4171bool KateDocument::findMatchingBracket( KateTextCursor& start, KateTextCursor& end, int maxLines )
4172{
4173 KateTextLine::Ptr textLine = m_buffer->plainLine( start.line() );
4174 if( !textLine )
4175 return false;
4176
4177 TQChar right = textLine->getChar( start.col() );
4178 TQChar left = textLine->getChar( start.col() - 1 );
4179 TQChar bracket;
4180
4181 if ( config()->configFlags() & cfOvr ) {
4182 if( isBracket( right ) ) {
4183 bracket = right;
4184 } else {
4185 return false;
4186 }
4187 } else if ( isStartBracket( right ) ) {
4188 bracket = right;
4189 } else if ( isEndBracket( left ) ) {
4190 start.setCol(start.col() - 1);
4191 bracket = left;
4192 } else if ( isBracket( left ) ) {
4193 start.setCol(start.col() - 1);
4194 bracket = left;
4195 } else if ( isBracket( right ) ) {
4196 bracket = right;
4197 } else {
4198 return false;
4199 }
4200
4201 TQChar opposite;
4202
4203 switch( bracket ) {
4204 case '{': opposite = '}'; break;
4205 case '}': opposite = '{'; break;
4206 case '[': opposite = ']'; break;
4207 case ']': opposite = '['; break;
4208 case '(': opposite = ')'; break;
4209 case ')': opposite = '('; break;
4210 default: return false;
4211 }
4212
4213 bool forward = isStartBracket( bracket );
4214 int startAttr = textLine->attribute( start.col() );
4215 uint count = 0;
4216 int lines = 0;
4217 end = start;
4218
4219 while( true ) {
4220 /* Increment or decrement, check base cases */
4221 if( forward ) {
4222 end.setCol(end.col() + 1);
4223 if( end.col() >= lineLength( end.line() ) ) {
4224 if( end.line() >= (int)lastLine() )
4225 return false;
4226 end.setPos(end.line() + 1, 0);
4227 textLine = m_buffer->plainLine( end.line() );
4228 lines++;
4229 }
4230 } else {
4231 end.setCol(end.col() - 1);
4232 if( end.col() < 0 ) {
4233 if( end.line() <= 0 )
4234 return false;
4235 end.setLine(end.line() - 1);
4236 end.setCol(lineLength( end.line() ) - 1);
4237 textLine = m_buffer->plainLine( end.line() );
4238 lines++;
4239 }
4240 }
4241
4242 if ((maxLines != -1) && (lines > maxLines))
4243 return false;
4244
4245 /* Easy way to skip comments */
4246 if( textLine->attribute( end.col() ) != startAttr )
4247 continue;
4248
4249 /* Check for match */
4250 TQChar c = textLine->getChar( end.col() );
4251 if( c == bracket ) {
4252 count++;
4253 } else if( c == opposite ) {
4254 if( count == 0 )
4255 return true;
4256 count--;
4257 }
4258
4259 }
4260}
4261
4262void KateDocument::guiActivateEvent( KParts::GUIActivateEvent *ev )
4263{
4264 KParts::ReadWritePart::guiActivateEvent( ev );
4265 if ( ev->activated() )
4266 emit selectionChanged();
4267}
4268
4269void KateDocument::setDocName (TQString name )
4270{
4271 if ( name == m_docName )
4272 return;
4273
4274 if ( !name.isEmpty() )
4275 {
4276 // TODO check for similarly named documents
4277 m_docName = name;
4278 updateFileType (KateFactory::self()->fileTypeManager()->fileType (this));
4279 emit nameChanged((Kate::Document *) this);
4280 return;
4281 }
4282
4283 // if the name is set, and starts with FILENAME, it should not be changed!
4284 if ( ! url().isEmpty() && m_docName.startsWith( url().filename() ) ) return;
4285
4286 int count = -1;
4287
4288 for (uint z=0; z < KateFactory::self()->documents()->count(); z++)
4289 {
4290 if ( (KateFactory::self()->documents()->at(z) != this) && (KateFactory::self()->documents()->at(z)->url().filename() == url().filename()) )
4291 if ( KateFactory::self()->documents()->at(z)->m_docNameNumber > count )
4292 count = KateFactory::self()->documents()->at(z)->m_docNameNumber;
4293 }
4294
4295 m_docNameNumber = count + 1;
4296
4297 m_docName = url().filename();
4298
4299 if (m_docName.isEmpty())
4300 m_docName = i18n ("Untitled");
4301
4302 if (m_docNameNumber > 0)
4303 m_docName = TQString(m_docName + " (%1)").arg(m_docNameNumber+1);
4304
4305 updateFileType (KateFactory::self()->fileTypeManager()->fileType (this));
4306 emit nameChanged ((Kate::Document *) this);
4307}
4308
4309void KateDocument::slotModifiedOnDisk( Kate::View * /*v*/ )
4310{
4311 if ( m_isasking < 0 )
4312 {
4313 m_isasking = 0;
4314 return;
4315 }
4316
4317 if ( !s_fileChangedDialogsActivated || m_isasking )
4318 return;
4319
4320 if (m_modOnHd && !url().isEmpty())
4321 {
4322 m_isasking = 1;
4323
4324 KateModOnHdPrompt p( this, m_modOnHdReason, reasonedMOHString(), widget() );
4325 switch ( p.exec() )
4326 {
4327 case KateModOnHdPrompt::Save:
4328 {
4329 m_modOnHd = false;
4330 KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveURLAndEncoding(config()->encoding(),
4331 url().url(),TQString::null,widget(),i18n("Save File"));
4332
4333 kdDebug(13020)<<"got "<<res.URLs.count()<<" URLs"<<endl;
4334 if( ! res.URLs.isEmpty() && ! res.URLs.first().isEmpty() && checkOverwrite( res.URLs.first() ) )
4335 {
4336 setEncoding( res.encoding );
4337
4338 if( ! saveAs( res.URLs.first() ) )
4339 {
4340 KMessageBox::error( widget(), i18n("Save failed") );
4341 m_modOnHd = true;
4342 }
4343 else
4344 emit modifiedOnDisc( this, false, 0 );
4345 }
4346 else // the save as dialog was cancelled, we are still modified on disk
4347 {
4348 m_modOnHd = true;
4349 }
4350
4351 m_isasking = 0;
4352 break;
4353 }
4354
4355 case KateModOnHdPrompt::Reload:
4356 m_modOnHd = false;
4357 emit modifiedOnDisc( this, false, 0 );
4358 reloadFile();
4359 m_isasking = 0;
4360 break;
4361
4362 case KateModOnHdPrompt::Ignore:
4363 m_modOnHd = false;
4364 emit modifiedOnDisc( this, false, 0 );
4365 m_isasking = 0;
4366 break;
4367
4368 case KateModOnHdPrompt::Overwrite:
4369 m_modOnHd = false;
4370 emit modifiedOnDisc( this, false, 0 );
4371 m_isasking = 0;
4372 save();
4373 break;
4374
4375 default: // cancel: ignore next focus event
4376 m_isasking = -1;
4377 }
4378 }
4379}
4380
4381void KateDocument::setModifiedOnDisk( int reason )
4382{
4383 m_modOnHdReason = reason;
4384 m_modOnHd = (reason > 0);
4385 emit modifiedOnDisc( this, (reason > 0), reason );
4386}
4387
4388class KateDocumentTmpMark
4389{
4390 public:
4391 TQString line;
4392 KTextEditor::Mark mark;
4393};
4394
4395void KateDocument::reloadFile()
4396{
4397 if ( !url().isEmpty() )
4398 {
4399 if (m_modOnHd && s_fileChangedDialogsActivated)
4400 {
4401 int i = KMessageBox::warningYesNoCancel
4402 (0, reasonedMOHString() + "\n\n" + i18n("What do you want to do?"),
4403 i18n("File Was Changed on Disk"), i18n("&Reload File"), i18n("&Ignore Changes"));
4404
4405 if ( i != KMessageBox::Yes)
4406 {
4407 if (i == KMessageBox::No)
4408 {
4409 m_modOnHd = false;
4410 m_modOnHdReason = 0;
4411 emit modifiedOnDisc (this, m_modOnHd, 0);
4412 }
4413
4414 return;
4415 }
4416 }
4417
4418 TQValueList<KateDocumentTmpMark> tmp;
4419
4420 for( TQIntDictIterator<KTextEditor::Mark> it( m_marks ); it.current(); ++it )
4421 {
4422 KateDocumentTmpMark m;
4423
4424 m.line = textLine (it.current()->line);
4425 m.mark = *it.current();
4426
4427 tmp.append (m);
4428 }
4429
4430 uint mode = hlMode ();
4431 bool byUser = hlSetByUser;
4432
4433 m_storedVariables.clear();
4434
4435 m_reloading = true;
4436
4437 TQValueList<int> lines, cols;
4438 for ( uint i=0; i < m_views.count(); i++ )
4439 {
4440 lines.append( m_views.at( i )->cursorLine() );
4441 cols.append( m_views.at( i )->cursorColumn() );
4442 }
4443
4444 KateDocument::openURL( url() );
4445
4446 for ( uint i=0; i < m_views.count(); i++ )
4447 m_views.at( i )->setCursorPositionInternal( lines[ i ], cols[ i ], m_config->tabWidth(), false );
4448
4449 m_reloading = false;
4450
4451 for ( TQValueList<int>::size_type z=0; z < tmp.size(); z++ )
4452 {
4453 if (z < numLines())
4454 {
4455 if (textLine(tmp[z].mark.line) == tmp[z].line)
4456 setMark (tmp[z].mark.line, tmp[z].mark.type);
4457 }
4458 }
4459
4460 if (byUser)
4461 setHlMode (mode);
4462 }
4463}
4464
4465void KateDocument::flush ()
4466{
4467 closeURL ();
4468}
4469
4470void KateDocument::setWordWrap (bool on)
4471{
4472 config()->setWordWrap (on);
4473}
4474
4475bool KateDocument::wordWrap ()
4476{
4477 return config()->wordWrap ();
4478}
4479
4480void KateDocument::setWordWrapAt (uint col)
4481{
4482 config()->setWordWrapAt (col);
4483}
4484
4485unsigned int KateDocument::wordWrapAt ()
4486{
4487 return config()->wordWrapAt ();
4488}
4489
4490void KateDocument::applyWordWrap ()
4491{
4492 // dummy to make the API happy
4493}
4494
4495void KateDocument::setPageUpDownMovesCursor (bool on)
4496{
4497 config()->setPageUpDownMovesCursor (on);
4498}
4499
4500bool KateDocument::pageUpDownMovesCursor ()
4501{
4502 return config()->pageUpDownMovesCursor ();
4503}
4504
4505void KateDocument::dumpRegionTree()
4506{
4507 m_buffer->foldingTree()->debugDump();
4508}
4509//END
4510
4511//BEGIN KTextEditor::CursorInterface stuff
4512
4513KTextEditor::Cursor *KateDocument::createCursor ( )
4514{
4515 return new KateSuperCursor (this, false, 0, 0, this);
4516}
4517
4518void KateDocument::tagArbitraryLines(KateView* view, KateSuperRange* range)
4519{
4520 if (view)
4521 view->tagLines(range->start(), range->end());
4522 else
4523 tagLines(range->start(), range->end());
4524}
4525
4526void KateDocument::lineInfo (KateLineInfo *info, unsigned int line)
4527{
4528 m_buffer->lineInfo(info,line);
4529}
4530
4531KateCodeFoldingTree *KateDocument::foldingTree ()
4532{
4533 return m_buffer->foldingTree();
4534}
4535
4536void KateDocument::setEncoding (const TQString &e)
4537{
4538 if ( m_encodingSticky )
4539 return;
4540
4541 TQString ce = m_config->encoding().lower();
4542 if ( e.lower() == ce )
4543 return;
4544
4545 m_config->setEncoding( e );
4546 if ( ! m_loading )
4547 reloadFile();
4548}
4549
4550TQString KateDocument::encoding() const
4551{
4552 return m_config->encoding();
4553}
4554
4555void KateDocument::updateConfig ()
4556{
4557 emit undoChanged ();
4558 tagAll();
4559
4560 for (KateView * view = m_views.first(); view != 0L; view = m_views.next() )
4561 {
4562 view->updateDocumentConfig ();
4563 }
4564
4565 // switch indenter if needed
4566 if (m_indenter->modeNumber() != m_config->indentationMode())
4567 {
4568 delete m_indenter;
4569 m_indenter = KateAutoIndent::createIndenter ( this, m_config->indentationMode() );
4570 }
4571
4572 m_indenter->updateConfig();
4573
4574 m_buffer->setTabWidth (config()->tabWidth());
4575
4576 // plugins
4577 for (uint i=0; i<KateFactory::self()->plugins().count(); i++)
4578 {
4579 if (config()->plugin (i))
4580 loadPlugin (i);
4581 else
4582 unloadPlugin (i);
4583 }
4584}
4585
4586//BEGIN Variable reader
4587// "local variable" feature by anders, 2003
4588/* TODO
4589 add config options (how many lines to read, on/off)
4590 add interface for plugins/apps to set/get variables
4591 add view stuff
4592*/
4593TQRegExp KateDocument::kvLine = TQRegExp("kate:(.*)");
4594TQRegExp KateDocument::kvLineWildcard = TQRegExp("kate-wildcard\\((.*)\\):(.*)");
4595TQRegExp KateDocument::kvLineMime = TQRegExp("kate-mimetype\\((.*)\\):(.*)");
4596TQRegExp KateDocument::kvVar = TQRegExp("([\\w\\-]+)\\s+([^;]+)");
4597
4598void KateDocument::readVariables(bool onlyViewAndRenderer)
4599{
4600 if (!onlyViewAndRenderer)
4601 m_config->configStart();
4602
4603 // views!
4604 KateView *v;
4605 for (v = m_views.first(); v != 0L; v= m_views.next() )
4606 {
4607 v->config()->configStart();
4608 v->renderer()->config()->configStart();
4609 }
4610 // read a number of lines in the top/bottom of the document
4611 for (uint i=0; i < kMin( 9U, numLines() ); ++i )
4612 {
4613 readVariableLine( textLine( i ), onlyViewAndRenderer );
4614 }
4615 if ( numLines() > 10 )
4616 {
4617 for ( uint i = kMax(10U, numLines() - 10); i < numLines(); ++i )
4618 {
4619 readVariableLine( textLine( i ), onlyViewAndRenderer );
4620 }
4621 }
4622
4623 if (!onlyViewAndRenderer)
4624 m_config->configEnd();
4625
4626 for (v = m_views.first(); v != 0L; v= m_views.next() )
4627 {
4628 v->config()->configEnd();
4629 v->renderer()->config()->configEnd();
4630 }
4631}
4632
4633void KateDocument::readVariableLine( TQString t, bool onlyViewAndRenderer )
4634{
4635 // simple check first, no regex
4636 // no kate inside, no vars, simple...
4637 if (t.find("kate") < 0)
4638 return;
4639
4640 // found vars, if any
4641 TQString s;
4642
4643 if ( kvLine.search( t ) > -1 )
4644 {
4645 s = kvLine.cap(1);
4646
4647 kdDebug (13020) << "normal variable line kate: matched: " << s << endl;
4648 }
4649 else if (kvLineWildcard.search( t ) > -1) // regex given
4650 {
4651 TQStringList wildcards (TQStringList::split(';', kvLineWildcard.cap(1)));
4652 TQString nameOfFile = url().fileName();
4653
4654 bool found = false;
4655 for (TQStringList::size_type i = 0; !found && i < wildcards.size(); ++i)
4656 {
4657 TQRegExp wildcard (wildcards[i], true/*TQt::CaseSensitive*/, true/*TQRegExp::Wildcard*/);
4658
4659 found = wildcard.exactMatch (nameOfFile);
4660 }
4661
4662 // nothing usable found...
4663 if (!found)
4664 return;
4665
4666 s = kvLineWildcard.cap(2);
4667
4668 kdDebug (13020) << "guarded variable line kate-wildcard: matched: " << s << endl;
4669 }
4670 else if (kvLineMime.search( t ) > -1) // mime-type given
4671 {
4672 TQStringList types (TQStringList::split(';', kvLineMime.cap(1)));
4673
4674 // no matching type found
4675 if (!types.contains (mimeType ()))
4676 return;
4677
4678 s = kvLineMime.cap(2);
4679
4680 kdDebug (13020) << "guarded variable line kate-mimetype: matched: " << s << endl;
4681 }
4682 else // nothing found
4683 {
4684 return;
4685 }
4686
4687 TQStringList vvl; // view variable names
4688 vvl << "dynamic-word-wrap" << "dynamic-word-wrap-indicators"
4689 << "line-numbers" << "icon-border" << "folding-markers"
4690 << "bookmark-sorting" << "auto-center-lines"
4691 << "icon-bar-color"
4692 // renderer
4693 << "background-color" << "selection-color"
4694 << "current-line-color" << "bracket-highlight-color"
4695 << "word-wrap-marker-color"
4696 << "font" << "font-size" << "scheme";
4697 int p( 0 );
4698
4699 TQString var, val;
4700 while ( (p = kvVar.search( s, p )) > -1 )
4701 {
4702 p += kvVar.matchedLength();
4703 var = kvVar.cap( 1 );
4704 val = TQString(kvVar.cap( 2 )).stripWhiteSpace();
4705 bool state; // store booleans here
4706 int n; // store ints here
4707
4708 // only apply view & renderer config stuff
4709 if (onlyViewAndRenderer)
4710 {
4711 if ( vvl.contains( var ) ) // FIXME define above
4712 setViewVariable( var, val );
4713 }
4714 else
4715 {
4716 // BOOL SETTINGS
4717 if ( var == "word-wrap" && checkBoolValue( val, &state ) )
4718 setWordWrap( state ); // ??? FIXME CHECK
4719 else if ( var == "block-selection" && checkBoolValue( val, &state ) )
4720 setBlockSelectionMode( state );
4721 // KateConfig::configFlags
4722 // FIXME should this be optimized to only a few calls? how?
4723 else if ( var == "backspace-indents" && checkBoolValue( val, &state ) )
4724 m_config->setConfigFlags( KateDocumentConfig::cfBackspaceIndents, state );
4725 else if ( var == "replace-tabs" && checkBoolValue( val, &state ) )
4726 m_config->setConfigFlags( KateDocumentConfig::cfReplaceTabsDyn, state );
4727 else if ( var == "remove-trailing-space" && checkBoolValue( val, &state ) )
4728 m_config->setConfigFlags( KateDocumentConfig::cfRemoveTrailingDyn, state );
4729 else if ( var == "wrap-cursor" && checkBoolValue( val, &state ) )
4730 m_config->setConfigFlags( KateDocumentConfig::cfWrapCursor, state );
4731 else if ( var == "auto-brackets" && checkBoolValue( val, &state ) )
4732 m_config->setConfigFlags( KateDocumentConfig::cfAutoBrackets, state );
4733 else if ( var == "overwrite-mode" && checkBoolValue( val, &state ) )
4734 m_config->setConfigFlags( KateDocumentConfig::cfOvr, state );
4735 else if ( var == "keep-indent-profile" && checkBoolValue( val, &state ) )
4736 m_config->setConfigFlags( KateDocumentConfig::cfKeepIndentProfile, state );
4737 else if ( var == "keep-extra-spaces" && checkBoolValue( val, &state ) )
4738 m_config->setConfigFlags( KateDocumentConfig::cfKeepExtraSpaces, state );
4739 else if ( var == "tab-indents" && checkBoolValue( val, &state ) )
4740 m_config->setConfigFlags( KateDocumentConfig::cfTabIndents, state );
4741 else if ( var == "show-tabs" && checkBoolValue( val, &state ) )
4742 m_config->setConfigFlags( KateDocumentConfig::cfShowTabs, state );
4743 else if ( var == "space-indent" && checkBoolValue( val, &state ) )
4744 m_config->setConfigFlags( KateDocumentConfig::cfSpaceIndent, state );
4745 else if ( var == "smart-home" && checkBoolValue( val, &state ) )
4746 m_config->setConfigFlags( KateDocumentConfig::cfSmartHome, state );
4747 else if ( var == "replace-trailing-space-save" && checkBoolValue( val, &state ) )
4748 m_config->setConfigFlags( KateDocumentConfig::cfRemoveSpaces, state );
4749 else if ( var == "auto-insert-doxygen" && checkBoolValue( val, &state) )
4750 m_config->setConfigFlags( KateDocumentConfig::cfDoxygenAutoTyping, state);
4751 else if ( var == "mixed-indent" && checkBoolValue( val, &state ) )
4752 m_config->setConfigFlags( KateDocumentConfig::cfMixedIndent, state );
4753
4754 // INTEGER SETTINGS
4755 else if ( var == "tab-width" && checkIntValue( val, &n ) )
4756 m_config->setTabWidth( n );
4757 else if ( var == "indent-width" && checkIntValue( val, &n ) )
4758 m_config->setIndentationWidth( n );
4759 else if ( var == "indent-mode" )
4760 {
4761 if ( checkIntValue( val, &n ) )
4762 m_config->setIndentationMode( n );
4763 else
4764 m_config->setIndentationMode( KateAutoIndent::modeNumber( val) );
4765 }
4766 else if ( var == "word-wrap-column" && checkIntValue( val, &n ) && n > 0 ) // uint, but hard word wrap at 0 will be no fun ;)
4767 m_config->setWordWrapAt( n );
4768 else if ( var == "undo-steps" && checkIntValue( val, &n ) && n >= 0 )
4769 setUndoSteps( n );
4770
4771 // STRING SETTINGS
4772 else if ( var == "eol" || var == "end-of-line" )
4773 {
4774 TQStringList l;
4775 l << "unix" << "dos" << "mac";
4776 if ( (n = l.findIndex( val.lower() )) != -1 )
4777 m_config->setEol( n );
4778 }
4779 else if ( var == "encoding" )
4780 m_config->setEncoding( val );
4781 else if ( var == "syntax" || var == "hl" )
4782 {
4783 for ( uint i=0; i < hlModeCount(); i++ )
4784 {
4785 if ( hlModeName( i ).lower() == val.lower() )
4786 {
4787 setHlMode( i );
4788 break;
4789 }
4790 }
4791 }
4792
4793 // VIEW SETTINGS
4794 else if ( vvl.contains( var ) )
4795 setViewVariable( var, val );
4796 else
4797 {
4798 m_storedVariables.insert( var, val );
4799 emit variableChanged( var, val );
4800 }
4801 }
4802 }
4803}
4804
4805void KateDocument::setViewVariable( TQString var, TQString val )
4806{
4807 KateView *v;
4808 bool state;
4809 int n;
4810 TQColor c;
4811 for (v = m_views.first(); v != 0L; v= m_views.next() )
4812 {
4813 if ( var == "dynamic-word-wrap" && checkBoolValue( val, &state ) )
4814 v->config()->setDynWordWrap( state );
4815 else if ( var == "persistent-selection" && checkBoolValue( val, &state ) )
4816 v->config()->setPersistentSelection( state );
4817 //else if ( var = "dynamic-word-wrap-indicators" )
4818 else if ( var == "line-numbers" && checkBoolValue( val, &state ) )
4819 v->config()->setLineNumbers( state );
4820 else if (var == "icon-border" && checkBoolValue( val, &state ) )
4821 v->config()->setIconBar( state );
4822 else if (var == "folding-markers" && checkBoolValue( val, &state ) )
4823 v->config()->setFoldingBar( state );
4824 else if ( var == "auto-center-lines" && checkIntValue( val, &n ) )
4825 v->config()->setAutoCenterLines( n ); // FIXME uint, > N ??
4826 else if ( var == "icon-bar-color" && checkColorValue( val, c ) )
4827 v->renderer()->config()->setIconBarColor( c );
4828 // RENDERER
4829 else if ( var == "background-color" && checkColorValue( val, c ) )
4830 v->renderer()->config()->setBackgroundColor( c );
4831 else if ( var == "selection-color" && checkColorValue( val, c ) )
4832 v->renderer()->config()->setSelectionColor( c );
4833 else if ( var == "current-line-color" && checkColorValue( val, c ) )
4834 v->renderer()->config()->setHighlightedLineColor( c );
4835 else if ( var == "bracket-highlight-color" && checkColorValue( val, c ) )
4836 v->renderer()->config()->setHighlightedBracketColor( c );
4837 else if ( var == "word-wrap-marker-color" && checkColorValue( val, c ) )
4838 v->renderer()->config()->setWordWrapMarkerColor( c );
4839 else if ( var == "font" || ( var == "font-size" && checkIntValue( val, &n ) ) )
4840 {
4841 TQFont _f( *v->renderer()->config()->font( ) );
4842
4843 if ( var == "font" )
4844 {
4845 _f.setFamily( val );
4846 _f.setFixedPitch( TQFont( val ).fixedPitch() );
4847 }
4848 else
4849 _f.setPointSize( n );
4850
4851 v->renderer()->config()->setFont( _f );
4852 }
4853 else if ( var == "scheme" )
4854 {
4855 v->renderer()->config()->setSchema( KateFactory::self()->schemaManager()->number( val ) );
4856 }
4857 }
4858}
4859
4860bool KateDocument::checkBoolValue( TQString val, bool *result )
4861{
4862 val = val.stripWhiteSpace().lower();
4863 TQStringList l;
4864 l << "1" << "on" << "true";
4865 if ( l.contains( val ) )
4866 {
4867 *result = true;
4868 return true;
4869 }
4870 l.clear();
4871 l << "0" << "off" << "false";
4872 if ( l.contains( val ) )
4873 {
4874 *result = false;
4875 return true;
4876 }
4877 return false;
4878}
4879
4880bool KateDocument::checkIntValue( TQString val, int *result )
4881{
4882 bool ret( false );
4883 *result = val.toInt( &ret );
4884 return ret;
4885}
4886
4887bool KateDocument::checkColorValue( TQString val, TQColor &c )
4888{
4889 c.setNamedColor( val );
4890 return c.isValid();
4891}
4892
4893// KTextEditor::variable
4894TQString KateDocument::variable( const TQString &name ) const
4895{
4896 if ( m_storedVariables.contains( name ) )
4897 return m_storedVariables[ name ];
4898
4899 return "";
4900}
4901
4902//END
4903
4904void KateDocument::slotModOnHdDirty (const TQString &path)
4905{
4906 if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 1))
4907 {
4908 // compare md5 with the one we have (if we have one)
4909 if ( ! m_digest.isEmpty() )
4910 {
4911 TQCString tmp;
4912 if ( createDigest( tmp ) && tmp == m_digest )
4913 return;
4914 }
4915
4916 m_modOnHd = true;
4917 m_modOnHdReason = 1;
4918
4919 // reenable dialog if not running atm
4920 if (m_isasking == -1)
4921 m_isasking = false;
4922
4923 emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason);
4924 }
4925}
4926
4927void KateDocument::slotModOnHdCreated (const TQString &path)
4928{
4929 if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 2))
4930 {
4931 m_modOnHd = true;
4932 m_modOnHdReason = 2;
4933
4934 // reenable dialog if not running atm
4935 if (m_isasking == -1)
4936 m_isasking = false;
4937
4938 emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason);
4939 }
4940}
4941
4942void KateDocument::slotModOnHdDeleted (const TQString &path)
4943{
4944 if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != 3))
4945 {
4946 m_modOnHd = true;
4947 m_modOnHdReason = 3;
4948
4949 // reenable dialog if not running atm
4950 if (m_isasking == -1)
4951 m_isasking = false;
4952
4953 emit modifiedOnDisc (this, m_modOnHd, m_modOnHdReason);
4954 }
4955}
4956
4957bool KateDocument::createDigest( TQCString &result )
4958{
4959 bool ret = false;
4960 result = "";
4961 if ( url().isLocalFile() )
4962 {
4963 TQFile f ( url().path() );
4964 if ( f.open( IO_ReadOnly) )
4965 {
4966 KMD5 md5;
4967 ret = md5.update( f );
4968 md5.hexDigest( result );
4969 f.close();
4970 ret = true;
4971 }
4972 }
4973 return ret;
4974}
4975
4976TQString KateDocument::reasonedMOHString() const
4977{
4978 switch( m_modOnHdReason )
4979 {
4980 case 1:
4981 return i18n("The file '%1' was modified by another program.").arg( url().prettyURL() );
4982 break;
4983 case 2:
4984 return i18n("The file '%1' was created by another program.").arg( url().prettyURL() );
4985 break;
4986 case 3:
4987 return i18n("The file '%1' was deleted by another program.").arg( url().prettyURL() );
4988 break;
4989 default:
4990 return TQString();
4991 }
4992}
4993
4994void KateDocument::removeTrailingSpace( uint line )
4995{
4996 // remove trailing spaces from left line if required
4997 if ( config()->configFlags() & KateDocumentConfig::cfRemoveTrailingDyn )
4998 {
4999 KateTextLine::Ptr ln = kateTextLine( line );
5000
5001 if ( ! ln ) return;
5002
5003 if ( line == activeView()->cursorLine()
5004 && activeView()->cursorColumnReal() >= (uint)kMax(0,ln->lastChar()) )
5005 return;
5006
5007 if ( ln->length() )
5008 {
5009 uint p = ln->lastChar() + 1;
5010 uint l = ln->length() - p;
5011 if ( l )
5012 editRemoveText( line, p, l);
5013 }
5014 }
5015}
5016
5017void KateDocument::updateFileType (int newType, bool user)
5018{
5019 if (user || !m_fileTypeSetByUser)
5020 {
5021 const KateFileType *t = 0;
5022 if ((newType == -1) || (t = KateFactory::self()->fileTypeManager()->fileType (newType)))
5023 {
5024 m_fileType = newType;
5025
5026 if (t)
5027 {
5028 m_config->configStart();
5029 // views!
5030 KateView *v;
5031 for (v = m_views.first(); v != 0L; v= m_views.next() )
5032 {
5033 v->config()->configStart();
5034 v->renderer()->config()->configStart();
5035 }
5036
5037 readVariableLine( t->varLine );
5038
5039 m_config->configEnd();
5040 for (v = m_views.first(); v != 0L; v= m_views.next() )
5041 {
5042 v->config()->configEnd();
5043 v->renderer()->config()->configEnd();
5044 }
5045 }
5046 }
5047 }
5048}
5049
5050uint KateDocument::documentNumber () const
5051{
5052 return KTextEditor::Document::documentNumber ();
5053}
5054
5055long KateDocument::documentListPosition () const
5056{
5057 return KTextEditor::Document::documentListPosition ();
5058}
5059
5060void KateDocument::setDocumentListPosition (long pos)
5061{
5062 KTextEditor::Document::setDocumentListPosition (pos);
5063}
5064
5065
5066
5067void KateDocument::slotQueryClose_save(bool *handled, bool* abortClosing) {
5068 *handled=true;
5069 *abortClosing=true;
5070 if (m_url.isEmpty())
5071 {
5072 KEncodingFileDialog::Result res=KEncodingFileDialog::getSaveURLAndEncoding(config()->encoding(),
5073 TQString::null,TQString::null,0,i18n("Save File"));
5074
5075 if( res.URLs.isEmpty() || !checkOverwrite( res.URLs.first() ) ) {
5076 *abortClosing=true;
5077 return;
5078 }
5079 setEncoding( res.encoding );
5080 saveAs( res.URLs.first() );
5081 *abortClosing=false;
5082 }
5083 else
5084 {
5085 save();
5086 *abortClosing=false;
5087 }
5088
5089}
5090
5091bool KateDocument::checkOverwrite( KURL u )
5092{
5093 if( !u.isLocalFile() )
5094 return true;
5095
5096 TQFileInfo info( u.path() );
5097 if( !info.exists() )
5098 return true;
5099
5100 return KMessageBox::Cancel != KMessageBox::warningContinueCancel( 0,
5101 i18n( "A file named \"%1\" already exists. "
5102 "Are you sure you want to overwrite it?" ).arg( info.fileName() ),
5103 i18n( "Overwrite File?" ),
5104 i18n( "&Overwrite" ) );
5105}
5106
5107void KateDocument::setDefaultEncoding (const TQString &encoding)
5108{
5109 s_defaultEncoding = encoding;
5110}
5111
5112//BEGIN KTextEditor::TemplateInterface
5113bool KateDocument::insertTemplateTextImplementation ( uint line, uint column, const TQString &templateString, const TQMap<TQString,TQString> &initialValues, TQWidget *) {
5114 return (new KateTemplateHandler(this,line,column,templateString,initialValues))->initOk();
5115}
5116
5117void KateDocument::testTemplateCode() {
5118 int col=activeView()->cursorColumn();
5119 int line=activeView()->cursorLine();
5120 insertTemplateText(line,col,"for ${index} \\${NOPLACEHOLDER} ${index} ${blah} ${fullname} \\$${Placeholder} \\${${PLACEHOLDER2}}\n next line:${ANOTHERPLACEHOLDER} $${DOLLARBEFOREPLACEHOLDER} {NOTHING} {\n${cursor}\n}",TQMap<TQString,TQString>());
5121}
5122
5123bool KateDocument::invokeTabInterceptor(KKey key) {
5124 if (m_tabInterceptor) return (*m_tabInterceptor)(key);
5125 return false;
5126}
5127
5128bool KateDocument::setTabInterceptor(KateKeyInterceptorFunctor *interceptor) {
5129 if (m_tabInterceptor) return false;
5130 m_tabInterceptor=interceptor;
5131 return true;
5132}
5133
5134bool KateDocument::removeTabInterceptor(KateKeyInterceptorFunctor *interceptor) {
5135 if (m_tabInterceptor!=interceptor) return false;
5136 m_tabInterceptor=0;
5137 return true;
5138}
5139//END KTextEditor::TemplateInterface
5140
5141//BEGIN DEPRECATED STUFF
5142 bool KateDocument::setSelection ( uint startLine, uint startCol, uint endLine, uint endCol )
5143{ if (m_activeView) return m_activeView->setSelection (startLine, startCol, endLine, endCol); return false; }
5144
5145 bool KateDocument::clearSelection ()
5146 { if (m_activeView) return m_activeView->clearSelection(); return false; }
5147
5148 bool KateDocument::hasSelection () const
5149 { if (m_activeView) return m_activeView->hasSelection (); return false; }
5150
5151 TQString KateDocument::selection () const
5152 { if (m_activeView) return m_activeView->selection (); return TQString(""); }
5153
5154 bool KateDocument::removeSelectedText ()
5155 { if (m_activeView) return m_activeView->removeSelectedText (); return false; }
5156
5157 bool KateDocument::selectAll()
5158 { if (m_activeView) return m_activeView->selectAll (); return false; }
5159
5160 int KateDocument::selStartLine()
5161 { if (m_activeView) return m_activeView->selStartLine (); return 0; }
5162
5163 int KateDocument::selStartCol()
5164 { if (m_activeView) return m_activeView->selStartCol (); return 0; }
5165
5166 int KateDocument::selEndLine()
5167 { if (m_activeView) return m_activeView->selEndLine (); return 0; }
5168
5169 int KateDocument::selEndCol()
5170 { if (m_activeView) return m_activeView->selEndCol (); return 0; }
5171
5172 bool KateDocument::blockSelectionMode ()
5173 { if (m_activeView) return m_activeView->blockSelectionMode (); return false; }
5174
5175bool KateDocument::setBlockSelectionMode (bool on)
5176 { if (m_activeView) return m_activeView->setBlockSelectionMode (on); return false; }
5177
5178bool KateDocument::toggleBlockSelectionMode ()
5179 { if (m_activeView) return m_activeView->toggleBlockSelectionMode (); return false; }
5180//END DEPRECATED
5181
5182//END DEPRECATED STUFF
KDialogBase
KDialogBase::addVBoxPage
TQVBox * addVBoxPage(const TQString &itemName, const TQString &header=TQString::null, const TQPixmap &pixmap=TQPixmap())
KDialogBase::Help
Help
KDialogBase::Ok
Ok
KDialogBase::Cancel
Cancel
KKey
KMD5
KMD5::update
void update(const char *in, int len=-1)
KMD5::hexDigest
TQCString hexDigest()
KMessageBox::information
static void information(TQWidget *parent, const TQString &text, const TQString &caption=TQString::null, const TQString &dontShowAgainName=TQString::null, int options=Notify)
KMessageBox::error
static void error(TQWidget *parent, const TQString &text, const TQString &caption=TQString::null, int options=Notify)
KMessageBox::warningYesNoCancel
static int warningYesNoCancel(TQWidget *parent, const TQString &text, const TQString &caption=TQString::null, const KGuiItem &buttonYes=KStdGuiItem::yes(), const KGuiItem &buttonNo=KStdGuiItem::no(), const TQString &dontAskAgainName=TQString::null, int options=Notify)
KMessageBox::warningContinueCancel
static int warningContinueCancel(TQWidget *parent, const TQString &text, const TQString &caption=TQString::null, const KGuiItem &buttonContinue=KStdGuiItem::cont(), const TQString &dontAskAgainName=TQString::null, int options=Notify)
KParts::GUIActivateEvent
KParts::ReadOnlyPart::guiActivateEvent
virtual void guiActivateEvent(GUIActivateEvent *event)
KParts::ReadWritePart::setReadWrite
virtual void setReadWrite(bool readwrite=true)
KParts::ReadWritePart::saveAs
virtual bool saveAs(const KURL &url)
KParts::ReadWritePart::closeURL
virtual bool closeURL()
KParts::ReadWritePart::save
virtual bool save()
KParts::ReadWritePart::setModified
virtual void setModified()
KTempFile
KURL
KURL::path
TQString path() const
KURL::directory
TQString directory(bool _strip_trailing_slash_from_result=true, bool _ignore_trailing_slash_in_path=true) const
KURL::fileName
TQString fileName(bool _ignore_trailing_slash_in_path=true) const
KURL::filename
TQString filename(bool _ignore_trailing_slash_in_path=true) const
KURL::isLocalFile
bool isLocalFile() const
KURL::prettyURL
TQString prettyURL(int _trailing=0) const
KURL::isValid
bool isValid() const
KWin::setIcons
static void setIcons(WId win, const TQPixmap &icon, const TQPixmap &miniIcon)
KXMLGUIFactory
KXMLGUIFactory::removeClient
void removeClient(KXMLGUIClient *client)
KXMLGUIFactory::addClient
void addClient(KXMLGUIClient *client)
KateArbitraryHighlight
An arbitrary highlighting interface for Kate.
Definition: katearbitraryhighlight.h:61
KateAutoIndent::modeNumber
virtual uint modeNumber() const
Mode index of this mode.
Definition: kateautoindent.h:185
KateAutoIndent::createIndenter
static KateAutoIndent * createIndenter(KateDocument *doc, uint mode)
Static methods to create and list indention modes.
Definition: kateautoindent.cpp:38
KateBrowserExtension
Interface for embedding KateDocument into a browser.
Definition: katedocumenthelpers.h:37
KateBuffer
The KateBuffer class maintains a collections of lines.
Definition: katebuffer.h:342
KateBuffer::setMaxLoadedBlocks
static void setMaxLoadedBlocks(uint count)
modifier for max loaded blocks limit
Definition: katebuffer.cpp:79
KateBuffer::maxLoadedBlocks
static uint maxLoadedBlocks()
maximal loaded block count
Definition: katebuffer.h:352
KateDocCursor
Cursor class with a pointer to its document.
Definition: katecursor.h:93
KateModOnHdPrompt
This dialog will prompt the user for what do with a file that is modified on disk.
Definition: katedialogs.h:371
KateSuperCursor
Possible additional features:
Definition: katesupercursor.h:46
KateSuperRange
Represents a range of text, from the start() to the end().
Definition: katesupercursor.h:169
KateTextCursor
Simple cursor class with no document pointer.
Definition: katecursor.h:34
KateTextLine
The KateTextLine represents a line of text.
Definition: katetextline.h:42
KateUndoGroup
Class to manage a group of undo items.
Definition: kateundo.h:34
KateUndoGroup::UndoType
UndoType
Types for undo items.
Definition: kateundo.h:63
Kate::Document
This interface provides access to the Kate Document class.
Definition: document.h:190
Kate::View
The Kate::View text editor interface.
Definition: view.h:45
TDEConfigBase::readEntry
TQString readEntry(const TQString &pKey, const TQString &aDefault=TQString::null) const
TDEConfigBase::readNumEntry
int readNumEntry(const TQString &pKey, int nDefault=0) const
TDEConfigBase::readIntListEntry
TQValueList< int > readIntListEntry(const TQString &pKey) const
TDEConfigBase::sync
virtual void sync()
TDEConfigBase::writeEntry
void writeEntry(const TQString &pKey, const TQString &pValue, bool bPersistent=true, bool bGlobal=false, bool bNLS=false)
TDEConfigBase::setGroup
void setGroup(const TQString &group)
TDEConfig
TDEGlobal::dirs
static TDEStandardDirs * dirs()
TDEIcon::SizeMedium
SizeMedium
TDESharedPtr
TDESharedPtr::count
int count() const
TDEStandardDirs::relativeLocation
TQString relativeLocation(const char *type, const TQString &absPath)
endl
kndbgstream & endl(kndbgstream &s)
kdDebug
kdbgstream kdDebug(int area=0)
flush
kndbgstream & flush(kndbgstream &s)
KNotifyClient::instance
TDEInstance * instance()
KStdAction::clear
TDEAction * clear(const TQObject *recvr, const char *slot, TDEActionCollection *parent, const char *name=0)
KStdAction::saveAs
TDEAction * saveAs(const TQObject *recvr, const char *slot, TDEActionCollection *parent, const char *name=0)
Kate
Kate namespace All classes in this namespace must stay BC during one major release series (e....
Definition: document.h:51
TDEStdAccel::forward
const TDEShortcut & forward()
TDEStdAccel::key
int key(StdAccel id)
TDEStdAccel::name
TQString name(StdAccel id)
TDEStdAccel::end
const TDEShortcut & end()
TDEStdAccel::save
const TDEShortcut & save()
tdelocale.h

kate

Skip menu "kate"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kate

Skip menu "kate"
  • arts
  • dcop
  • dnssd
  • interfaces
  •   kspeech
  •     interface
  •     library
  •   tdetexteditor
  • kate
  • kded
  • kdoctools
  • kimgio
  • kjs
  • libtdemid
  • libtdescreensaver
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdeioslave
  •   http
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for kate by doxygen 1.9.4
This website is maintained by Timothy Pearson.