1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
|
/***************************************************************************
*
* Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
***************************************************************************/
#include <ntqfileinfo.h>
#include <tdeversion.h>
#include <tdetexteditor/selectioninterface.h>
#include <tdetexteditor/viewcursorinterface.h>
#include <tdetexteditor/popupmenuinterface.h>
#include <tdetexteditor/editinterface.h>
#include <kate/document.h>
#include <kate/view.h>
#include "editorpage.h"
#include "kscopeconfig.h"
/**
* Class constructor.
* @param pDoc The document object associated with this page
* @param pMenu A Cscope queries popup menu to use with the editor
* @param pParent The parent widget
* @param szName The widget's name
*/
EditorPage::EditorPage(KTextEditor::Document* pDoc, TQPopupMenu* pMenu,
TQTabWidget* pParent, const char* szName) : TQHBox(pParent, szName),
m_pParentTab(pParent),
m_pDoc(pDoc),
m_bOpen(false),
m_bNewFile(false),
m_sName(""),
m_bWritable(true), /* new documents are writable by default */
m_bModified(false),
m_nLine(0),
m_bSaveNewSizes(false)
{
KTextEditor::PopupMenuInterface* pMenuIf;
KTextEditor::ViewCursorInterface* pCursorIf;
// Create code-completion objects (will be deleted by TQObject destructor)
m_pCompletion = new SymbolCompletion(this, this);
// Set read-only mode, if required
if (Config().getReadOnlyMode())
m_pDoc->setReadWrite(false);
// Create the child widgets
m_pSplit = new TQSplitter(this);
m_pCtagsList = new CtagsList(m_pSplit);
m_pView = m_pDoc->createView(m_pSplit);
m_pSplit->setResizeMode(m_pCtagsList, TQSplitter::KeepSize);
// Perform tasks only when the document has been loaded completely
connect(m_pDoc, SIGNAL(completed()), this, SLOT(slotFileOpened()));
// Be notified when the text in the editor changes
connect(m_pDoc, SIGNAL(textChanged()), this, SLOT(slotSetModified()));
connect(m_pDoc, SIGNAL(undoChanged()), this, SLOT(slotUndoChanged()));
// Store the sizes of the child windows when the tag list is resized
// (since it may imply a move of the splitter divider)
connect(m_pCtagsList, SIGNAL(resized()), this, SLOT(slotChildResized()));
// Go to a symbol's line if it is selected in the tag list
connect(m_pCtagsList, SIGNAL(lineRequested(uint)), this,
SLOT(slotGotoLine(uint)));
// Add Ctag records to the tag list
connect(&m_ctags, SIGNAL(dataReady(FrontendToken*)), m_pCtagsList,
SLOT(slotDataReady(FrontendToken*)));
// Monitor Ctags' operation
connect(&m_ctags, SIGNAL(finished(uint)), m_pCtagsList,
SLOT(slotCtagsFinished(uint)));
// Set the context menu
pMenuIf = dynamic_cast<KTextEditor::PopupMenuInterface*>(m_pView);
if (pMenuIf)
pMenuIf->installPopup(pMenu);
// Emit a signal whenever the cursor's position changes
pCursorIf = dynamic_cast<KTextEditor::ViewCursorInterface*>(m_pView);
if (pCursorIf) {
connect(m_pView, SIGNAL(cursorPositionChanged()), this,
SLOT(slotCursorPosChange()));
}
}
/**
* Class destructor.
*/
EditorPage::~EditorPage()
{
}
/**
* Returns a pointer to the editor document object embedded in this page.
* @returns the document pointer
*/
KTextEditor::Document* EditorPage::getDocument()
{
return m_pDoc;
}
/**
* Returns a pointer to the editor view object embedded in this page.
* @returns the view pointer
*/
KTextEditor::View* EditorPage::getView()
{
return m_pView;
}
/**
* Returns the full path of the file being edited.
* @return The path of the file associated with the Document object, empty
* string if no file is currently open
*/
TQString EditorPage::getFilePath()
{
return m_pDoc->url().path();
}
/**
* Returns the name of the file being edited.
* @return The name of the file associated with the Document object, empty
* string if no file is currently open
*/
TQString EditorPage::getFileName()
{
return m_sName;
}
/**
* Determines whether this file can be modified, according to the file-system
* permissions, and KScope's global settings.
* @return true if this document can be changed, false otherwise
*/
bool EditorPage::isWritable()
{
// Check global settings first
if (Config().getReadOnlyMode())
return false;
// Return FS write permissions
return m_bWritable;
}
/**
* Determines if the file edited in this page was modified, and the changes
* were not yet saved.
* @return true if the file was modified, false otherwise
*/
bool EditorPage::isModified()
{
return m_pDoc->isModified();
}
/**
* Opens a file for editing.
* @param sFileName The full path name of the file to edit.
*/
void EditorPage::open(const TQString& sFileName)
{
// Open the given file
m_bOpen = false;
m_pDoc->openURL(sFileName);
}
/**
* Marks the page as containing a new unnamed file.
*/
void EditorPage::setNewFile()
{
m_bNewFile = true;
emit newFile(this);
}
/**
* Saves the edited file.
*/
void EditorPage::save()
{
if (m_pDoc->isModified())
m_pDoc->save();
}
/**
* Closes an edited file.
* @param bForce true to close the file regardless of any modifications,
* false to prompt the user in case of unsaved chnages
* @return true if the file has been closed, false if the user has aborted
*/
bool EditorPage::close(bool bForce)
{
TQString sPath;
// To override the prompt-on-close behaviour, we need to mark the file
// as unmodified
if (bForce)
m_pDoc->setModified(false);
// Close the file, unless the user aborts the action
sPath = m_pDoc->url().path();
if (!m_pDoc->closeURL())
return false;
emit fileClosed(sPath);
return true;
}
/**
* Applies any changes to the user preferences concerning an editor window.
*/
void EditorPage::applyPrefs()
{
// Determine whether the editor should work in a read-only mode
if (m_bWritable)
m_pDoc->setReadWrite(!Config().getReadOnlyMode());
// Apply preferences to the tag list of this window
m_pCtagsList->applyPrefs();
}
/**
* Sets the keyboard focus to the editor part of the page.
* This method is called whenever the page is activated. It is more reasonable
* to set the focus to the editor than to the tag list.
*/
void EditorPage::setEditorFocus()
{
m_pView->setFocus();
slotCursorPosChange();
}
/**
* Sets the keyboard focus to the tag list.
* This method is called when the "Go To Tag" menu command is invoked.
*/
void EditorPage::setTagListFocus()
{
m_pCtagsList->slotSetFocus();
}
/**
* Sets a bookmark at the given line.
* @param nLine The line to mark
*/
void EditorPage::addBookmark(uint nLine)
{
KTextEditor::MarkInterface* pMarkIf;
pMarkIf = dynamic_cast<KTextEditor::MarkInterface*>(m_pDoc);
if (pMarkIf)
pMarkIf->setMark(nLine, KTextEditor::MarkInterface::markType01);
}
/**
* Retrieves a list of all bookmarks in this page.
*/
void EditorPage::getBookmarks(FileLocationList& fll)
{
KTextEditor::MarkInterface* pMarkIf;
TQPtrList<KTextEditor::Mark> plMarks;
KTextEditor::Mark* pMark;
// Get the marks interface
pMarkIf = dynamic_cast<KTextEditor::MarkInterface*>(m_pDoc);
if (!pMarkIf)
return;
// Find all bookmarks
plMarks = pMarkIf->marks();
for (pMark = plMarks.first(); pMark; pMark = plMarks.next()) {
if (pMark->type == KTextEditor::MarkInterface::markType01)
fll.append(new FileLocation(getFilePath(), pMark->line, 0));
}
}
/**
* Returns the currently selected text in an open file.
* @return The selected text, or a null string if no text is currently
* selected
*/
TQString EditorPage::getSelection()
{
KTextEditor::SelectionInterface* pSelect;
// Get the selected text
pSelect = dynamic_cast<KTextEditor::SelectionInterface*>(m_pDoc);
if (!pSelect || !pSelect->hasSelection())
return TQString::null;
// Return the selected text
return pSelect->selection();
}
/**
* Returns a the complete word defined by the current cursor position.
* Attempts to extract a valid C symbol from the location of the cursor, by
* starting at the current line and column, and looking forward and backward
* for non-symbol characters.
* @return A C symbol under the cursor, if any, or TQString::null otherwise
*/
TQString EditorPage::getWordUnderCursor(uint* pPosInWord)
{
KTextEditor::ViewCursorInterface* pCursor;
KTextEditor::EditInterface* pEditIf;
TQString sLine;
uint nLine, nCol, nFrom, nTo, nLast, nLength;
TQChar ch;
// Get a cursor object
pCursor = dynamic_cast<KTextEditor::ViewCursorInterface*>(m_pView);
if (pCursor == NULL)
return TQString::null;
// Get a pointer to the edit interface
pEditIf = dynamic_cast<KTextEditor::EditInterface*>(m_pDoc);
if (!pEditIf)
return TQString::null;
// Get the line on which the cursor is positioned
pCursor->cursorPositionReal(&nLine, &nCol);
sLine = pEditIf->textLine(nLine);
// Find the beginning of the current word
for (nFrom = nCol; nFrom > 0;) {
ch = sLine.at(nFrom - 1);
if (!ch.isLetter() && !ch.isDigit() && ch != '_')
break;
nFrom--;
}
// Find the end of the current word
nLast = sLine.length();
for (nTo = nCol; nTo < nLast;) {
ch = sLine.at(nTo);
if (!ch.isLetter() && !ch.isDigit() && ch != '_')
break;
nTo++;
}
// Mark empty words
nLength = nTo - nFrom;
if (nLength == 0)
return TQString::null;
// Return the in-word position, if required
if (pPosInWord != NULL)
*pPosInWord = nCol - nFrom;
// Extract the word under the cursor from the entire line
return sLine.mid(nFrom, nLength);
}
/**
* Combines getSelection() and getWordUnderCursor() to return a suggested
* text for queries.
* The function first looks if any text is selected. If so, the selected text
* is returned. Otherwise, the word under the cursor location is returned, if
* one exists.
* @return Either the currently selected text, or the word under the cursor,
* or TQString::null if both options fail
*/
TQString EditorPage::getSuggestedText()
{
TQString sText;
sText = getSelection();
if (sText == TQString::null)
sText = getWordUnderCursor();
return sText;
}
/**
* Returns the contents of the requested line.
* Truncates the leading and trailing white spaces.
* @param nLine The line number
* @return The text of the requested line, if successful, TQString::null
* otherwise
*/
TQString EditorPage::getLineContents(uint nLine)
{
KTextEditor::EditInterface* pEditIf;
TQString sLine;
// Cannot accept line 0
if (nLine == 0)
return TQString::null;
// Get a pointer to the edit interface
pEditIf = dynamic_cast<KTextEditor::EditInterface*>(m_pDoc);
if (!pEditIf)
return TQString::null;
// Get the line on which the cursor is positioned
sLine = pEditIf->textLine(nLine - 1);
return sLine.stripWhiteSpace();
}
/**
* Moves the editing caret to the beginning of a given line.
* @param nLine The line number to move to
*/
void EditorPage::slotGotoLine(uint nLine)
{
// Ensure there is an open document
if (!m_bOpen)
return;
// Set the cursor to the requested line
if (!setCursorPos(nLine))
return;
// Update Ctags view
m_pCtagsList->gotoLine(nLine);
// Set the focus to the selected line
m_pView->setFocus();
}
/**
* Sets this editor as the current page, when the edited file's name is
* selected in the "Window" menu.
*/
void EditorPage::slotMenuSelect()
{
m_pParentTab->setCurrentPage(m_pParentTab->indexOf(this));
}
/**
* Displays a list of possible completions for the symbol currently under the
* cursor.
*/
void EditorPage::slotCompleteSymbol()
{
m_pCompletion->slotComplete();
}
/**
* Stores the sizes of the child widgets whenever they are changed.
* This slot is connected to the resized() signal of the CtagsList child
* widget.
*/
void EditorPage::slotChildResized()
{
SPLIT_SIZES si;
// Only store sizes when allowed to
if (!m_bSaveNewSizes) {
m_bSaveNewSizes = true;
return;
}
// Get the current widths of the child widgets
si = m_pSplit->sizes();
if (si.count() == 2)
Config().setEditorSizes(si);
}
/**
* Sets the visibility status and sizes of the child widgets.
* @param bShowTagList true to show the tag list, false otherwise
* @param si The new sizes to use
*/
void EditorPage::setLayout(bool bShowTagList, const SPLIT_SIZES& si)
{
// Make sure sizes are not stored during this process
m_bSaveNewSizes = false;
// Adjust the layout
m_pCtagsList->setShown(bShowTagList);
if (bShowTagList)
m_pSplit->setSizes(si);
}
/**
* Returns the current position of the cursor.
* @param nLine Holds the line on which the cursor is currently located
* @param nCol Holds the column on which the cursor is currently located
* @return true if successful, false otherwise (cursor interface was not
* obtained)
*/
bool EditorPage::getCursorPos(uint& nLine, uint& nCol)
{
KTextEditor::ViewCursorInterface* pCursorIf;
// Acquire the view cursor
pCursorIf = dynamic_cast<KTextEditor::ViewCursorInterface*>(m_pView);
if (pCursorIf == NULL)
return false;
// Get the cursor position (adjusted to 1-based counting)
pCursorIf->cursorPosition(&nLine, &nCol);
nLine++;
nCol++;
return true;
}
/**
* Moves the cursor to a given position.
* @param nLine The cursor's new line number
* @param nCol The cursor's new column number
* @return true if successful, false otherwise (cursor interface was not
* obtained)
*/
bool EditorPage::setCursorPos(uint nLine, uint nCol)
{
Kate::View* pKateView;
KTextEditor::ViewCursorInterface* pCursorIf;
// Cannot accept line 0
if (nLine == 0)
return false;
// Adjust to 0-based counting
nLine--;
nCol--;
// Acquire the view cursor
pCursorIf = dynamic_cast<KTextEditor::ViewCursorInterface*>(m_pView);
if (pCursorIf == NULL)
return false;
// NOTE: The following code is a fix to a bug in Kate, which wrongly
// calculates the column number in setCursorPosition.
pKateView = dynamic_cast<Kate::View*>(m_pView);
if (pKateView != NULL) {
KTextEditor::EditInterface* pEditIf;
const char* szLine;
uint nRealCol;
uint nTabAdjust;
// Get a pointer to the edit interface
pEditIf = dynamic_cast<KTextEditor::EditInterface*>(m_pDoc);
if (!pEditIf)
return false;
nRealCol = 0;
// Check for out of bound line numbers
if (nLine < pEditIf->numLines()) {
// Get the contents of the requested line
szLine = pEditIf->textLine(nLine).latin1();
// Check for empty line
if (szLine != NULL) {
// The number of columns which a tab character adds
nTabAdjust = pKateView->tabWidth() - 1;
// Calculate the real column, based on the tab width
for (; nRealCol < nCol && szLine[nRealCol] != 0; nRealCol++) {
if (szLine[nRealCol] == '\t')
nCol -= nTabAdjust;
}
}
}
else {
// Marker set beyond end of file, move to the last line
nLine = pEditIf->numLines() - 1;
}
// Set the cursor position
pCursorIf->setCursorPositionReal(nLine, nRealCol);
}
else {
// Non-Kate editors: set the cursor position normally
pCursorIf->setCursorPosition(nLine, nCol);
}
return true;
}
void EditorPage::setTabWidth(uint nTabWidth)
{
Kate::Document* pKateDoc;
Kate::Command* pKateCmd;
TQString sCmd, sResult;
pKateDoc = dynamic_cast<Kate::Document*>(m_pDoc);
if ((pKateDoc) &&
(pKateCmd = pKateDoc->queryCommand("set-tab-width"))) {
sCmd.sprintf("set-tab-width %u", nTabWidth);
pKateCmd->exec((Kate::View*)m_pView, sCmd, sResult);
}
}
/**
* Called when a document has completed loading.
* Determines the file's properties and refreshes the tag list of the editor
* window.
* This slot is connected to the completed() signal of the document object.
* The signal is emitted when a new file is opened, or when a modified file is
* saved.
*/
void EditorPage::slotFileOpened()
{
TQFileInfo fi(m_pDoc->url().path());
// Get file information
m_sName = fi.fileName();
m_bWritable = fi.isWritable();
// Set read/write or read-only mode
m_pDoc->setReadWrite(!Config().getReadOnlyMode() && m_bWritable);
// Refresh the tag list
m_pCtagsList->clear();
m_ctags.run(m_pDoc->url().path());
// Check if this is a modified file that has just been saved
if (m_bModified)
emit fileSaved(m_pDoc->url().path(), m_bNewFile);
// Notify that the document has loaded
m_bOpen = true;
m_bModified = false;
emit fileOpened(this, m_pDoc->url().path());
// Set initial position of the cursor
m_nLine = 0;
slotCursorPosChange();
// This is no longer a new file
m_bNewFile = false;
}
/**
* Marks a file as modified when the contents of the editor change.
* This slot is conncted to the textChanged() signal of the Document object.
* In addition to marking the file, this method also emits the modified()
* signal.
*/
void EditorPage::slotSetModified()
{
// Only perform tasks if the file is not already marked
if (!m_bModified && m_pDoc->isModified()) {
m_bModified = true;
emit modified(this, m_bModified);
#if KDE_IS_VERSION(3,3,0)
Kate::DocumentExt* pKateDoc;
// If the editor is a Kate part, check whether it was modified on
// disk as well, and issue a warning if so
pKateDoc = dynamic_cast<Kate::DocumentExt*>(m_pDoc);
if (pKateDoc)
pKateDoc->slotModifiedOnDisk(dynamic_cast<Kate::View*>(m_pView));
#endif
}
// Start/restart the auto-completion timer
m_pCompletion->slotAutoComplete();
}
/**
* Marks a file as not modified if all undo levels have been applied.
* This slot is conncted to the undoChanged() signal of the Document object.
* In addition to marking the file, this method also emits the modified()
* signal.
*/
void EditorPage::slotUndoChanged()
{
// Check if the file contents have been fully restored
if (m_bModified && !m_pDoc->isModified()) {
m_bModified = false;
emit modified(this, m_bModified);
}
}
/**
* Handles changes in the cursor position.
* Emits a signal with the new line and column numbers.
*/
void EditorPage::slotCursorPosChange()
{
uint nLine, nCol;
// Find the new line and column number, and emit the signal
if (!getCursorPos(nLine, nCol))
return;
emit cursorPosChanged(nLine, nCol);
// Select the relevant symbol in the tag list
if (Config().getAutoTagHl() && (m_nLine != nLine)) {
m_pCtagsList->gotoLine(nLine);
m_nLine = nLine;
}
// Abort code completion on cursor changes during the completion
// process
m_pCompletion->abort();
}
#include "editorpage.moc"
|