1 /*
2 * This file is part of the Code::Blocks IDE and licensed under the GNU Lesser General Public License, version 3
3 * http://www.gnu.org/licenses/lgpl-3.0.html
4 *
5 * $Revision: 11970 $
6 * $Id: cbeditor.cpp 11970 2020-02-23 14:25:19Z fuscated $
7 * $HeadURL: svn://svn.code.sf.net/p/codeblocks/code/branches/release-20.xx/src/sdk/cbeditor.cpp $
8 */
9
10 #include "sdk_precomp.h"
11
12 #ifndef CB_PRECOMP
13 #include <wx/app.h>
14 #include <wx/filedlg.h>
15 #include <wx/filename.h>
16 #include <wx/menu.h>
17 #include <wx/notebook.h>
18 #include <wx/wfstream.h>
19
20 #include "cbeditor.h" // class's header file
21
22 #include "cbauibook.h"
23 #include "cbplugin.h"
24 #include "cbproject.h"
25 #include "configmanager.h"
26 #include "debuggermanager.h"
27 #include "editorcolourset.h"
28 #include "editormanager.h"
29 #include "globals.h"
30 #include "infowindow.h"
31 #include "logmanager.h"
32 #include "macrosmanager.h" // ReplaceMacros
33 #include "manager.h"
34 #include "pluginmanager.h"
35 #include "projectbuildtarget.h"
36 #include "projectfile.h"
37 #include "projectmanager.h"
38 #include "sdk_events.h"
39 #endif
40 #include "cbstyledtextctrl.h"
41 #include "cbcolourmanager.h"
42
43 #include <stack>
44
45 #include <wx/fontutil.h>
46 #include <wx/splitter.h>
47
48 #include "cbeditorprintout.h"
49 #include "cbdebugger_interfaces.h"
50 #include "editor_hooks.h"
51 #include "encodingdetector.h"
52 #include "filefilters.h"
53 #include "projectfileoptionsdlg.h"
54
55 const wxString g_EditorModified = _T("*");
56
57 #define ERROR_STYLE wxSCI_MARK_SMALLRECT
58 #define BOOKMARK_STYLE wxSCI_MARK_ARROW
59 #define BREAKPOINT_STYLE wxSCI_MARK_CIRCLE
60 #define DEBUG_STYLE wxSCI_MARK_ARROW
61 #define DEBUG_STYLE_HIGHLIGHT wxSCI_MARK_BACKGROUND
62
63 #define BREAKPOINT_OTHER_MARKER 1
64 #define BREAKPOINT_DISABLED_MARKER 2
65 #define BREAKPOINT_MARKER 3
66 #define BOOKMARK_MARKER 4
67 #define ERROR_MARKER 5
68 #define DEBUG_MARKER 6
69 #define DEBUG_MARKER_HIGHLIGHT 7
70
71 #define C_LINE_MARGIN 0 // Line numbers
72 #define C_MARKER_MARGIN 1 // Bookmarks, Breakpoints...
73 #define C_CHANGEBAR_MARGIN 2
74 #define C_FOLDING_MARGIN 3
75
76 const int foldingMarginBaseWidth = 16;
77 const int changeBarMarginBaseWidth = 4;
78
79 /* This struct holds private data for the cbEditor class.
80 * It's a paradigm to avoid rebuilding the entire project (as cbEditor is a basic dependency)
81 * for just adding a private var or method.
82 * What happens is that we 've declared a cbEditorInternalData* private member in cbEditor
83 * and define it in the .cpp file (here). Effectively, this means that we can now add/remove
84 * elements from cbEditorInternalData without needing to rebuild the project :)
85 * The cbEditor::m_pData is the variable to use in code. It's the very first thing
86 * constructed and the very last destructed.
87 *
88 * So, if we want to add a new private member in cbEditor, we add it here instead
89 * and access it with m_pData->
90 * e.g. m_pData->lastPosForCodeCompletion
91 * and of course you can add member functions here ;)
92 *
93 * cbEditorInternalData also contains a pointer to the owner cbEditor (named m_pOwner).
94 * Using m_pOwner the struct's member functions can access cbEditor functions
95 * (even private ones - it's a friend).
96 *
97 * The same logic should be used all around the project's classes, gradually.
98 */
99 struct cbEditorInternalData
100 {
101 cbEditor* m_pOwner;
102
cbEditorInternalDatacbEditorInternalData103 cbEditorInternalData(cbEditor* owner, LoaderBase* fileLoader = nullptr)
104 : m_pOwner(owner),
105 m_strip_trailing_spaces(true),
106 m_ensure_final_line_end(false),
107 m_ensure_consistent_line_ends(true),
108 m_LastMarginMenuLine(-1),
109 m_LastDebugLine(-1),
110 m_useByteOrderMark(false),
111 m_byteOrderMarkLength(0),
112 m_lineNumbersWidth(0),
113 m_lineNumbersWidth2(0),
114 m_pFileLoader(fileLoader)
115 {
116 m_encoding = wxLocale::GetSystemEncoding();
117
118 if (m_pFileLoader)
119 {
120 #ifdef fileload_measuring
121 wxStopWatch sw;
122 #endif
123 EncodingDetector enc(fileLoader);
124 if (enc.IsOK())
125 {
126 m_byteOrderMarkLength = enc.GetBOMSizeInBytes();
127 m_useByteOrderMark = enc.UsesBOM();
128 m_encoding = enc.GetFontEncoding();
129 }
130 #ifdef fileload_measuring
131 Manager::Get()->GetLogManager()->DebugLog(F(_T("Encoding via fileloader took : %d ms"),(int)sw.Time()));
132 #endif
133 }
134 }
135
~cbEditorInternalDatacbEditorInternalData136 ~cbEditorInternalData()
137 {
138 if (m_pFileLoader)
139 {
140 delete m_pFileLoader;
141 m_pFileLoader = nullptr;
142 }
143 }
144
145 // funcs
146
147 /** Strip trailing blanks before saving */
StripTrailingSpacescbEditorInternalData148 void StripTrailingSpaces()
149 {
150 cbStyledTextCtrl* control = m_pOwner->GetControl();
151 // The following code was adapted from the SciTE sourcecode
152
153 int maxLines = control->GetLineCount();
154 for (int line = 0; line < maxLines; line++)
155 {
156 int lineStart = control->PositionFromLine(line);
157 int lineEnd = control->GetLineEndPosition(line);
158 int i = lineEnd-1;
159 wxChar ch = (wxChar)(control->GetCharAt(i));
160 if (control->GetLexer() == wxSCI_LEX_DIFF)
161 lineStart++;
162 while ((i >= lineStart) && ((ch == _T(' ')) || (ch == _T('\t'))))
163 {
164 i--;
165 ch = (wxChar)(control->GetCharAt(i));
166 }
167 if (i < (lineEnd-1))
168 {
169 control->SetTargetStart(i+1);
170 control->SetTargetEnd(lineEnd);
171 control->ReplaceTarget(_T(""));
172 }
173 }
174 }
175
176 /** Add extra blank line to the file */
EnsureFinalLineEndcbEditorInternalData177 void EnsureFinalLineEnd()
178 {
179 cbStyledTextCtrl* control = m_pOwner->GetControl();
180 // The following code was adapted from the SciTE sourcecode
181 int maxLines = control->GetLineCount();
182 int enddoc = control->PositionFromLine(maxLines);
183 if (maxLines <= 1 || enddoc > control->PositionFromLine(maxLines-1))
184 control->InsertText(enddoc, GetEOLStr(m_pOwner->GetControl()->GetEOLMode()));
185 }
186
187 /** Make sure all the lines end with the same EOL mode */
EnsureConsistentLineEndscbEditorInternalData188 void EnsureConsistentLineEnds()
189 {
190 cbStyledTextCtrl* control = m_pOwner->GetControl();
191 // The following code was adapted from the SciTE sourcecode
192 control->ConvertEOLs(control->GetEOLMode());
193 }
194
195 /** Set line number column width */
SetLineNumberColWidthcbEditorInternalData196 void SetLineNumberColWidth(bool both=true)
197 {
198 ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("editor"));
199
200 if (cfg->ReadBool(_T("/show_line_numbers"), true))
201 {
202 if (m_pOwner->m_pControl2 && both)
203 {
204 int pixelWidth = m_pOwner->m_pControl->TextWidth(wxSCI_STYLE_LINENUMBER, _T("9"));
205 int pixelWidth2 = m_pOwner->m_pControl2->TextWidth(wxSCI_STYLE_LINENUMBER, _T("9"));
206
207 if (cfg->ReadBool(_T("/margin/dynamic_width"), false))
208 {
209 int lineNumChars = 1;
210 int lineCount = m_pOwner->m_pControl->GetLineCount();
211
212 while (lineCount >= 10)
213 {
214 lineCount /= 10;
215 ++lineNumChars;
216 }
217
218 int lineNumWidth = lineNumChars * pixelWidth + pixelWidth * 0.75;
219
220 if (lineNumWidth != m_lineNumbersWidth)
221 {
222 m_pOwner->m_pControl->SetMarginWidth(C_LINE_MARGIN, lineNumWidth);
223 m_lineNumbersWidth = lineNumWidth;
224 }
225
226 lineNumWidth = lineNumChars * pixelWidth2 + pixelWidth2 * 0.75;
227 if (lineNumWidth != m_lineNumbersWidth2)
228 {
229 m_pOwner->m_pControl2->SetMarginWidth(C_LINE_MARGIN, lineNumWidth);
230 m_lineNumbersWidth2 = lineNumWidth;
231 }
232 }
233 else
234 {
235 const int charsWidth = cfg->ReadInt(_T("/margin/width_chars"), 6);
236 const int newWidth = pixelWidth * (0.75 + charsWidth);
237 m_pOwner->m_pControl->SetMarginWidth(C_LINE_MARGIN, newWidth);
238 m_pOwner->m_pControl2->SetMarginWidth(C_LINE_MARGIN, newWidth);
239 }
240 }
241 else
242 {
243 cbStyledTextCtrl* control = m_pOwner->GetControl();
244 int* pLineNumbersWidth = nullptr;
245 if (control == m_pOwner->m_pControl)
246 pLineNumbersWidth = &m_lineNumbersWidth;
247 else
248 pLineNumbersWidth = &m_lineNumbersWidth2;
249
250 int pixelWidth = control->TextWidth(wxSCI_STYLE_LINENUMBER, _T("9"));
251
252 if (cfg->ReadBool(_T("/margin/dynamic_width"), false))
253 {
254 int lineNumChars = 1;
255 int lineCount = control->GetLineCount();
256
257 while (lineCount >= 10)
258 {
259 lineCount /= 10;
260 ++lineNumChars;
261 }
262
263 int lineNumWidth = lineNumChars * pixelWidth + pixelWidth * 0.75;
264
265 if (lineNumWidth != *pLineNumbersWidth)
266 {
267 control->SetMarginWidth(C_LINE_MARGIN, lineNumWidth);
268 *pLineNumbersWidth = lineNumWidth;
269 }
270 }
271 else
272 {
273 const int charsWidth = cfg->ReadInt(_T("/margin/width_chars"), 6);
274 const int newWidth = pixelWidth * (0.75 + charsWidth);
275 control->SetMarginWidth(C_LINE_MARGIN, newWidth);
276 }
277 }
278 }
279 else
280 {
281 m_pOwner->m_pControl->SetMarginWidth(C_LINE_MARGIN, 0);
282 if (m_pOwner->m_pControl2 && both)
283 m_pOwner->m_pControl2->SetMarginWidth(C_LINE_MARGIN, 0);
284 }
285 }
286
CalcWidthcbEditorInternalData287 static int CalcWidth(cbStyledTextCtrl* control, int baseWidth, int minWidth,
288 float defaultPointSize)
289 {
290 int width = baseWidth * (defaultPointSize + control->GetZoom()) / defaultPointSize;
291 if (width < minWidth)
292 width = minWidth;
293 return width;
294 }
295
296 /// Call this only if the margin with marginId is enabled and is visible.
SetColumnWidthcbEditorInternalData297 void SetColumnWidth(int marginId, int baseWidth, int minWidth, bool both)
298 {
299 const float pointSize = m_pOwner->m_pControl->StyleGetFont(wxSCI_STYLE_DEFAULT).GetPointSize();
300 if (both)
301 {
302 const int width = CalcWidth(m_pOwner->m_pControl, baseWidth, minWidth, pointSize);
303 m_pOwner->m_pControl->SetMarginWidth(marginId, width);
304 if (m_pOwner->m_pControl2)
305 {
306 const int width = CalcWidth(m_pOwner->m_pControl2, baseWidth, minWidth, pointSize);
307 m_pOwner->m_pControl2->SetMarginWidth(marginId, width);
308 }
309 }
310 else
311 {
312 cbStyledTextCtrl* control = m_pOwner->GetControl();
313 const int width = CalcWidth(control, baseWidth, minWidth, pointSize);
314 control->SetMarginWidth(marginId, width);
315 }
316 }
317
SetMarkerWidthcbEditorInternalData318 static void SetMarkerWidth(cbStyledTextCtrl* control, float defaultPointSize)
319 {
320 // Leave 15% for borders.
321 const float scale = 1.0f/1.15f;
322
323 const int oldWidth = floorf(control->GetMarginWidth(C_MARKER_MARGIN) * scale);
324 const int newWidth = CalcWidth(control, 18, 1, defaultPointSize);
325
326 const int width = floorf(newWidth * scale);
327
328 const int possibleWidths[] = { 8, 10, 12, 16, 20, 24, 28, 32, 40, 48, 56, 64 };
329
330 const int selectedWidth = cbFindMinSize(width, possibleWidths, cbCountOf(possibleWidths));
331 const int oldSelectedWidth = cbFindMinSize(oldWidth, possibleWidths, cbCountOf(possibleWidths));
332
333
334 // We don't want to reload images if the width haven't changed!
335 if (selectedWidth != oldSelectedWidth)
336 {
337 control->SetMarginWidth(C_MARKER_MARGIN, newWidth);
338 SetupBreakpointMarkers(control, selectedWidth);
339 }
340 }
341
SetMarkerColumnWidthcbEditorInternalData342 void SetMarkerColumnWidth(bool both)
343 {
344 const float pointSize = m_pOwner->m_pControl->StyleGetFont(wxSCI_STYLE_DEFAULT).GetPointSize();
345 if (both)
346 {
347 SetMarkerWidth(m_pOwner->m_pControl, pointSize);
348 if (m_pOwner->m_pControl2)
349 SetMarkerWidth(m_pOwner->m_pControl2, pointSize);
350 }
351 else
352 SetMarkerWidth(m_pOwner->GetControl(), pointSize);
353 }
354
SetupBreakpointMarkerscbEditorInternalData355 static void SetupBreakpointMarkers(cbStyledTextCtrl* control, int size)
356 {
357 wxString basepath = ConfigManager::GetDataFolder() + wxT("/manager_resources.zip#zip:/images/");
358 basepath += wxString::Format(wxT("%dx%d/"), size, size);
359 ConfigManager* mgr = Manager::Get()->GetConfigManager(_T("editor"));
360 bool imageBP = mgr->ReadBool(_T("/margin_1_image_bp"), true);
361 if (imageBP)
362 {
363 wxBitmap iconBP = cbLoadBitmap(basepath + wxT("breakpoint.png"), wxBITMAP_TYPE_PNG);
364 wxBitmap iconBPDis = cbLoadBitmap(basepath + wxT("breakpoint_disabled.png"), wxBITMAP_TYPE_PNG);
365 wxBitmap iconBPOth = cbLoadBitmap(basepath + wxT("breakpoint_other.png"), wxBITMAP_TYPE_PNG);
366 if (iconBP.IsOk() && iconBPDis.IsOk() && iconBPOth.IsOk())
367 {
368 control->MarkerDefineBitmap(BREAKPOINT_MARKER, iconBP );
369 control->MarkerDefineBitmap(BREAKPOINT_DISABLED_MARKER, iconBPDis);
370 control->MarkerDefineBitmap(BREAKPOINT_OTHER_MARKER, iconBPOth);
371 }
372 else
373 imageBP = false; // apply default markers
374 }
375 if (!imageBP)
376 {
377 control->MarkerDefine(BREAKPOINT_MARKER, BREAKPOINT_STYLE);
378 control->MarkerSetBackground(BREAKPOINT_MARKER, wxColour(0xFF, 0x00, 0x00));
379 control->MarkerDefine(BREAKPOINT_DISABLED_MARKER, BREAKPOINT_STYLE);
380 control->MarkerSetBackground(BREAKPOINT_DISABLED_MARKER, wxColour(0x90, 0x90, 0x90));
381 control->MarkerDefine(BREAKPOINT_OTHER_MARKER, BREAKPOINT_STYLE);
382 control->MarkerSetBackground(BREAKPOINT_OTHER_MARKER, wxColour(0x59, 0x74, 0x8e));
383 }
384 }
385
GetUrlcbEditorInternalData386 wxString GetUrl()
387 {
388 cbStyledTextCtrl* control = m_pOwner->GetControl();
389 if (!control)
390 return wxEmptyString;
391
392 wxRegEx reUrl(wxT("***:("
393 "((ht|f)tp(s?)\\:\\/\\/)"
394 "|(www\\.)"
395 ")"
396 "("
397 "([\\w\\-]+(\\.[\\w\\-]+)+)"
398 "|localhost"
399 ")"
400 "(\\/?)([\\w\\-\\.\\?\\,\\'\\/\\\\\\+&%\\$#]*)?"
401 "([\\d\\w\\.\\/\\%\\+\\-\\=\\&\\?\\:\\\\\\"\\'\\,\\|\\~\\;]*)"));
402 wxString url = control->GetSelectedText();
403 // Is the URL selected?
404 if (reUrl.Matches(url))
405 return reUrl.GetMatch(url);
406 // else is there a URL near the cursor?
407
408 // Find out start position
409 int startPos = control->GetCurrentPos();
410 const wxString space = wxT(" \n\r\t{}");
411 wxChar curCh = control->GetCharAt(startPos);
412 while ( (startPos > 0) && (space.Find(curCh) == -1) )
413 {
414 startPos--;
415 curCh = control->GetCharAt(startPos);
416 }
417
418 // Find out end position
419 int endPos = control->GetCurrentPos();
420 int maxPos = control->GetLineEndPosition(control->GetLineCount());
421 curCh = control->GetCharAt(endPos);
422 while ( (endPos < maxPos) && (space.Find(curCh) == -1) )
423 {
424 endPos++;
425 curCh = control->GetCharAt(endPos);
426 }
427
428 url = control->GetTextRange(startPos, endPos);
429 if ( (control->GetLexer() == wxSCI_LEX_CPP)
430 && ( (control->GetStyleAt(control->GetCurrentPos()) == wxSCI_C_STRING)
431 || (control->GetStyleAt(control->GetCurrentPos()) == wxSCI_C_STRINGEOL) ) )
432 {
433 url.Replace(wxT("\\n"), wxT("\n"));
434 url.Replace(wxT("\\r"), wxT("\r"));
435 url.Replace(wxT("\\t"), wxT("\t"));
436 }
437
438 if (reUrl.Matches(url))
439 {
440 wxString match = reUrl.GetMatch(url);
441 if ( (url.Find(match) + startPos < control->GetCurrentPos())
442 && (url.Find(match) + startPos + (int)match.Length() > control->GetCurrentPos()) )
443 {
444 url = match(0, match.find_last_not_of(wxT(",.")) + 1); // trim trailing
445 }
446 else
447 url = wxEmptyString; // nope, too far from cursor, return invalid (empty)
448 }
449 else
450 url = wxEmptyString; // nope, return invalid (empty)
451
452 return url;
453 }
454
GetLineIndentStringcbEditorInternalData455 static wxString GetLineIndentString(int line, cbStyledTextCtrl* stc)
456 {
457 int currLine = (line == -1)
458 ? stc->LineFromPosition(stc->GetCurrentPos())
459 : line;
460 wxString text = stc->GetLine(currLine);
461 unsigned int len = text.Length();
462 wxString indent;
463 for (unsigned int i = 0; i < len; ++i)
464 {
465 if (text[i] == _T(' ') || text[i] == _T('\t'))
466 {
467 indent << text[i];
468 }
469 else
470 {
471 break;
472 }
473 }
474 return indent;
475 }
476
477 /** Detect the indentation style used in a document.
478 * Values for the thresholds are from trial and error over many
479 * example files. If this function is not certain on the style
480 * used, it will return -1.
481 */
DetectIndentStylecbEditorInternalData482 static int DetectIndentStyle(cbStyledTextCtrl* stc)
483 {
484 int lineCount[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
485 // lineCount[0] == number of lines with tabs
486 // lineCount[1] == number of lines with only spaces
487 // lineCount[2 ... 8] == number of lines divisible by that number
488 // Scan 1000 lines from the middle of the file to generate statistics
489 const int maxLine = std::min(stc->GetLineCount(), stc->GetLineCount() / 2 + 500);
490 for (int line = std::max(0, stc->GetLineCount() / 2 - 500); line < maxLine; ++line)
491 {
492 const wxString& indent = cbEditorInternalData::GetLineIndentString(line, stc);
493 if (indent.IsEmpty())
494 continue;
495 if (indent.Find(wxT('\t')) != wxNOT_FOUND)
496 ++lineCount[0];
497 else
498 {
499 ++lineCount[1];
500 for (int i = 2; i < 9; ++i)
501 {
502 if (indent.Length() % i == 0)
503 ++lineCount[i];
504 }
505 }
506 }
507
508 if (lineCount[0] > 0 && lineCount[1] == 0)
509 return 0; // tabs
510 double total = lineCount[0] + lineCount[1];
511 if (total < 10)
512 return -1; // not sure -> use defaults
513 else if (lineCount[0] / total > 0.75)
514 return 0; // tabs
515 else if (lineCount[1] / total < 0.75)
516 return -1; // not sure -> use defaults
517
518 total = lineCount[1];
519 int tabSize = 8;
520 for (int i = 2; i < 8; ++i)
521 {
522 if (lineCount[i] > lineCount[tabSize])
523 tabSize = i;
524 }
525 if (lineCount[tabSize] / total < 0.65)
526 return -1; // not sure -> use defaults
527
528 switch (tabSize)
529 {
530 case 2:
531 if ((lineCount[2] - lineCount[6]) / total < 0.1)
532 return 6;
533 if ( lineCount[2] > lineCount[4] * 1.8
534 || lineCount[4] / total < 0.5 )
535 return 2;
536 // fall through
537 case 4:
538 if ( lineCount[4] > lineCount[8] * 1.8
539 || lineCount[8] / total < 0.5 )
540 return 4;
541 // fall through
542 case 8:
543 if (lineCount[8] / total < 0.6)
544 return -1; // not sure -> use defaults
545 return 8;
546
547 case 3:
548 if ( lineCount[3] > lineCount[6] * 1.8
549 || lineCount[6] / total < 0.5 )
550 return 3;
551 if (lineCount[6] / total < 0.6)
552 return -1; // not sure -> use defaults
553 return 6;
554
555 default:
556 if (lineCount[tabSize] / total < 0.7)
557 return -1; // not sure -> use defaults
558 return tabSize;
559 }
560 }
561
562 // vars
563 bool m_strip_trailing_spaces;
564 bool m_ensure_final_line_end;
565 bool m_ensure_consistent_line_ends;
566
567 int m_LastMarginMenuLine;
568 int m_LastDebugLine;
569
570 bool mFoldingLimit;
571 int mFoldingLimitLevel;
572
573 wxFontEncoding m_encoding;
574 bool m_useByteOrderMark;
575 int m_byteOrderMarkLength;
576
577 int m_lineNumbersWidth;
578 int m_lineNumbersWidth2;
579
580 LoaderBase* m_pFileLoader;
581 };
582 ////////////////////////////////////////////////////////////////////////////////
583
584 const int idEmptyMenu = wxNewId();
585 const int idEdit = wxNewId();
586 const int idUndo = wxNewId();
587 const int idRedo = wxNewId();
588 const int idClearHistory = wxNewId();
589 const int idCut = wxNewId();
590 const int idCopy = wxNewId();
591 const int idPaste = wxNewId();
592 const int idDelete = wxNewId();
593 const int idUpperCase = wxNewId();
594 const int idLowerCase = wxNewId();
595 const int idSelectAll = wxNewId();
596 const int idSwapHeaderSource = wxNewId();
597 const int idOpenContainingFolder = wxNewId();
598 const int idBookmarks = wxNewId();
599 const int idBookmarksToggle = wxNewId();
600 const int idBookmarksPrevious = wxNewId();
601 const int idBookmarksNext = wxNewId();
602 const int idBookmarksClearAll = wxNewId();
603 const int idFolding = wxNewId();
604 const int idFoldingFoldAll = wxNewId();
605 const int idFoldingUnfoldAll = wxNewId();
606 const int idFoldingToggleAll = wxNewId();
607 const int idFoldingFoldCurrent = wxNewId();
608 const int idFoldingUnfoldCurrent = wxNewId();
609 const int idFoldingToggleCurrent = wxNewId();
610 const int idInsert = wxNewId();
611 const int idSplit = wxNewId();
612 const int idSplitHorz = wxNewId();
613 const int idSplitVert = wxNewId();
614 const int idUnsplit = wxNewId();
615 const int idProperties = wxNewId();
616 const int idAddFileToProject = wxNewId();
617 const int idRemoveFileFromProject = wxNewId();
618 const int idShowFileInProject = wxNewId();
619 const int idOpenUrl = wxNewId();
620
621 const int idBookmarkAdd = wxNewId();
622 const int idBookmarkRemove = wxNewId();
623 const int idBookmarkRemoveAll = wxNewId();
624
625 const int idBreakpointAdd = wxNewId();
626 const int idBreakpointEdit = wxNewId();
627 const int idBreakpointRemove = wxNewId();
628 const long idBreakpointEnable = wxNewId();
629 const long idBreakpointDisable = wxNewId();
630
BEGIN_EVENT_TABLE(cbEditor,EditorBase)631 BEGIN_EVENT_TABLE(cbEditor, EditorBase)
632 EVT_CLOSE(cbEditor::OnClose)
633 // we got dynamic events; look in ConnectEvents()
634
635 EVT_MENU(idUndo, cbEditor::OnContextMenuEntry)
636 EVT_MENU(idRedo, cbEditor::OnContextMenuEntry)
637 EVT_MENU(idClearHistory, cbEditor::OnContextMenuEntry)
638 EVT_MENU(idCut, cbEditor::OnContextMenuEntry)
639 EVT_MENU(idCopy, cbEditor::OnContextMenuEntry)
640 EVT_MENU(idPaste, cbEditor::OnContextMenuEntry)
641 EVT_MENU(idDelete, cbEditor::OnContextMenuEntry)
642 EVT_MENU(idUpperCase, cbEditor::OnContextMenuEntry)
643 EVT_MENU(idLowerCase, cbEditor::OnContextMenuEntry)
644 EVT_MENU(idSelectAll, cbEditor::OnContextMenuEntry)
645 EVT_MENU(idSwapHeaderSource, cbEditor::OnContextMenuEntry)
646 EVT_MENU(idOpenContainingFolder, cbEditor::OnContextMenuEntry)
647 EVT_MENU(idBookmarksToggle, cbEditor::OnContextMenuEntry)
648 EVT_MENU(idBookmarksPrevious, cbEditor::OnContextMenuEntry)
649 EVT_MENU(idBookmarksNext, cbEditor::OnContextMenuEntry)
650 EVT_MENU(idBookmarksClearAll, cbEditor::OnContextMenuEntry)
651 EVT_MENU(idFoldingFoldAll, cbEditor::OnContextMenuEntry)
652 EVT_MENU(idFoldingUnfoldAll, cbEditor::OnContextMenuEntry)
653 EVT_MENU(idFoldingToggleAll, cbEditor::OnContextMenuEntry)
654 EVT_MENU(idFoldingFoldCurrent, cbEditor::OnContextMenuEntry)
655 EVT_MENU(idFoldingUnfoldCurrent, cbEditor::OnContextMenuEntry)
656 EVT_MENU(idFoldingToggleCurrent, cbEditor::OnContextMenuEntry)
657 EVT_MENU(idProperties, cbEditor::OnContextMenuEntry)
658 EVT_MENU(idAddFileToProject, cbEditor::OnContextMenuEntry)
659 EVT_MENU(idRemoveFileFromProject, cbEditor::OnContextMenuEntry)
660 EVT_MENU(idShowFileInProject, cbEditor::OnContextMenuEntry)
661 EVT_MENU(idBookmarkAdd, cbEditor::OnContextMenuEntry)
662 EVT_MENU(idBookmarkRemove, cbEditor::OnContextMenuEntry)
663 EVT_MENU(idBookmarkRemoveAll, cbEditor::OnContextMenuEntry)
664 EVT_MENU(idBreakpointAdd, cbEditor::OnContextMenuEntry)
665 EVT_MENU(idBreakpointEdit, cbEditor::OnContextMenuEntry)
666 EVT_MENU(idBreakpointRemove, cbEditor::OnContextMenuEntry)
667 EVT_MENU(idBreakpointEnable, cbEditor::OnContextMenuEntry)
668 EVT_MENU(idBreakpointDisable, cbEditor::OnContextMenuEntry)
669 EVT_MENU(idSplitHorz, cbEditor::OnContextMenuEntry)
670 EVT_MENU(idSplitVert, cbEditor::OnContextMenuEntry)
671 EVT_MENU(idUnsplit, cbEditor::OnContextMenuEntry)
672 EVT_MENU(idOpenUrl, cbEditor::OnContextMenuEntry)
673
674 EVT_SCI_ZOOM(-1, cbEditor::OnZoom)
675 EVT_SCI_ZOOM(-1, cbEditor::OnZoom)
676
677 END_EVENT_TABLE()
678
679 // Count lines of EOL style in the opened file
680 static void CountLineEnds(cbStyledTextCtrl* control, int &linesCR, int &linesLF, int &linesCRLF)
681 {
682 linesCR = 0;
683 linesLF = 0;
684 linesCRLF = 0;
685
686 int lengthDoc = control->GetLength();
687 const int maxLengthDoc = 1000000;
688 char chPrev = ' ';
689 char chNext = control->GetCharAt(0);
690 for (int i = 0; i < lengthDoc; i++)
691 {
692 char ch = chNext;
693 chNext = control->GetCharAt(i + 1);
694 if (ch == '\r')
695 {
696 if (chNext == '\n')
697 linesCRLF++;
698 else
699 linesCR++;
700 }
701 else if (ch == '\n')
702 {
703 if (chPrev != '\r')
704 linesLF++;
705 }
706 else if (i > maxLengthDoc) // stop the loop if the file contains too many characters
707 return;
708
709 chPrev = ch;
710 }
711 }
712
713 // Detect the EOL mode of the control. If a file has mixed EOLs, we will using the voting
714 // logic, and give user a InfoWindow notification.
DetectLineEnds(cbStyledTextCtrl * control)715 static int DetectLineEnds(cbStyledTextCtrl* control)
716 {
717 int eolMode;
718 wxString eolModeStr;
719 // initial EOL mode depend on OS
720 if (platform::windows)
721 {
722 eolMode = wxSCI_EOL_CRLF;
723 eolModeStr = _T("\"CR-LF\"");
724 }
725 else
726 {
727 eolMode = wxSCI_EOL_LF;
728 eolModeStr = _T("\"LF\"");
729 }
730
731 int linesCR;
732 int linesLF;
733 int linesCRLF;
734 // count lines of each EOL style
735 CountLineEnds(control, linesCR, linesLF, linesCRLF);
736
737 // voting logic
738 // if the file does not contain any line-feed or the most largest counts are equal( e.g.: linesLF=5,
739 // linesCRLF=5, linesCR=0 ), then we will use the initial EOL mode
740 if ( (linesLF > linesCR) && (linesLF > linesCRLF) )
741 {
742 eolMode = wxSCI_EOL_LF;
743 eolModeStr = _T("\"LF\"");
744 }
745 else if ( (linesCR > linesLF) && (linesCR > linesCRLF) )
746 {
747 eolMode = wxSCI_EOL_CR;
748 eolModeStr = _T("\"CR\"");
749 }
750 else if ( (linesCRLF > linesLF) && (linesCRLF > linesCR))
751 {
752 eolMode = wxSCI_EOL_CRLF;
753 eolModeStr = _T("\"CR-LF\"");
754 }
755
756 unsigned int delay = 2000;
757 if ( ( (linesCR>0) && (linesCRLF>0) )
758 || ( (linesLF>0) && (linesCRLF>0) )
759 || ( (linesCR>0) && (linesLF>0) ) )
760 {
761 //In mixed EOL file, give the user a beep and InfoWindow notification.
762 wxBell();
763 InfoWindow::Display(_("Mixed Line Endings"), _("Mixed line endings found, setting mode ") + eolModeStr, delay);
764 }
765 return eolMode;
766 }
767
768 // class constructor
cbEditor(wxWindow * parent,const wxString & filename,EditorColourSet * theme)769 cbEditor::cbEditor(wxWindow* parent, const wxString& filename, EditorColourSet* theme)
770 : EditorBase(parent, filename),
771 m_pSplitter(nullptr),
772 m_pSizer(nullptr),
773 m_pControl(nullptr),
774 m_pControl2(nullptr),
775 m_foldBackup(nullptr),
776 m_SplitType(stNoSplit),
777 m_Modified(false),
778 m_Index(-1),
779 m_pProjectFile(nullptr),
780 m_pTheme(theme),
781 m_lang(HL_AUTO)
782 {
783 DoInitializations(filename);
784 }
785
786 // class constructor
cbEditor(wxWindow * parent,LoaderBase * fileLdr,const wxString & filename,EditorColourSet * theme)787 cbEditor::cbEditor(wxWindow* parent, LoaderBase* fileLdr, const wxString& filename, EditorColourSet* theme)
788 : EditorBase(parent, filename),
789 m_pSplitter(nullptr),
790 m_pSizer(nullptr),
791 m_pControl(nullptr),
792 m_pControl2(nullptr),
793 m_foldBackup(nullptr),
794 m_SplitType(stNoSplit),
795 m_Modified(false),
796 m_Index(-1),
797 m_pProjectFile(nullptr),
798 m_pTheme(theme),
799 m_lang(HL_AUTO)
800 {
801 DoInitializations(filename, fileLdr);
802 }
803
804 // class destructor
~cbEditor()805 cbEditor::~cbEditor()
806 {
807 SetSizer(nullptr);
808
809 // moved in ~EditorBase
810 // NotifyPlugins(cbEVT_EDITOR_CLOSE, 0, m_Filename);
811
812 UpdateProjectFile();
813 if (m_pControl)
814 {
815 if (m_pProjectFile)
816 m_pProjectFile->editorOpen = false;
817 m_pControl->Destroy();
818 m_pControl = nullptr;
819 }
820 DestroySplitView();
821
822 delete m_pData;
823 }
824
DoInitializations(const wxString & filename,LoaderBase * fileLdr)825 void cbEditor::DoInitializations(const wxString& filename, LoaderBase* fileLdr)
826 {
827 // first thing to do!
828 // if we add more constructors in the future, don't forget to set this!
829 m_pData = new cbEditorInternalData(this);//, fileLdr);
830 m_pData->m_pFileLoader = fileLdr;
831 m_IsBuiltinEditor = true;
832
833 if (!filename.IsEmpty())
834 {
835 InitFilename(filename);
836 wxFileName fname(m_Filename);
837 NormalizePath(fname, wxEmptyString);
838 m_Filename = fname.GetFullPath();
839 }
840 else
841 {
842 static int untitledCounter = 1;
843 wxString f;
844 cbProject* prj = Manager::Get()->GetProjectManager()->GetActiveProject();
845 if (prj)
846 f.Printf(_("%sUntitled%d"), prj->GetBasePath().c_str(), untitledCounter++);
847 else
848 f.Printf(_("Untitled%d"), untitledCounter++);
849
850 InitFilename(f);
851 }
852 // Manager::Get()->GetLogManager()->DebugLog(_T("ctor: Filename=%s\nShort=%s"), m_Filename.c_str(), m_Shortname.c_str());
853
854 // initialize left control (unsplit state)
855 m_pSizer = new wxBoxSizer(wxVERTICAL);
856 m_pControl = CreateEditor();
857 m_pSizer->Add(m_pControl, 1, wxEXPAND);
858 SetSizer(m_pSizer);
859
860 // the following two lines make the editors behave strangely in linux:
861 // when resizing other docked windows, the editors do NOT resize too
862 // and they stame the same size...
863 // if commenting the following two lines causes problems in other platforms,
864 // simply put an "#ifdef __WXGTK__" guard around and uncomment them.
865 // m_pSizer->Fit(this);
866 // m_pSizer->SetSizeHints(this);
867
868 m_pSizer->SetItemMinSize(m_pControl, 32, 32);
869
870 // by default we show no markers, marginMasks are set explicitly in "InternalSetEditorStyleBeforeFileOpen()"
871 // and/or by plugins, that use markers, like browsemarks-plugin
872 m_pControl->SetMarginMask(C_LINE_MARGIN, 0);
873 m_pControl->SetMarginMask(C_MARKER_MARGIN, 0);
874 m_pControl->SetMarginMask(C_CHANGEBAR_MARGIN, 0);
875 m_pControl->SetMarginMask(C_FOLDING_MARGIN, 0);
876
877 SetEditorStyleBeforeFileOpen();
878 m_IsOK = Open();
879 SetEditorStyleAfterFileOpen();
880 if (Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/folding/fold_all_on_open"), false))
881 FoldAll();
882
883 // if !m_IsOK then it's a new file, so set the modified flag ON
884 if (!m_IsOK || filename.IsEmpty())
885 {
886 SetModified(true);
887 m_IsOK = false;
888 }
889 ConnectEvents(m_pControl);
890 }
891
NotifyPlugins(wxEventType type,int intArg,const wxString & strArg,int xArg,int yArg)892 void cbEditor::NotifyPlugins(wxEventType type, int intArg, const wxString& strArg, int xArg, int yArg)
893 {
894 if (!Manager::Get()->GetPluginManager())
895 return; // no plugin manager! app shutting down?
896 CodeBlocksEvent event(type);
897 event.SetEditor(this);
898 event.SetInt(intArg);
899 event.SetString(strArg);
900 event.SetX(xArg);
901 event.SetY(yArg);
902 if (m_pProjectFile)
903 event.SetProject(m_pProjectFile->GetParentProject());
904 //wxPostEvent(Manager::Get()->GetAppWindow(), event);
905 Manager::Get()->GetPluginManager()->NotifyPlugins(event);
906 }
907
DestroySplitView()908 void cbEditor::DestroySplitView()
909 {
910 if (m_pControl2)
911 {
912 m_pControl2->Destroy();
913 m_pControl2 = nullptr;
914 }
915 if (m_pSplitter)
916 {
917 m_pSplitter->Destroy();
918 m_pSplitter = nullptr;
919 }
920 }
921
GetControl() const922 cbStyledTextCtrl* cbEditor::GetControl() const
923 {
924 // return the last focused control (left or right)
925 if (m_pControl2)
926 {
927 // every time a control gets the focus it stores the actual timestamp, the timestamp defaults to 0 so the
928 // greater is the timestamp of the control that had the focus last time
929 // finding the focused window does not work if another control has the keyboard-focus
930 if ( m_pControl2->GetLastFocusTime() > m_pControl->GetLastFocusTime() )
931 return m_pControl2;
932 }
933 return m_pControl;
934 }
935
GetModified() const936 bool cbEditor::GetModified() const
937 {
938 return m_Modified || m_pControl->GetModify();
939 }
940
SetModified(bool modified)941 void cbEditor::SetModified(bool modified)
942 {
943 if (modified != m_Modified)
944 {
945 m_Modified = modified;
946 if (!m_Modified)
947 m_pControl->SetSavePoint();
948
949 SetEditorTitle(m_Shortname);
950 NotifyPlugins(cbEVT_EDITOR_MODIFIED);
951 // visual state
952 if (m_pProjectFile)
953 m_pProjectFile->SetFileState(m_pControl->GetReadOnly() ? fvsReadOnly : (m_Modified ? fvsModified : fvsNormal));
954 }
955 }
956
SetEditorTitle(const wxString & title)957 void cbEditor::SetEditorTitle(const wxString& title)
958 {
959 if (m_Modified)
960 SetTitle(g_EditorModified + title);
961 else
962 SetTitle(title);
963 }
964
SetProjectFile(ProjectFile * project_file,bool preserve_modified)965 void cbEditor::SetProjectFile(ProjectFile* project_file, bool preserve_modified)
966 {
967 if (m_pProjectFile == project_file)
968 return; // we 've been here before ;)
969
970 bool wasmodified = false;
971 if (preserve_modified)
972 wasmodified = GetModified();
973
974 m_pProjectFile = project_file;
975 if (m_pProjectFile)
976 {
977 // update our filename
978 m_Filename = UnixFilename(project_file->file.GetFullPath());
979
980 m_pControl->GotoPos(m_pProjectFile->editorPos);
981 m_pControl->ScrollToLine(m_pProjectFile->editorTopLine);
982 m_pControl->ScrollToColumn(0);
983 m_pControl->SetZoom(m_pProjectFile->editorZoom);
984 if (m_pProjectFile->editorSplit != (int)stNoSplit)
985 {
986 Split((SplitType)m_pProjectFile->editorSplit);
987 if (m_pControl2)
988 {
989 m_pSplitter->SetSashPosition(m_pProjectFile->editorSplitPos);
990 m_pControl2->GotoPos(m_pProjectFile->editorPos_2);
991 m_pControl2->ScrollToLine(m_pProjectFile->editorTopLine_2);
992 m_pControl2->ScrollToColumn(0);
993 m_pControl2->SetZoom(m_pProjectFile->editorZoom_2);
994 }
995 }
996
997 m_pData->SetLineNumberColWidth();
998
999 if ( Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/folding/show_folds"), true) )
1000 {
1001 for (unsigned int i = 0; i < m_pProjectFile->editorFoldLinesArray.GetCount(); i++)
1002 m_pControl->ToggleFold(m_pProjectFile->editorFoldLinesArray[i]);
1003 }
1004
1005 m_pProjectFile->editorOpen = true;
1006
1007 if (Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/tab_text_relative"), true))
1008 m_Shortname = m_pProjectFile->relativeToCommonTopLevelPath;
1009 else
1010 m_Shortname = m_pProjectFile->file.GetFullName();
1011 SetEditorTitle(m_Shortname);
1012
1013 if (!wxFileExists(m_Filename))
1014 m_pProjectFile->SetFileState(fvsMissing);
1015 else if (!wxFile::Access(m_Filename.c_str(), wxFile::write)) // readonly
1016 m_pProjectFile->SetFileState(fvsReadOnly);
1017 }
1018
1019 #if 0
1020 wxString dbg;
1021 dbg << _T("[ed] Filename: ") << GetFilename() << _T('\n');
1022 dbg << _T("[ed] Short name: ") << GetShortName() << _T('\n');
1023 dbg << _T("[ed] Modified: ") << GetModified() << _T('\n');
1024 dbg << _T("[ed] Project: ") << ((m_pProjectFile && m_pProjectFile->project) ? m_pProjectFile->project->GetTitle() : _T("unknown")) << _T('\n');
1025 dbg << _T("[ed] Project file: ") << (m_pProjectFile ? m_pProjectFile->relativeFilename : _T("unknown")) << _T('\n');
1026 Manager::Get()->GetLogManager()->DebugLog(dbg);
1027 #endif
1028 if (preserve_modified)
1029 SetModified(wasmodified);
1030 }
1031
UpdateProjectFile()1032 void cbEditor::UpdateProjectFile()
1033 {
1034 if (m_pControl && m_pProjectFile)
1035 {
1036 m_pProjectFile->editorOpen = true;
1037 m_pProjectFile->editorSplit = m_SplitType;
1038 m_pProjectFile->editorPos = m_pControl->GetCurrentPos();
1039 m_pProjectFile->editorTopLine = m_pControl->GetFirstVisibleLine();
1040 m_pProjectFile->editorZoom = m_pControl->GetZoom();
1041 m_pProjectFile->editorSplitActive = 1;
1042 if (m_pControl2)
1043 {
1044 m_pProjectFile->editorSplitPos = m_pSplitter->GetSashPosition();
1045 m_pProjectFile->editorPos_2 = m_pControl2->GetCurrentPos();
1046 m_pProjectFile->editorTopLine_2 = m_pControl2->GetFirstVisibleLine();
1047 m_pProjectFile->editorZoom_2 = m_pControl2->GetZoom();
1048 if (GetControl()==m_pControl2)
1049 m_pProjectFile->editorSplitActive = 2;
1050 }
1051
1052 if (m_pProjectFile->editorFoldLinesArray.GetCount() != 0)
1053 m_pProjectFile->editorFoldLinesArray.Clear();
1054
1055 int i = 0;
1056 while ((i = m_pControl->ContractedFoldNext(i)) != -1)
1057 m_pProjectFile->editorFoldLinesArray.Add(i++);
1058 }
1059 }
1060
SetMarkerStyle(int marker,int markerType,wxColor fore,wxColor back)1061 void cbEditor::SetMarkerStyle(int marker, int markerType, wxColor fore, wxColor back)
1062 {
1063 m_pControl->MarkerDefine(marker, markerType);
1064 m_pControl->MarkerSetForeground(marker, fore);
1065 m_pControl->MarkerSetBackground(marker, back);
1066
1067 if (m_pControl2)
1068 {
1069 m_pControl2->MarkerDefine(marker, markerType);
1070 m_pControl2->MarkerSetForeground(marker, fore);
1071 m_pControl2->MarkerSetBackground(marker, back);
1072 }
1073 }
1074
UnderlineFoldedLines(bool underline)1075 void cbEditor::UnderlineFoldedLines(bool underline)
1076 {
1077 m_pControl->SetFoldFlags(underline ? 16 : 0);
1078 if (m_pControl2)
1079 m_pControl2->SetFoldFlags(underline ? 16 : 0);
1080 }
1081
CreateEditor()1082 cbStyledTextCtrl* cbEditor::CreateEditor()
1083 {
1084 // avoid gtk-critical because of sizes less than -1 (can happen with wxAuiNotebook/cbAuiNotebook)
1085 wxSize size = m_pControl ? wxDefaultSize : GetSize();
1086 size.x = std::max(size.x, -1);
1087 size.y = std::max(size.y, -1);
1088
1089 cbStyledTextCtrl* control = new cbStyledTextCtrl(this, wxNewId(), wxDefaultPosition, size);
1090 control->UsePopUp(false);
1091
1092 ConfigManager *config = Manager::Get()->GetConfigManager(_T("editor"));
1093 wxString encodingName = config->Read(_T("/default_encoding"), wxLocale::GetSystemEncodingName());
1094 m_pData->m_encoding = wxFontMapper::GetEncodingFromName(encodingName);
1095 if (m_pData->m_encoding == wxFONTENCODING_MAX && encodingName == wxT("default"))
1096 m_pData->m_encoding = wxFont::GetDefaultEncoding();
1097
1098 for (int marker = 0 ; marker <= wxSCI_MARKNUM_LASTUNUSED ; ++marker)
1099 control->MarkerDefine(marker, wxSCI_MARK_EMPTY);
1100
1101 return control;
1102 }
1103
ConnectEvents(cbStyledTextCtrl * stc)1104 void cbEditor::ConnectEvents(cbStyledTextCtrl* stc)
1105 {
1106 wxWindowID stcID = stc->GetId();
1107 // dynamic events
1108 Connect( stcID, wxEVT_SCI_MARGINCLICK, wxScintillaEventHandler(cbEditor::OnMarginClick) );
1109 Connect( stcID, wxEVT_SCI_UPDATEUI, wxScintillaEventHandler(cbEditor::OnEditorUpdateUI) );
1110 Connect( stcID, wxEVT_SCI_CHANGE, wxScintillaEventHandler(cbEditor::OnEditorChange) );
1111 Connect( stcID, wxEVT_SCI_CHARADDED, wxScintillaEventHandler(cbEditor::OnEditorCharAdded) );
1112 Connect( stcID, wxEVT_SCI_DWELLSTART, wxScintillaEventHandler(cbEditor::OnEditorDwellStart) );
1113 Connect( stcID, wxEVT_SCI_DWELLEND, wxScintillaEventHandler(cbEditor::OnEditorDwellEnd) );
1114 Connect( stcID, wxEVT_SCI_USERLISTSELECTION, wxScintillaEventHandler(cbEditor::OnUserListSelection) );
1115 Connect( stcID, wxEVT_SCI_MODIFIED, wxScintillaEventHandler(cbEditor::OnEditorModified) );
1116
1117 // Now bind all *other* scintilla events to a common function so that editor hooks
1118 // can be informed for them too.
1119 // If you implement one of these events using a different function, do the following:
1120 // * comment it out here,
1121 // * "connect" it in the above block
1122 // * and make sure you call OnScintillaEvent() from your new handler function
1123 // This will make sure that all editor hooks will be called when needed.
1124 int scintilla_events[] =
1125 {
1126 // wxEVT_SCI_CHANGE,
1127 wxEVT_SCI_STYLENEEDED,
1128 // wxEVT_SCI_CHARADDED,
1129 wxEVT_SCI_SAVEPOINTREACHED,
1130 wxEVT_SCI_SAVEPOINTLEFT,
1131 wxEVT_SCI_ROMODIFYATTEMPT,
1132 wxEVT_SCI_DOUBLECLICK,
1133 // wxEVT_SCI_UPDATEUI,
1134 // wxEVT_SCI_MODIFIED,
1135 wxEVT_SCI_MACRORECORD,
1136 // wxEVT_SCI_MARGINCLICK,
1137 wxEVT_SCI_NEEDSHOWN,
1138 wxEVT_SCI_PAINTED,
1139 // wxEVT_SCI_USERLISTSELECTION,
1140 // wxEVT_SCI_DWELLSTART,
1141 // wxEVT_SCI_DWELLEND,
1142 wxEVT_SCI_START_DRAG,
1143 wxEVT_SCI_DRAG_OVER,
1144 wxEVT_SCI_DO_DROP,
1145 wxEVT_SCI_ZOOM,
1146 wxEVT_SCI_HOTSPOT_CLICK,
1147 wxEVT_SCI_HOTSPOT_DCLICK,
1148 wxEVT_SCI_CALLTIP_CLICK,
1149 wxEVT_SCI_AUTOCOMP_SELECTION,
1150 // wxEVT_SCI_INDICATOR_CLICK,
1151 // wxEVT_SCI_INDICATOR_RELEASE,
1152 wxEVT_SCI_AUTOCOMP_CANCELLED,
1153
1154 -1 // to help enumeration of this array
1155 };
1156 int i = 0;
1157 while (scintilla_events[i] != -1)
1158 {
1159 Connect( stcID, scintilla_events[i], wxScintillaEventHandler(cbEditor::OnScintillaEvent) );
1160 ++i;
1161 }
1162 }
1163
Split(cbEditor::SplitType split)1164 void cbEditor::Split(cbEditor::SplitType split)
1165 {
1166 Freeze();
1167
1168 // unsplit first, if needed
1169 if (m_pSplitter)
1170 {
1171 Unsplit();
1172 Manager::Yield();
1173 }
1174 m_SplitType = split;
1175 if (m_SplitType == stNoSplit)
1176 {
1177 Thaw();
1178 return;
1179 }
1180
1181 // remove the left control from the sizer
1182 m_pSizer->Detach(m_pControl);
1183
1184 // create the splitter window
1185 m_pSplitter = new wxSplitterWindow(this, wxNewId(), wxDefaultPosition, wxDefaultSize, wxSP_NOBORDER | wxSP_LIVE_UPDATE);
1186 m_pSplitter->SetMinimumPaneSize(32);
1187
1188 // create the right control
1189 m_pControl2 = CreateEditor();
1190
1191 // update controls' look'n'feel
1192 // do it here (before) document is attached, speeds up syntaxhighlighting
1193 // we do not call "SetEditorStyleAfterFileOpen" here because it calls SetLanguage for the already loaded text inside
1194 // the left control and slows down loading of large files a lot.
1195 InternalSetEditorStyleBeforeFileOpen(m_pControl2);
1196
1197 // make sure basic settings of indicators (maybe set by plugins) are used for the new control
1198 for (int i = 0; i < wxSCI_INDIC_MAX; ++i )
1199 {
1200 m_pControl2->IndicatorSetStyle(i, m_pControl->IndicatorGetStyle(i));
1201 m_pControl2->IndicatorSetUnder(i, m_pControl->IndicatorGetUnder(i));
1202 m_pControl2->IndicatorSetForeground(i, m_pControl->IndicatorGetForeground(i));
1203 }
1204
1205 ConfigManager* mgr = Manager::Get()->GetConfigManager(_T("editor"));
1206 SetFoldingIndicator(mgr->ReadInt(_T("/folding/indicator"), 2));
1207 UnderlineFoldedLines(mgr->ReadBool(_T("/folding/underline_folded_line"), true));
1208
1209 if (m_pTheme)
1210 {
1211 m_pTheme->Apply(m_lang, m_pControl2, false, true);
1212 SetLanguageDependentColours(*m_pControl2);
1213 }
1214
1215 // and make it a live copy of left control
1216 m_pControl2->SetDocPointer(m_pControl->GetDocPointer());
1217
1218 // on wxGTK > 2.9 we need to thaw before reparent and refreeze the editor here or the whole app stays frozen
1219 #if defined ( __WXGTK__ ) && wxCHECK_VERSION(3, 0, 0)
1220 Thaw();
1221 #endif
1222 // parent both controls under the splitter
1223 m_pControl->Reparent(m_pSplitter);
1224 m_pControl2->Reparent(m_pSplitter);
1225 #if defined ( __WXGTK__ ) && wxCHECK_VERSION(3, 0, 0)
1226 Freeze();
1227 #endif
1228
1229 // add the splitter in the sizer
1230 m_pSizer->SetDimension(0, 0, GetSize().x, GetSize().y);
1231 m_pSizer->Add(m_pSplitter, 1, wxEXPAND);
1232
1233 m_pSizer->Layout();
1234
1235 // split as needed
1236 switch (m_SplitType)
1237 {
1238 case stHorizontal:
1239 m_pSplitter->SplitHorizontally(m_pControl, m_pControl2, 0);
1240 break;
1241
1242 case stVertical:
1243 m_pSplitter->SplitVertically(m_pControl, m_pControl2, 0);
1244 break;
1245
1246 case stNoSplit: // fall-trough
1247 default:
1248 break;
1249 }
1250
1251 SetEditorStyleAfterFileOpen();
1252
1253 // initial zoom is same as left/top control
1254 m_pControl2->SetZoom(m_pControl->GetZoom());
1255 // make sure the line numbers margin is correct for the new control
1256 m_pControl2->SetMarginWidth(C_LINE_MARGIN, m_pControl->GetMarginWidth(C_LINE_MARGIN));
1257
1258 ConnectEvents(m_pControl2);
1259
1260 NotifyPlugins(cbEVT_EDITOR_SPLIT);
1261
1262 Thaw();
1263 }
1264
Unsplit()1265 void cbEditor::Unsplit()
1266 {
1267 m_SplitType = stNoSplit;
1268 if (!m_pSplitter)
1269 return;
1270
1271 Freeze();
1272
1273 // if "unsplit" requested on right control, swap left-right first
1274 if (GetControl() == m_pControl2)
1275 {
1276 cbStyledTextCtrl* tmp = m_pControl;
1277 m_pControl = m_pControl2;
1278 m_pControl2 = tmp;
1279 }
1280
1281 // remove the splitter from the sizer
1282 m_pSizer->Detach(m_pSplitter);
1283
1284 // on wxGTK > 2.9 we need to thaw before reparent and refreeze the editor here or the whole app stays frozen
1285 #if defined ( __WXGTK__ ) && wxCHECK_VERSION(3, 0, 0)
1286 Thaw();
1287 #endif
1288 // parent the left control under this
1289 m_pControl->Reparent(this);
1290 #if defined ( __WXGTK__ ) && wxCHECK_VERSION(3, 0, 0)
1291 Freeze();
1292 #endif
1293 // add it in the sizer
1294 m_pSizer->Add(m_pControl, 1, wxEXPAND);
1295 // notify the plugin when the right splitter window is not destroyed and the left window is reparented to cbEditor
1296 NotifyPlugins(cbEVT_EDITOR_UNSPLIT);
1297 // destroy the splitter and right control
1298 DestroySplitView();
1299 // and layout
1300 m_pSizer->Layout();
1301
1302 Thaw();
1303 }
1304
SetLanguageDependentColours(cbStyledTextCtrl & control)1305 void cbEditor::SetLanguageDependentColours(cbStyledTextCtrl &control)
1306 {
1307 control.MarkerSetBackground(DEBUG_MARKER_HIGHLIGHT, control.GetCaretLineBackground());
1308 }
1309
SetEditorStyle()1310 void cbEditor::SetEditorStyle()
1311 {
1312 SetEditorStyleBeforeFileOpen();
1313 SetEditorStyleAfterFileOpen();
1314 }
1315
OverrideUseTabsPerLanguage(cbStyledTextCtrl * control)1316 inline void OverrideUseTabsPerLanguage(cbStyledTextCtrl *control)
1317 {
1318 if (!control)
1319 return;
1320 // Override the use tab setting for languages which have explicit requirements about tab/space
1321 // usage.
1322 int lexer = control->GetLexer();
1323 switch (lexer)
1324 {
1325 case wxSCI_LEX_PYTHON:
1326 case wxSCI_LEX_YAML:
1327 control->SetUseTabs(false);
1328 break;
1329 case wxSCI_LEX_MAKEFILE:
1330 control->SetUseTabs(true);
1331 break;
1332 default:
1333 break;
1334 }
1335 }
1336
SetEditorTechnology(cbStyledTextCtrl * control,ConfigManager * config)1337 static void SetEditorTechnology(cbStyledTextCtrl *control, ConfigManager *config)
1338 {
1339 if (!control)
1340 return;
1341 #if defined(__WXMSW__) && wxCHECK_VERSION(3, 1, 0)
1342 const int technology = config->ReadInt(wxT("/technology"), 0);
1343 if (technology == 1)
1344 control->SetTechnology(wxSCI_TECHNOLOGY_DIRECTWRITE);
1345 else
1346 control->SetTechnology(wxSCI_TECHNOLOGY_DEFAULT);
1347
1348 const int fontQuality = config->ReadInt(wxT("/font_quality"), 0);
1349 switch (fontQuality)
1350 {
1351 default:
1352 case 0:
1353 control->SetFontQuality(wxSCI_EFF_QUALITY_DEFAULT);
1354 break;
1355 case 1:
1356 control->SetFontQuality(wxSCI_EFF_QUALITY_NON_ANTIALIASED);
1357 break;
1358 case 2:
1359 control->SetFontQuality(wxSCI_EFF_QUALITY_ANTIALIASED);
1360 break;
1361 case 3:
1362 control->SetFontQuality(wxSCI_EFF_QUALITY_LCD_OPTIMIZED);
1363 break;
1364 }
1365 #endif // defined(__WXMSW__) && wxCHECK_VERSION(3, 1, 0)
1366 }
1367
SetEditorStyleBeforeFileOpen()1368 void cbEditor::SetEditorStyleBeforeFileOpen()
1369 {
1370 ConfigManager* mgr = Manager::Get()->GetConfigManager(_T("editor"));
1371
1372 // update the tab text based on preferences
1373 if (m_pProjectFile)
1374 {
1375 if (mgr->ReadBool(_T("/tab_text_relative"), true))
1376 m_Shortname = m_pProjectFile->relativeToCommonTopLevelPath;
1377 else
1378 m_Shortname = m_pProjectFile->file.GetFullName();
1379 SetEditorTitle(m_Shortname);
1380 }
1381
1382 // Folding properties.
1383 m_pData->mFoldingLimit = mgr->ReadBool(_T("/folding/limit"), false);
1384 m_pData->mFoldingLimitLevel = mgr->ReadInt(_T("/folding/limit_level"), 1);
1385
1386 // EOL properties
1387 m_pData->m_strip_trailing_spaces = mgr->ReadBool(_T("/eol/strip_trailing_spaces"), true);
1388 m_pData->m_ensure_final_line_end = mgr->ReadBool(_T("/eol/ensure_final_line_end"), true);
1389 m_pData->m_ensure_consistent_line_ends = mgr->ReadBool(_T("/eol/ensure_consistent_line_ends"), false);
1390
1391 InternalSetEditorStyleBeforeFileOpen(m_pControl);
1392
1393 if (m_pControl2)
1394 InternalSetEditorStyleBeforeFileOpen(m_pControl2);
1395
1396 SetFoldingIndicator(mgr->ReadInt(_T("/folding/indicator"), 2));
1397
1398 SetLanguage(HL_AUTO, false);
1399
1400 OverrideUseTabsPerLanguage(m_pControl);
1401 OverrideUseTabsPerLanguage(m_pControl2);
1402
1403 SetEditorTechnology(m_pControl, mgr);
1404 SetEditorTechnology(m_pControl2, mgr);
1405 }
1406
SetEditorStyleAfterFileOpen()1407 void cbEditor::SetEditorStyleAfterFileOpen()
1408 {
1409 InternalSetEditorStyleAfterFileOpen(m_pControl);
1410 if (m_pControl2)
1411 InternalSetEditorStyleAfterFileOpen(m_pControl2);
1412
1413 ConfigManager* mgr = Manager::Get()->GetConfigManager(_T("editor"));
1414
1415 UnderlineFoldedLines(mgr->ReadBool(_T("/folding/underline_folded_line"), true));
1416
1417 // line numbers
1418 m_pData->SetLineNumberColWidth();
1419 }
1420
1421 // static
1422 // public version of InternalSetEditorStyleBeforeFileOpen
ApplyStyles(cbStyledTextCtrl * control)1423 void cbEditor::ApplyStyles(cbStyledTextCtrl* control)
1424 {
1425 if (!control)
1426 return;
1427
1428 InternalSetEditorStyleBeforeFileOpen(control);
1429 InternalSetEditorStyleAfterFileOpen(control);
1430 }
1431
1432 // static
InternalSetEditorStyleBeforeFileOpen(cbStyledTextCtrl * control)1433 void cbEditor::InternalSetEditorStyleBeforeFileOpen(cbStyledTextCtrl* control)
1434 {
1435 if (!control)
1436 return;
1437
1438 ConfigManager* mgr = Manager::Get()->GetConfigManager(_T("editor"));
1439
1440 // setting the default editor font size to 10 point
1441 wxFont font(10, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1442
1443 wxString fontstring = mgr->Read(_T("/font"), wxEmptyString);
1444
1445 if (!fontstring.IsEmpty())
1446 {
1447 wxNativeFontInfo nfi;
1448 nfi.FromString(fontstring);
1449 font.SetNativeFontInfo(nfi);
1450 }
1451
1452 control->SetMouseDwellTime(1000);
1453
1454 int caretStyle = mgr->ReadInt(_T("/caret/style"), wxSCI_CARETSTYLE_LINE);
1455 control->SetCaretStyle(caretStyle);
1456 if (caretStyle == wxSCI_CARETSTYLE_LINE)
1457 control->SetCaretWidth(mgr->ReadInt(_T("/caret/width"), 1));
1458 else
1459 control->SetCaretWidth(1);
1460
1461 ColourManager *colours = Manager::Get()->GetColourManager();
1462
1463 control->SetCaretForeground(colours->GetColour(wxT("editor_caret")));
1464 control->SetCaretPeriod(mgr->ReadInt(_T("/caret/period"), 500));
1465 control->SetCaretLineVisible(mgr->ReadBool(_T("/highlight_caret_line"), false));
1466 control->SetFoldMarginColour(true, colours->GetColour(wxT("editor_margin_chrome")));
1467 control->SetFoldMarginHiColour(true, colours->GetColour(wxT("editor_margin_chrome_highlight")));
1468
1469 control->SetWhitespaceForeground(true, colours->GetColour(wxT("editor_whitespace")));
1470
1471 // setup for "CamelCase selection"
1472 if (mgr->ReadBool(_T("/camel_case"), false))
1473 {
1474 // consider CamelCase for both: cursor movement with CTRL and selection with CTRL+SHIFT:
1475 control->CmdKeyAssign(wxSCI_KEY_LEFT, wxSCI_KEYMOD_CTRL, wxSCI_CMD_WORDPARTLEFT);
1476 control->CmdKeyAssign(wxSCI_KEY_RIGHT, wxSCI_KEYMOD_CTRL, wxSCI_CMD_WORDPARTRIGHT);
1477 control->CmdKeyAssign(wxSCI_KEY_LEFT, wxSCI_KEYMOD_CTRL|wxSCI_KEYMOD_SHIFT, wxSCI_CMD_WORDPARTLEFTEXTEND);
1478 control->CmdKeyAssign(wxSCI_KEY_RIGHT, wxSCI_KEYMOD_CTRL|wxSCI_KEYMOD_SHIFT, wxSCI_CMD_WORDPARTRIGHTEXTEND);
1479 }
1480 else // else set default "none CamelCase" key behavior (also default scintilla behaviour, see scintilla docs)
1481 {
1482 control->CmdKeyAssign(wxSCI_KEY_LEFT, wxSCI_KEYMOD_CTRL, wxSCI_CMD_WORDLEFT);
1483 control->CmdKeyAssign(wxSCI_KEY_RIGHT, wxSCI_KEYMOD_CTRL, wxSCI_CMD_WORDRIGHT);
1484 control->CmdKeyAssign(wxSCI_KEY_LEFT, wxSCI_KEYMOD_CTRL|wxSCI_KEYMOD_SHIFT, wxSCI_CMD_WORDLEFTEXTEND);
1485 control->CmdKeyAssign(wxSCI_KEY_RIGHT, wxSCI_KEYMOD_CTRL|wxSCI_KEYMOD_SHIFT, wxSCI_CMD_WORDRIGHTEXTEND);
1486 }
1487
1488 control->SetUseTabs(mgr->ReadBool(_T("/use_tab"), false));
1489 control->SetIndentationGuides(mgr->ReadBool(_T("/show_indent_guides"), false)?wxSCI_IV_LOOKBOTH:wxSCI_IV_NONE);
1490 control->SetTabIndents(mgr->ReadBool(_T("/tab_indents"), true));
1491 control->SetBackSpaceUnIndents(mgr->ReadBool(_T("/backspace_unindents"), true));
1492 control->SetWrapMode(mgr->ReadBool(_T("/word_wrap"), false));
1493 if (mgr->ReadBool(_T("/word_wrap_style_home_end"), true))
1494 {
1495 // in word wrap mode, home/end keys goto the wrap point if not already there,
1496 // otherwise to the start/end of the entire line.
1497 // alt+home/end go to start/end of the entire line.
1498 // in unwrapped mode, there is no difference between home/end and alt+home/end
1499 control->CmdKeyAssign(wxSCI_KEY_END, wxSCI_KEYMOD_NORM, wxSCI_CMD_LINEENDWRAP);
1500 control->CmdKeyAssign(wxSCI_KEY_END, wxSCI_KEYMOD_ALT, wxSCI_CMD_LINEEND);
1501 control->CmdKeyAssign(wxSCI_KEY_END, wxSCI_KEYMOD_SHIFT, wxSCI_CMD_LINEENDWRAPEXTEND);
1502 control->CmdKeyAssign(wxSCI_KEY_END, wxSCI_KEYMOD_SHIFT|wxSCI_KEYMOD_ALT, wxSCI_CMD_LINEENDEXTEND);
1503
1504 // if user wants "Home" key to set cursor to the very beginning of line
1505 if (mgr->ReadBool(_T("/simplified_home"), false))
1506 {
1507 control->CmdKeyAssign(wxSCI_KEY_HOME,wxSCI_KEYMOD_NORM,wxSCI_CMD_HOMEWRAP);
1508 control->CmdKeyAssign(wxSCI_KEY_HOME,wxSCI_KEYMOD_ALT,wxSCI_CMD_HOME);
1509 control->CmdKeyAssign(wxSCI_KEY_HOME,wxSCI_KEYMOD_SHIFT,wxSCI_CMD_HOMEWRAPEXTEND);
1510 control->CmdKeyAssign(wxSCI_KEY_HOME,wxSCI_KEYMOD_SHIFT|wxSCI_KEYMOD_ALT,wxSCI_CMD_HOMEEXTEND);
1511 }
1512 else // else set default "Home" key behaviour
1513 {
1514 control->CmdKeyAssign(wxSCI_KEY_HOME,wxSCI_KEYMOD_NORM,wxSCI_CMD_VCHOMEWRAP);
1515 control->CmdKeyAssign(wxSCI_KEY_HOME,wxSCI_KEYMOD_ALT,wxSCI_CMD_VCHOME);
1516 control->CmdKeyAssign(wxSCI_KEY_HOME,wxSCI_KEYMOD_SHIFT,wxSCI_CMD_VCHOMEWRAPEXTEND);
1517 control->CmdKeyAssign(wxSCI_KEY_HOME,wxSCI_KEYMOD_SHIFT|wxSCI_KEYMOD_ALT,wxSCI_CMD_VCHOMEEXTEND);
1518 }
1519 }
1520 else
1521 { // in word wrap mode, home/end keys goto start/end of the entire line. alt+home/end goes to wrap points
1522 control->CmdKeyAssign(wxSCI_KEY_END, wxSCI_KEYMOD_ALT, wxSCI_CMD_LINEENDWRAP);
1523 control->CmdKeyAssign(wxSCI_KEY_END, wxSCI_KEYMOD_SHIFT|wxSCI_KEYMOD_ALT, wxSCI_CMD_LINEENDWRAPEXTEND);
1524
1525 // if user wants "Home" key to set cursor to the very beginning of line
1526 if (mgr->ReadBool(_T("/simplified_home"), false))
1527 {
1528 control->CmdKeyAssign(wxSCI_KEY_HOME,wxSCI_KEYMOD_ALT,wxSCI_CMD_HOMEWRAP);
1529 control->CmdKeyAssign(wxSCI_KEY_HOME,wxSCI_KEYMOD_SHIFT|wxSCI_KEYMOD_ALT,wxSCI_CMD_HOMEWRAPEXTEND);
1530 }
1531 else // else set default "Home" key behaviour
1532 {
1533 control->CmdKeyAssign(wxSCI_KEY_HOME,wxSCI_KEYMOD_ALT,wxSCI_CMD_VCHOMEWRAP);
1534 control->CmdKeyAssign(wxSCI_KEY_HOME,wxSCI_KEYMOD_SHIFT|wxSCI_KEYMOD_ALT,wxSCI_CMD_VCHOMEWRAPEXTEND);
1535 }
1536 }
1537 control->SetViewEOL(mgr->ReadBool(_T("/show_eol"), false));
1538 control->SetViewWhiteSpace(mgr->ReadInt(_T("/view_whitespace"), 0));
1539
1540 const int caretBuffer = mgr->ReadInt(wxT("/caret_buffer"), 2);
1541 if (caretBuffer == 0)
1542 {
1543 control->SetYCaretPolicy(wxSCI_CARET_EVEN, 0); // default
1544 control->SetVisiblePolicy(wxSCI_CARET_EVEN, 0); // default
1545 }
1546 else if (caretBuffer > 0 && caretBuffer <= 10)
1547 {
1548 // margin of N lines at top/bottom
1549 control->SetYCaretPolicy(wxSCI_CARET_SLOP | wxSCI_CARET_STRICT | wxSCI_CARET_EVEN,
1550 caretBuffer);
1551 control->SetVisiblePolicy(wxSCI_CARET_SLOP | wxSCI_CARET_STRICT | wxSCI_CARET_EVEN,
1552 caretBuffer);
1553 }
1554 else
1555 {
1556 // centred mode
1557 control->SetYCaretPolicy(wxSCI_CARET_STRICT | wxSCI_CARET_EVEN, 4);
1558 control->SetVisiblePolicy(wxSCI_CARET_STRICT | wxSCI_CARET_EVEN, 4);
1559 }
1560
1561 // gutter
1562 control->SetEdgeMode(mgr->ReadInt(_T("/gutter/mode"), 0));
1563 control->SetEdgeColour(colours->GetColour(wxT("editor_gutter")));
1564 control->SetEdgeColumn(mgr->ReadInt(_T("/gutter/column"), 80));
1565
1566 control->StyleSetFont(wxSCI_STYLE_DEFAULT, font);
1567 control->StyleClearAll();
1568
1569 control->SetTabWidth(mgr->ReadInt(_T("/tab_size"), 4));
1570
1571 // margin for bookmarks, breakpoints etc.
1572 // FIXME: how to display a mark with an offset???
1573 control->SetMarginWidth(C_MARKER_MARGIN, 16);
1574 control->SetMarginType(C_MARKER_MARGIN, wxSCI_MARGIN_SYMBOL);
1575 control->SetMarginSensitive(C_MARKER_MARGIN, mgr->ReadBool(_T("/margin_1_sensitive"), true));
1576 // use "|" here or we might break plugins that use the margin (like browsemarks)
1577 control->SetMarginMask(C_MARKER_MARGIN,
1578 control->GetMarginMask(C_MARKER_MARGIN)
1579 | (1 << BOOKMARK_MARKER)
1580 | (1 << BREAKPOINT_MARKER)
1581 | (1 << BREAKPOINT_DISABLED_MARKER)
1582 | (1 << BREAKPOINT_OTHER_MARKER)
1583 | (1 << DEBUG_MARKER)
1584 | (1 << DEBUG_MARKER_HIGHLIGHT)
1585 | (1 << ERROR_MARKER) );
1586
1587 // 1.) Marker for Bookmarks etc...
1588 control->MarkerDefine(BOOKMARK_MARKER, BOOKMARK_STYLE);
1589 control->MarkerSetBackground(BOOKMARK_MARKER, wxColour(0xA0, 0xA0, 0xFF));
1590
1591 // 2.) Marker for Breakpoints etc...
1592 cbEditorInternalData::SetupBreakpointMarkers(control, 16);
1593
1594 // 3.) Marker for Debugging (currently debugged line) etc...
1595 control->MarkerDefine(DEBUG_MARKER, DEBUG_STYLE);
1596 control->MarkerSetBackground(DEBUG_MARKER, wxColour(0xFF, 0xFF, 0x00));
1597
1598 control->MarkerDefine(DEBUG_MARKER_HIGHLIGHT, DEBUG_STYLE_HIGHLIGHT);
1599 control->MarkerSetBackground(DEBUG_MARKER_HIGHLIGHT, wxColour(0xFF, 0xFF, 0x00));
1600
1601 // 4.) Marker for Errors...
1602 control->MarkerDefine(ERROR_MARKER, ERROR_STYLE);
1603 control->MarkerSetBackground(ERROR_MARKER, wxColour(0xFF, 0x00, 0x00));
1604
1605 // changebar margin
1606 if (mgr->ReadBool(_T("/margin/use_changebar"), true))
1607 {
1608 control->SetMarginWidth(C_CHANGEBAR_MARGIN, changeBarMarginBaseWidth);
1609 control->SetMarginType(C_CHANGEBAR_MARGIN, wxSCI_MARGIN_SYMBOL);
1610 // use "|" here or we might break plugins that use the margin (none at the moment)
1611 control->SetMarginMask(C_CHANGEBAR_MARGIN,
1612 control->GetMarginMask(C_CHANGEBAR_MARGIN)
1613 | (1 << wxSCI_MARKNUM_CHANGEUNSAVED)
1614 | (1 << wxSCI_MARKNUM_CHANGESAVED) );
1615
1616 control->MarkerDefine(wxSCI_MARKNUM_CHANGEUNSAVED, wxSCI_MARK_FULLRECT);
1617 control->MarkerSetBackground(wxSCI_MARKNUM_CHANGEUNSAVED, wxColour(0xFF, 0xE6, 0x04));
1618 control->MarkerDefine(wxSCI_MARKNUM_CHANGESAVED, wxSCI_MARK_FULLRECT);
1619 control->MarkerSetBackground(wxSCI_MARKNUM_CHANGESAVED, wxColour(0x04, 0xFF, 0x50));
1620 }
1621 else
1622 control->SetMarginWidth(C_CHANGEBAR_MARGIN, 0);
1623
1624 // NOTE: duplicate line in editorconfigurationdlg.cpp (ctor)
1625 control->SetScrollWidthTracking( mgr->ReadBool(_T("/margin/scroll_width_tracking"), false));
1626 control->SetMultipleSelection( mgr->ReadBool(_T("/selection/multi_select"), false));
1627 const bool multiTyping = mgr->ReadBool(_T("/selection/multi_typing"), false);
1628 control->SetAdditionalSelectionTyping(multiTyping);
1629 control->SetMultiPaste(multiTyping);
1630
1631 unsigned virtualSpace = 0;
1632 if (mgr->ReadBool(_T("/selection/use_rect_vspace"), false))
1633 virtualSpace |= wxSCI_VS_RECTANGULARSELECTION;
1634 if (mgr->ReadBool(_T("/selection/use_vspace"), false))
1635 virtualSpace |= wxSCI_VS_USERACCESSIBLE;
1636 if (!virtualSpace)
1637 virtualSpace = wxSCI_VS_NONE; // Just in case wxSCI_VS_NONE != 0
1638 control->SetVirtualSpaceOptions(virtualSpace);
1639 }
1640
1641 // static
InternalSetEditorStyleAfterFileOpen(cbStyledTextCtrl * control)1642 void cbEditor::InternalSetEditorStyleAfterFileOpen(cbStyledTextCtrl* control)
1643 {
1644 if (!control)
1645 return;
1646
1647 ConfigManager* mgr = Manager::Get()->GetConfigManager(_T("editor"));
1648
1649 // set the EOL, fall back value: Windows takes CR+LF, other platforms LF only
1650 int eolMode = mgr->ReadInt(_T("/eol/eolmode"), platform::windows ? wxSCI_EOL_CRLF : wxSCI_EOL_LF);
1651
1652 if (eolMode == 3) //auto detect the EOL
1653 eolMode = DetectLineEnds(control);
1654
1655 control->SetEOLMode(eolMode);
1656
1657 // indentation style is already set
1658 if (mgr->ReadBool(_T("/detect_indent"), false))
1659 {
1660 // override style if auto-detection succeeds
1661 int indentStyle = cbEditorInternalData::DetectIndentStyle(control);
1662 if (indentStyle == 0)
1663 control->SetUseTabs(true);
1664 else if (indentStyle != -1)
1665 {
1666 control->SetUseTabs(false);
1667 control->SetTabWidth(indentStyle);
1668 }
1669 }
1670
1671 // Interpret #if/#else/#endif to grey out code that is not active
1672 control->SetProperty(_T("lexer.cpp.track.preprocessor"), mgr->ReadBool(_T("/track_preprocessor"), true) ? _T("1") : _T("0"));
1673
1674 // code folding
1675 if (mgr->ReadBool(_T("/folding/show_folds"), true))
1676 {
1677 control->SetProperty(_T("fold"), _T("1"));
1678 control->SetProperty(_T("fold.html"), mgr->ReadBool(_T("/folding/fold_xml"), true) ? _T("1") : _T("0"));
1679 control->SetProperty(_T("fold.comment"), mgr->ReadBool(_T("/folding/fold_comments"), false) ? _T("1") : _T("0"));
1680 control->SetProperty(_T("fold.compact"), _T("0"));
1681 control->SetProperty(_T("fold.preprocessor"), mgr->ReadBool(_T("/folding/fold_preprocessor"), false) ? _T("1") : _T("0"));
1682
1683 control->SetFoldFlags(16);
1684 control->SetMarginType(C_FOLDING_MARGIN, wxSCI_MARGIN_SYMBOL);
1685 control->SetMarginWidth(C_FOLDING_MARGIN, foldingMarginBaseWidth);
1686 // use "|" here or we might break plugins that use the margin (none at the moment)
1687 control->SetMarginMask(C_FOLDING_MARGIN,
1688 control->GetMarginMask(C_FOLDING_MARGIN)
1689 | ( wxSCI_MASK_FOLDERS
1690 - ( (1 << wxSCI_MARKNUM_CHANGEUNSAVED)
1691 | (1 << wxSCI_MARKNUM_CHANGESAVED))) );
1692 control->SetMarginSensitive(C_FOLDING_MARGIN, 1);
1693 }
1694 else
1695 {
1696 control->SetProperty(_T("fold"), _T("0"));
1697 control->SetMarginWidth(C_FOLDING_MARGIN, 0);
1698 }
1699 control->SetProperty(_T("highlight.wxsmith"), mgr->ReadBool(_T("/highlight_wxsmith"), true) ? _T("1") : _T("0"));
1700
1701 // line numbering
1702 control->SetMarginType(C_LINE_MARGIN, wxSCI_MARGIN_NUMBER);
1703
1704 // As a final step colourise the document. This make sure that style and folding information is
1705 // set on every line/character of the editor. If Colourise is called earlier restoring the
1706 // folding of the editor might not work. Also this is probably slow for larger files, so it is
1707 // best if we call it minimal number of times.
1708 control->Colourise(0, -1);
1709 }
1710
SetColourSet(EditorColourSet * theme)1711 void cbEditor::SetColourSet(EditorColourSet* theme)
1712 {
1713 m_pTheme = theme;
1714 SetLanguage(m_lang, true);
1715 }
1716
GetEncoding() const1717 wxFontEncoding cbEditor::GetEncoding() const
1718 {
1719 if (!m_pData)
1720 return wxFONTENCODING_SYSTEM;
1721
1722 return m_pData->m_encoding;
1723 }
1724
GetEncodingName() const1725 wxString cbEditor::GetEncodingName( ) const
1726 {
1727 return wxFontMapper::GetEncodingName(GetEncoding());
1728 }
1729
SetEncoding(wxFontEncoding encoding)1730 void cbEditor::SetEncoding(wxFontEncoding encoding)
1731 {
1732 if (!m_pData)
1733 return;
1734
1735 if (encoding == wxFONTENCODING_SYSTEM)
1736 encoding = wxLocale::GetSystemEncoding();
1737
1738 if (encoding == m_pData->m_encoding)
1739 return;
1740
1741 m_pData->m_encoding = encoding;
1742 SetModified(true);
1743 }
1744
GetUseBom() const1745 bool cbEditor::GetUseBom() const
1746 {
1747 if (!m_pData)
1748 return false;
1749 return m_pData->m_useByteOrderMark;
1750 }
1751
SetUseBom(bool bom)1752 void cbEditor::SetUseBom( bool bom )
1753 {
1754 if (!m_pData)
1755 return;
1756
1757 if ( bom == GetUseBom() )
1758 return;
1759
1760 m_pData->m_useByteOrderMark = bom;
1761 SetModified(true);
1762 }
1763
Reload(bool detect_encoding)1764 bool cbEditor::Reload(bool detect_encoding)
1765 {
1766 // keep current pos
1767 const int pos = m_pControl ? m_pControl->GetCurrentPos() : 0;
1768 const int pos2 = m_pControl2 ? m_pControl2->GetCurrentPos() : 0;
1769
1770 // call open
1771 if (!Open(detect_encoding))
1772 {
1773 return false;
1774 }
1775 // Re-establish margin styles, width, etc
1776 SetEditorStyleAfterFileOpen();
1777
1778 // return (if possible) to old pos
1779 if (m_pControl)
1780 {
1781 m_pControl->GotoPos(pos);
1782 }
1783 if (m_pControl2)
1784 {
1785 m_pControl2->GotoPos(pos2);
1786 }
1787 return true;
1788 } // end of Reload
1789
Touch()1790 void cbEditor::Touch()
1791 {
1792 m_LastModified = wxDateTime::Now();
1793 SetModified(true);
1794 }
1795
SetLanguage(HighlightLanguage lang,bool colourise)1796 void cbEditor::SetLanguage(HighlightLanguage lang, bool colourise)
1797 {
1798 if (m_pTheme)
1799 {
1800 m_lang = m_pTheme->Apply(this, lang, colourise);
1801 if (m_pControl)
1802 SetLanguageDependentColours(*m_pControl);
1803 if (m_pControl2)
1804 SetLanguageDependentColours(*m_pControl2);
1805 }
1806 else
1807 m_lang = HL_AUTO;
1808 }
1809
Open(bool detectEncoding)1810 bool cbEditor::Open(bool detectEncoding)
1811 {
1812 if (m_pProjectFile)
1813 {
1814 if (!wxFileExists(m_Filename))
1815 m_pProjectFile->SetFileState(fvsMissing);
1816 else if (!wxFile::Access(m_Filename.c_str(), wxFile::write)) // readonly
1817 m_pProjectFile->SetFileState(fvsReadOnly);
1818 }
1819
1820 if (!wxFileExists(m_Filename))
1821 return false;
1822
1823 // open file
1824 SetReadOnly(false);
1825
1826 m_pControl->ClearAll();
1827 m_pControl->SetModEventMask(0);
1828
1829 if (!m_pData)
1830 return false;
1831
1832 if (!m_pData->m_pFileLoader)
1833 m_pData->m_pFileLoader = Manager::Get()->GetFileManager()->Load(m_Filename, false);
1834
1835 #ifdef fileload_measuring
1836 wxStopWatch sw;
1837 #endif
1838 EncodingDetector enc((wxByte*)m_pData->m_pFileLoader->GetData(), m_pData->m_pFileLoader->GetLength());
1839 if (detectEncoding)
1840 {
1841 m_pData->m_useByteOrderMark = enc.UsesBOM();
1842 m_pData->m_byteOrderMarkLength = enc.GetBOMSizeInBytes();
1843 m_pData->m_encoding = enc.GetFontEncoding();
1844
1845 SetEncoding(enc.GetFontEncoding());
1846 SetUseBom(m_pData->m_byteOrderMarkLength > 0);
1847 }
1848
1849 ConfigManager* mgr = Manager::Get()->GetConfigManager(_T("editor"));
1850 #ifdef fileload_measuring
1851 Manager::Get()->GetLogManager()->DebugLog(F(_T("cbEditor::Open() => Encoding detection and conversion took : %d ms"),(int)sw.Time()));
1852 sw.Start();
1853 #endif
1854
1855 m_pControl->InsertText(0, enc.GetWxStr());
1856 m_pControl->EmptyUndoBuffer(mgr->ReadBool(_T("/margin/use_changebar"), true));
1857 m_pControl->SetModEventMask(wxSCI_MODEVENTMASKALL);
1858
1859 // mark the file read-only, if applicable
1860 bool read_only = !wxFile::Access(m_Filename.c_str(), wxFile::write);
1861 SetReadOnly(read_only);
1862
1863 wxFileName fname(m_Filename);
1864 m_LastModified = fname.GetModificationTime();
1865
1866 SetModified(false);
1867
1868 NotifyPlugins(cbEVT_EDITOR_OPEN);
1869
1870 if (m_pData->m_pFileLoader)
1871 {
1872 delete m_pData->m_pFileLoader;
1873 m_pData->m_pFileLoader = nullptr;
1874 }
1875 #ifdef fileload_measuring
1876 Manager::Get()->GetLogManager()->DebugLog(F(_T("loading into editor needs : %d ms"),(int)sw.Time()));
1877 #endif
1878 return true;
1879 }
1880
Save()1881 bool cbEditor::Save()
1882 {
1883 if ( !GetModified() )
1884 return true;
1885
1886 // remember current column (caret and anchor)
1887 int columnC = m_pControl->GetColumn(m_pControl->GetCurrentPos());
1888 int columnA = m_pControl->GetColumn(m_pControl->GetAnchor());
1889
1890 // one undo action for all modifications in this context
1891 // (angled braces added for clarity)
1892 m_pControl->BeginUndoAction();
1893 {
1894 if (m_pData->m_strip_trailing_spaces)
1895 m_pData->StripTrailingSpaces();
1896 if (m_pData->m_ensure_consistent_line_ends)
1897 m_pData->EnsureConsistentLineEnds();
1898 if (m_pData->m_ensure_final_line_end)
1899 m_pData->EnsureFinalLineEnd();
1900 }
1901 m_pControl->EndUndoAction();
1902
1903 // restore virtual position ( if changed by StripTrailingSpaces() )
1904 columnC -= m_pControl->GetColumn(m_pControl->GetCurrentPos());
1905 columnA -= m_pControl->GetColumn(m_pControl->GetAnchor());
1906 if (columnC > 0)
1907 m_pControl->SetSelectionNCaretVirtualSpace(0, columnC);
1908 if (columnA > 0)
1909 m_pControl->SetSelectionNAnchorVirtualSpace(0, columnA);
1910
1911 if (!m_IsOK)
1912 return SaveAs();
1913
1914 m_pControl->BeginUndoAction();
1915 NotifyPlugins(cbEVT_EDITOR_BEFORE_SAVE);
1916 m_pControl->EndUndoAction();
1917
1918 ConfigManager *cfg = Manager::Get()->GetConfigManager(wxT("app"));
1919 const bool robustSave = cfg->ReadBool(wxT("/environment/robust_save"), true);
1920
1921 if (!cbSaveToFile(m_Filename, m_pControl->GetText(), GetEncoding(), GetUseBom(), robustSave))
1922 {
1923 wxString msg;
1924 msg.Printf(_("File %s could not be saved..."), GetFilename().c_str());
1925 cbMessageBox(msg, _("Error saving file"), wxICON_ERROR);
1926 return false; // failed; file is read-only?
1927 }
1928
1929 wxFileName fname(m_Filename);
1930 m_LastModified = fname.GetModificationTime();
1931
1932 m_IsOK = true;
1933
1934 m_pControl->SetSavePoint();
1935 SetModified(false);
1936
1937 NotifyPlugins(cbEVT_EDITOR_SAVE);
1938 return true;
1939 } // end of Save
1940
SaveAs()1941 bool cbEditor::SaveAs()
1942 {
1943 wxFileName fname;
1944 fname.Assign(m_Filename);
1945 ConfigManager* mgr = Manager::Get()->GetConfigManager(_T("app"));
1946 int StoredIndex = 0;
1947 wxString Filters = FileFilters::GetFilterString();
1948 wxString Path = fname.GetPath();
1949 wxString Extension = fname.GetExt();
1950 wxString Filter;
1951 if (!Extension.IsEmpty())
1952 { // use the current extension as the filter
1953 // Select filter belonging to this file type:
1954 Extension.Prepend(_T("."));
1955 Filter = FileFilters::GetFilterString(Extension);
1956 }
1957 else if (mgr)
1958 {
1959 // File type is unknown. Select the last used filter:
1960 Filter = mgr->Read(_T("/file_dialogs/save_file_as/filter"), _T("C/C++ files"));
1961 }
1962 if (!Filter.IsEmpty())
1963 {
1964 // We found a filter, look up its index:
1965 int sep = Filter.find(_T("|"));
1966 if (sep != wxNOT_FOUND)
1967 Filter.Truncate(sep);
1968 if (!Filter.IsEmpty())
1969 FileFilters::GetFilterIndexFromName(Filters, Filter, StoredIndex);
1970 }
1971 if (mgr && Path.IsEmpty())
1972 Path = mgr->Read(_T("/file_dialogs/save_file_as/directory"), Path);
1973
1974 wxFileDialog dlg(Manager::Get()->GetAppWindow(), _("Save file"), Path, fname.GetFullName(),
1975 wxEmptyString, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
1976 // Initialize the wildcards here. If we do it in the wxFileDialog constructor it will detect
1977 // that our file name doesn't have extension and it will try to add one. It doens't have a
1978 // correct value for the filter index we want, so it uses the first one which is ads for Ada
1979 // files. This is happening only on wxGTK.
1980 dlg.SetWildcard(Filters);
1981 dlg.SetFilterIndex(StoredIndex);
1982 PlaceWindow(&dlg);
1983 if (dlg.ShowModal() != wxID_OK)
1984 { // cancelled out
1985 return false;
1986 }
1987 m_Filename = dlg.GetPath();
1988 Manager::Get()->GetLogManager()->Log(m_Filename);
1989 fname.Assign(m_Filename);
1990 m_Shortname = fname.GetFullName();
1991 SetEditorTitle(m_Shortname);
1992 // invalidate m_pProjectFile, because if kept, it would point to the ProjectFile with old name and
1993 // cause ProjectManager::RemoveFileFromProject called via context menu to crash
1994 SetProjectFile(nullptr);
1995 //Manager::Get()->GetLogManager()->Log(mltDevDebug, "Filename=%s\nShort=%s", m_Filename.c_str(), m_Shortname.c_str());
1996 m_IsOK = true;
1997 SetLanguage(HL_AUTO, true);
1998 SetModified(true);
1999 SetEditorStyleAfterFileOpen();
2000 OverrideUseTabsPerLanguage(m_pControl);
2001 OverrideUseTabsPerLanguage(m_pControl2);
2002 // store the last used filter and directory
2003 if (mgr)
2004 {
2005 int Index = dlg.GetFilterIndex();
2006 Filter.Empty();
2007 if (FileFilters::GetFilterNameFromIndex(Filters, Index, Filter))
2008 mgr->Write(_T("/file_dialogs/save_file_as/filter"), Filter);
2009 wxString Test = dlg.GetDirectory();
2010 mgr->Write(_T("/file_dialogs/save_file_as/directory"), dlg.GetDirectory());
2011 }
2012 return Save();
2013 } // end of SaveAs
2014
SaveFoldState()2015 bool cbEditor::SaveFoldState()
2016 {
2017 bool bRet = false;
2018 if ((m_foldBackup = CreateEditor()))
2019 {
2020 ApplyStyles(m_foldBackup);
2021 m_foldBackup->SetText(m_pControl->GetText());
2022 int count = m_pControl->GetLineCount();
2023 for (int i = 0; i < count; ++i)
2024 m_foldBackup->SetFoldLevel(i,m_pControl->GetFoldLevel(i));
2025 bRet = true;
2026 }
2027 return bRet;
2028 } // end of SaveFoldState
2029
FixFoldState()2030 bool cbEditor::FixFoldState()
2031 {
2032 bool bRet = false;
2033 if (m_foldBackup)
2034 {
2035 int backupLength = m_foldBackup->GetLineCount();
2036 int realLength = m_pControl->GetLineCount();
2037 if (backupLength == realLength) // It is supposed to be the same, but you never know :)
2038 {
2039 ConfigManager* mgr = Manager::Get()->GetConfigManager(_T("editor"));
2040 if (mgr->ReadBool(_T("/folding/show_folds"), true)) // Only fix the folds if the folds are enabled
2041 {
2042 int count = m_pControl->GetLineCount();
2043 for (int i = 0; i < count; ++i)
2044 {
2045 int oldFoldLevel = m_foldBackup->GetFoldLevel(i);
2046 int newFoldLevel = m_pControl->GetFoldLevel(i);
2047 if (oldFoldLevel != newFoldLevel)
2048 {
2049 if (m_pControl->GetLineVisible(i) == true)
2050 m_pControl->SetFoldExpanded(i, true);
2051 else
2052 {
2053 int parent = m_foldBackup->GetFoldParent(i);
2054 while(parent != -1)
2055 {
2056 m_pControl->ToggleFold(parent);
2057 parent = m_foldBackup->GetFoldParent(parent);
2058 }
2059 m_pControl->ShowLines(i, i);
2060 parent = m_foldBackup->GetFoldParent(i);
2061 while(parent != -1)
2062 {
2063 m_pControl->ToggleFold(parent);
2064 parent = m_foldBackup->GetFoldParent(parent);
2065 }
2066 }
2067 }
2068 }
2069 }
2070 bRet = true;
2071 }
2072 m_foldBackup->Destroy();
2073 m_foldBackup = nullptr;
2074 }
2075 return bRet;
2076 } // end of FixFoldState
2077
AutoComplete()2078 void cbEditor::AutoComplete()
2079 {
2080 Manager::Get()->GetLogManager()->Log(_T("cbEditor::AutoComplete() is obsolete.\nUse AutoComplete(cbEditor &ed) from the Abbreviations plugin instead."));
2081 }
2082
DoFoldAll(FoldMode fold)2083 void cbEditor::DoFoldAll(FoldMode fold)
2084 {
2085 cbAssert(m_pControl);
2086 if (m_SplitType != stNoSplit)
2087 cbAssert(m_pControl2);
2088 cbStyledTextCtrl* ctrl = GetControl();
2089
2090 ctrl->Colourise(0, -1); // the *most* important part!
2091
2092 if (fold != FoldMode::toggle)
2093 {
2094 const int count = ctrl->GetLineCount();
2095 for (int line = 0; line < count; ++line)
2096 {
2097 const int level = ctrl->GetFoldLevel(line);
2098 if ((level & wxSCI_FOLDLEVELHEADERFLAG) == 0)
2099 continue;
2100
2101 if (m_pData->mFoldingLimit)
2102 {
2103 const bool isExpanded = ctrl->GetFoldExpanded(line);
2104
2105 // Apply the folding level limit only if the current block will be
2106 // folded (that means it's currently expanded), folding level limiter
2107 // must be enabled of course. Unfolding will not be affected.
2108 if (isExpanded && (fold == FoldMode::contract || fold == FoldMode::toggle))
2109 {
2110 int levelNumber = (level & wxSCI_FOLDLEVELNUMBERMASK) - wxSCI_FOLDLEVELBASE;
2111 if (levelNumber > m_pData->mFoldingLimitLevel - 1)
2112 continue;
2113 }
2114 }
2115
2116 ctrl->FoldLine(line, int(fold));
2117 }
2118 }
2119 else
2120 {
2121 // Toggle implementation - we need this one because the implementation for fold/unfold
2122 // doesn't work correctly - it expands parent folds when there is an expanded child fold.
2123 // We want to toggle all fold points in the editor no matter what level are they at. If
2124 // there is a folding limit we respect it for both contracting and expanding.
2125
2126 struct FoldRange
2127 {
2128 FoldRange(int start, int end, bool hasContracted) :
2129 start(start),
2130 end(end),
2131 hasContracted(hasContracted)
2132 {}
2133
2134 int start, end;
2135 bool hasContracted;
2136 };
2137 // We need to know if a parent fold has been contracted. For this we use a stack with the
2138 // active folds at the line we are processing.
2139 std::stack<FoldRange> parentFolds;
2140
2141 const int count = ctrl->GetLineCount();
2142 for (int line = 0; line < count; ++line)
2143 {
2144 while (!parentFolds.empty() && parentFolds.top().end < line)
2145 parentFolds.pop();
2146
2147 const int level = ctrl->GetFoldLevel(line);
2148 if ((level & wxSCI_FOLDLEVELHEADERFLAG) == 0)
2149 continue;
2150 const int levelNumber = (level & wxSCI_FOLDLEVELNUMBERMASK) - wxSCI_FOLDLEVELBASE;
2151 if (m_pData->mFoldingLimit)
2152 {
2153 // Apply folding level limit.
2154 if (levelNumber > m_pData->mFoldingLimitLevel - 1)
2155 continue;
2156 }
2157
2158 const bool toContract = ctrl->GetFoldExpanded(line);
2159 const int lastChild = ctrl->GetLastChild(line, -1);
2160
2161 bool hasContracted;
2162 if (toContract)
2163 hasContracted = true;
2164 else
2165 {
2166 if (parentFolds.empty())
2167 hasContracted = false;
2168 else
2169 hasContracted = parentFolds.top().hasContracted;
2170 }
2171 parentFolds.push(FoldRange(line, lastChild, hasContracted));
2172
2173 if (toContract)
2174 {
2175 ctrl->SetFoldExpanded(line, false);
2176 ctrl->HideLines(line + 1, lastChild);
2177 }
2178 else
2179 {
2180 ctrl->SetFoldExpanded(line, true);
2181 // If there are contracted parent folds we must not show lines for child folds even
2182 // if they are expanded. When the user does an expand operation for the parent
2183 // Scintilla will make sure to show these lines.
2184 if (!hasContracted)
2185 ctrl->ShowLines(line + 1, lastChild);
2186 }
2187 }
2188 }
2189 }
2190
DoFoldBlockFromLine(int line,FoldMode fold,unsigned foldFlags)2191 void cbEditor::DoFoldBlockFromLine(int line, FoldMode fold, unsigned foldFlags)
2192 {
2193 // Use static asserts to verify the constants are the same, because we don't want to include
2194 // wxscintilla.h in cbeditor.h. This code is here, because we want the Foldmode to be private.
2195 static_assert(int(cbEditor::FoldMode::contract) == wxSCI_FOLDACTION_CONTRACT, "Must match");
2196 static_assert(int(cbEditor::FoldMode::expand) == wxSCI_FOLDACTION_EXPAND, "Must match");
2197 static_assert(int(cbEditor::FoldMode::toggle) == wxSCI_FOLDACTION_TOGGLE, "Must match");
2198
2199 cbAssert(m_pControl);
2200 if (m_SplitType != stNoSplit)
2201 cbAssert(m_pControl2);
2202 cbStyledTextCtrl* ctrl = GetControl();
2203 if (!ctrl)
2204 return;
2205
2206 // This is needed to update the folding information for the current view.
2207 ctrl->Colourise(0, -1);
2208
2209 int foldLine;
2210 int level = ctrl->GetFoldLevel(line);
2211 if (level & wxSCI_FOLDLEVELHEADERFLAG)
2212 foldLine = line;
2213 else
2214 {
2215 // If the line is not a block start line, find the block start. This makes it possible to
2216 // toggle the folding when the cursor is in the middle of some block of code.
2217 foldLine = ctrl->GetFoldParent(line);
2218 if (foldLine == -1)
2219 return;
2220 }
2221 const bool isExpanded = ctrl->GetFoldExpanded(foldLine);
2222
2223 ctrl->FoldLine(foldLine, int(fold));
2224
2225 if (foldFlags & FoldFlags::ensureVisible)
2226 {
2227 if (fold == FoldMode::expand || (fold == FoldMode::toggle && !isExpanded))
2228 ctrl->EnsureVisibleEnforcePolicy(line);
2229 }
2230 }
2231
FoldAll()2232 void cbEditor::FoldAll()
2233 {
2234 DoFoldAll(FoldMode::contract);
2235 }
2236
UnfoldAll()2237 void cbEditor::UnfoldAll()
2238 {
2239 DoFoldAll(FoldMode::expand);
2240 }
2241
ToggleAllFolds()2242 void cbEditor::ToggleAllFolds()
2243 {
2244 DoFoldAll(FoldMode::toggle);
2245 }
2246
SetFoldingIndicator(int id)2247 void cbEditor::SetFoldingIndicator(int id)
2248 {
2249 wxColor f(0xff, 0xff, 0xff); // foreground colour
2250 wxColor b(0x80, 0x80, 0x80); // background colour
2251 // Arrow
2252 if (id == 0)
2253 {
2254 SetMarkerStyle(wxSCI_MARKNUM_FOLDEROPEN, wxSCI_MARK_ARROWDOWN, f, b);
2255 SetMarkerStyle(wxSCI_MARKNUM_FOLDER, wxSCI_MARK_ARROW, f, b);
2256 SetMarkerStyle(wxSCI_MARKNUM_FOLDERSUB, wxSCI_MARK_BACKGROUND, f, b);
2257 SetMarkerStyle(wxSCI_MARKNUM_FOLDERTAIL, wxSCI_MARK_BACKGROUND, f, b);
2258 SetMarkerStyle(wxSCI_MARKNUM_FOLDEREND, wxSCI_MARK_ARROW, f, b);
2259 SetMarkerStyle(wxSCI_MARKNUM_FOLDEROPENMID, wxSCI_MARK_ARROWDOWN, f, b);
2260 SetMarkerStyle(wxSCI_MARKNUM_FOLDERMIDTAIL, wxSCI_MARK_BACKGROUND, f, b);
2261 }
2262 // Circle
2263 else if (id == 1)
2264 {
2265 SetMarkerStyle(wxSCI_MARKNUM_FOLDEROPEN, wxSCI_MARK_CIRCLEMINUS, f, b);
2266 SetMarkerStyle(wxSCI_MARKNUM_FOLDER, wxSCI_MARK_CIRCLEPLUS, f, b);
2267 SetMarkerStyle(wxSCI_MARKNUM_FOLDERSUB, wxSCI_MARK_VLINE, f, b);
2268 SetMarkerStyle(wxSCI_MARKNUM_FOLDERTAIL, wxSCI_MARK_LCORNERCURVE, f, b);
2269 SetMarkerStyle(wxSCI_MARKNUM_FOLDEREND, wxSCI_MARK_CIRCLEPLUSCONNECTED, f, b);
2270 SetMarkerStyle(wxSCI_MARKNUM_FOLDEROPENMID, wxSCI_MARK_CIRCLEMINUSCONNECTED, f, b);
2271 SetMarkerStyle(wxSCI_MARKNUM_FOLDERMIDTAIL, wxSCI_MARK_TCORNER, f, b);
2272 }
2273 // Square
2274 else if (id == 2)
2275 {
2276 SetMarkerStyle(wxSCI_MARKNUM_FOLDEROPEN, wxSCI_MARK_BOXMINUS, f, b);
2277 SetMarkerStyle(wxSCI_MARKNUM_FOLDER, wxSCI_MARK_BOXPLUS, f, b);
2278 SetMarkerStyle(wxSCI_MARKNUM_FOLDERSUB, wxSCI_MARK_VLINE, f, b);
2279 SetMarkerStyle(wxSCI_MARKNUM_FOLDERTAIL, wxSCI_MARK_LCORNER, f, b);
2280 SetMarkerStyle(wxSCI_MARKNUM_FOLDEREND, wxSCI_MARK_BOXPLUSCONNECTED, f, b);
2281 SetMarkerStyle(wxSCI_MARKNUM_FOLDEROPENMID, wxSCI_MARK_BOXMINUSCONNECTED, f, b);
2282 SetMarkerStyle(wxSCI_MARKNUM_FOLDERMIDTAIL, wxSCI_MARK_TCORNER, f, b);
2283 }
2284 // Simple
2285 else if (id == 3)
2286 {
2287 SetMarkerStyle(wxSCI_MARKNUM_FOLDEROPEN, wxSCI_MARK_MINUS, f, b);
2288 SetMarkerStyle(wxSCI_MARKNUM_FOLDER, wxSCI_MARK_PLUS, f, b);
2289 SetMarkerStyle(wxSCI_MARKNUM_FOLDERSUB, wxSCI_MARK_BACKGROUND, f, b);
2290 SetMarkerStyle(wxSCI_MARKNUM_FOLDERTAIL, wxSCI_MARK_BACKGROUND, f, b);
2291 SetMarkerStyle(wxSCI_MARKNUM_FOLDEREND, wxSCI_MARK_PLUS, f, b);
2292 SetMarkerStyle(wxSCI_MARKNUM_FOLDEROPENMID, wxSCI_MARK_MINUS, f, b);
2293 SetMarkerStyle(wxSCI_MARKNUM_FOLDERMIDTAIL, wxSCI_MARK_BACKGROUND, f, b);
2294 }
2295 }
2296
FoldBlockFromLine(int line)2297 void cbEditor::FoldBlockFromLine(int line)
2298 {
2299 if (line == -1)
2300 line = GetControl()->GetCurrentLine();
2301 DoFoldBlockFromLine(line, FoldMode::contract, FoldFlags::ensureVisible);
2302 }
2303
UnfoldBlockFromLine(int line)2304 void cbEditor::UnfoldBlockFromLine(int line)
2305 {
2306 if (line == -1)
2307 line = GetControl()->GetCurrentLine();
2308 DoFoldBlockFromLine(line, FoldMode::expand, FoldFlags::ensureVisible);
2309 }
2310
ToggleFoldBlockFromLine(int line)2311 void cbEditor::ToggleFoldBlockFromLine(int line)
2312 {
2313 if (line == -1)
2314 line = GetControl()->GetCurrentLine();
2315 DoFoldBlockFromLine(line, FoldMode::toggle, FoldFlags::ensureVisible);
2316 }
2317
GotoLine(int line,cb_unused bool centerOnScreen)2318 void cbEditor::GotoLine(int line, cb_unused bool centerOnScreen)
2319 {
2320 cbStyledTextCtrl* control = GetControl();
2321
2322 // Make sure the line is not folded. This is done before moving to that
2323 // line because folding may change the lines layout.
2324 control->EnsureVisible(line);
2325
2326 // If the line or the following is a fold point it will be unfolded, in this way
2327 // when the line is a function declaration (or only contains the opening brace of it [yes, that happens sometimes] )
2328 // the body is shown.
2329 DoFoldBlockFromLine(line, FoldMode::expand, FoldFlags::none);
2330 DoFoldBlockFromLine(line+1, FoldMode::expand, FoldFlags::none);
2331
2332 control->GotoLine(line);
2333 }
2334
GotoTokenPosition(int line,const wxString & tokenName)2335 bool cbEditor::GotoTokenPosition(int line, const wxString& tokenName)
2336 {
2337 cbStyledTextCtrl* control = GetControl();
2338 if (line > control->GetLineCount())
2339 return false;
2340
2341 GotoLine(line, true); // center function on screen
2342 SetFocus(); // ...and set focus to this editor
2343
2344 // Now highlight the token
2345 const int startPos = control->GetCurrentPos();
2346 const int endPos = startPos + control->LineLength(line);
2347 if (endPos <= startPos)
2348 return false;
2349
2350 int tokenPos = control->FindText(startPos, endPos, tokenName,
2351 wxSCI_FIND_WHOLEWORD | wxSCI_FIND_MATCHCASE, nullptr);
2352 if (tokenPos != wxSCI_INVALID_POSITION)
2353 control->SetSelectionInt(tokenPos, tokenPos + tokenName.Len());
2354 else
2355 control->GotoPos(startPos); // fall back, point the cursor to it
2356
2357 return true;
2358 }
2359
BreakpointMarkerToggle(int line)2360 void cbEditor::BreakpointMarkerToggle(int line)
2361 {
2362 int marker = m_pControl->MarkerGet(line);
2363 if (marker & (1 << BREAKPOINT_MARKER))
2364 m_pControl->MarkerDelete(line, BREAKPOINT_MARKER);
2365 else if (marker & (1 << BREAKPOINT_DISABLED_MARKER))
2366 m_pControl->MarkerDelete(line, BREAKPOINT_DISABLED_MARKER);
2367 else
2368 m_pControl->MarkerAdd(line, BREAKPOINT_MARKER);
2369 }
2370
AddBreakpoint(int line,bool notifyDebugger)2371 bool cbEditor::AddBreakpoint(int line, bool notifyDebugger)
2372 {
2373 if (HasBreakpoint(line))
2374 return false;
2375
2376 if (line == -1)
2377 line = GetControl()->GetCurrentLine();
2378
2379 if (!notifyDebugger)
2380 {
2381 BreakpointMarkerToggle(line);
2382 return false;
2383 }
2384
2385 DebuggerManager *dbgManager = Manager::Get()->GetDebuggerManager();
2386 if (dbgManager->GetBreakpointDialog()->AddBreakpoint(dbgManager->GetActiveDebugger(), m_Filename, line + 1))
2387 {
2388 BreakpointMarkerToggle(line);
2389 return true;
2390 }
2391 return false;
2392 }
2393
RemoveBreakpoint(int line,bool notifyDebugger)2394 bool cbEditor::RemoveBreakpoint(int line, bool notifyDebugger)
2395 {
2396 if (!HasBreakpoint(line))
2397 return false;
2398
2399 if (line == -1)
2400 line = GetControl()->GetCurrentLine();
2401
2402 if (!notifyDebugger)
2403 {
2404 BreakpointMarkerToggle(line);
2405 return false;
2406 }
2407
2408 DebuggerManager *dbgManager = Manager::Get()->GetDebuggerManager();
2409 if (dbgManager->GetBreakpointDialog()->RemoveBreakpoint(dbgManager->GetActiveDebugger(), m_Filename, line + 1))
2410 {
2411 BreakpointMarkerToggle(line);
2412 return true;
2413 }
2414 return false;
2415 }
2416
ToggleBreakpoint(int line,bool notifyDebugger)2417 void cbEditor::ToggleBreakpoint(int line, bool notifyDebugger)
2418 {
2419 if (line == -1)
2420 line = GetControl()->GetCurrentLine();
2421 if (!notifyDebugger)
2422 {
2423 BreakpointMarkerToggle(line);
2424 return;
2425 }
2426
2427 DebuggerManager *dbgManager = Manager::Get()->GetDebuggerManager();
2428 cbBreakpointsDlg *dialog = dbgManager->GetBreakpointDialog();
2429 cbDebuggerPlugin *plugin = dbgManager->GetActiveDebugger();
2430 if (!plugin || !plugin->SupportsFeature(cbDebuggerFeature::Breakpoints))
2431 return;
2432
2433 bool toggle = false;
2434 if (HasBreakpoint(line))
2435 {
2436 if (dialog->RemoveBreakpoint(plugin, m_Filename, line + 1))
2437 toggle = true;
2438 }
2439 else
2440 {
2441 if (dialog->AddBreakpoint(plugin, m_Filename, line + 1))
2442 toggle = true;
2443 }
2444
2445 if (toggle)
2446 {
2447 BreakpointMarkerToggle(line);
2448 dialog->Reload();
2449 }
2450 }
2451
HasBreakpoint(int line) const2452 bool cbEditor::HasBreakpoint(int line) const
2453 {
2454 if (line == -1)
2455 line = GetControl()->GetCurrentLine();
2456 return LineHasMarker(BREAKPOINT_MARKER, line) || LineHasMarker(BREAKPOINT_DISABLED_MARKER, line);
2457 }
2458
GotoNextBreakpoint()2459 void cbEditor::GotoNextBreakpoint()
2460 {
2461 MarkerNext(BREAKPOINT_MARKER);
2462 }
2463
GotoPreviousBreakpoint()2464 void cbEditor::GotoPreviousBreakpoint()
2465 {
2466 MarkerPrevious(BREAKPOINT_MARKER);
2467 }
2468
ToggleBookmark(int line)2469 void cbEditor::ToggleBookmark(int line)
2470 {
2471 MarkerToggle(BOOKMARK_MARKER, line);
2472 }
2473
RefreshBreakpointMarkers()2474 void cbEditor::RefreshBreakpointMarkers()
2475 {
2476 // First remove all breakpoint markers, then add the markers for the active debugger
2477 cbStyledTextCtrl *c = GetControl();
2478 int line = 0;
2479 while ((line = c->MarkerNext(line, (1 << BREAKPOINT_MARKER))) != -1)
2480 MarkerToggle(BREAKPOINT_MARKER, line);
2481
2482 line = 0;
2483 while ((line = c->MarkerNext(line, (1 << BREAKPOINT_DISABLED_MARKER))) != -1)
2484 MarkerToggle(BREAKPOINT_DISABLED_MARKER, line);
2485
2486 line = 0;
2487 while ((line = c->MarkerNext(line, (1 << BREAKPOINT_OTHER_MARKER))) != -1)
2488 MarkerToggle(BREAKPOINT_OTHER_MARKER, line);
2489
2490 const DebuggerManager::RegisteredPlugins &plugins = Manager::Get()->GetDebuggerManager()->GetAllDebuggers();
2491 for (DebuggerManager::RegisteredPlugins::const_iterator it = plugins.begin(); it != plugins.end(); ++it)
2492 {
2493 const cbDebuggerPlugin *debugger = it->first;
2494 if (debugger == Manager::Get()->GetDebuggerManager()->GetActiveDebugger())
2495 {
2496 for (int ii = 0; ii < debugger->GetBreakpointsCount(); ++ii)
2497 {
2498 cb::shared_ptr<const cbBreakpoint> bp = debugger->GetBreakpoint(ii);
2499 if (bp->GetLocation() == GetFilename())
2500 {
2501 if (bp->IsEnabled())
2502 MarkerToggle(BREAKPOINT_MARKER, bp->GetLine() - 1);
2503 else
2504 MarkerToggle(BREAKPOINT_DISABLED_MARKER, bp->GetLine() - 1);
2505 }
2506 }
2507 }
2508 else
2509 {
2510 // all breakpoints for the non active debugger use the other breakpoint marker
2511 for (int ii = 0; ii < debugger->GetBreakpointsCount(); ++ii)
2512 {
2513 cb::shared_ptr<const cbBreakpoint> bp = debugger->GetBreakpoint(ii);
2514 if (bp->GetLocation() == GetFilename())
2515 MarkerToggle(BREAKPOINT_OTHER_MARKER, bp->GetLine() - 1);
2516 }
2517 }
2518 }
2519 }
2520
HasBookmark(int line) const2521 bool cbEditor::HasBookmark(int line) const
2522 {
2523 return LineHasMarker(BOOKMARK_MARKER, line);
2524 }
2525
GotoNextBookmark()2526 void cbEditor::GotoNextBookmark()
2527 {
2528 MarkerNext(BOOKMARK_MARKER);
2529 }
2530
GotoPreviousBookmark()2531 void cbEditor::GotoPreviousBookmark()
2532 {
2533 MarkerPrevious(BOOKMARK_MARKER);
2534 }
2535
ClearAllBookmarks()2536 void cbEditor::ClearAllBookmarks()
2537 {
2538 cbStyledTextCtrl* control = GetControl();
2539 control->MarkerDeleteAll(BOOKMARK_MARKER);
2540 }
2541
SetDebugLine(int line)2542 void cbEditor::SetDebugLine(int line)
2543 {
2544 MarkLine(DEBUG_MARKER, line);
2545 if (GetControl()->GetCaretLineVisible())
2546 MarkLine(DEBUG_MARKER_HIGHLIGHT, line);
2547 m_pData->m_LastDebugLine = line;
2548 }
2549
SetErrorLine(int line)2550 void cbEditor::SetErrorLine(int line)
2551 {
2552 MarkLine(ERROR_MARKER, line);
2553 }
2554
Undo()2555 void cbEditor::Undo()
2556 {
2557 cbAssert(GetControl());
2558 GetControl()->Undo();
2559 }
2560
Redo()2561 void cbEditor::Redo()
2562 {
2563 cbAssert(GetControl());
2564 GetControl()->Redo();
2565 }
2566
ClearHistory()2567 void cbEditor::ClearHistory()
2568 {
2569 cbAssert(GetControl());
2570 GetControl()->EmptyUndoBuffer(Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/margin/use_changebar"), true));
2571 }
2572
GotoNextChanged()2573 void cbEditor::GotoNextChanged()
2574 {
2575 cbAssert(GetControl());
2576 cbStyledTextCtrl* p_Control = GetControl();
2577 int fromLine = p_Control->LineFromPosition(p_Control->GetCurrentPos());
2578 int toLine = p_Control->GetLineCount() - 1;
2579 if (fromLine == toLine)
2580 fromLine = 0;
2581 else
2582 fromLine++;
2583
2584 int newLine = p_Control->FindChangedLine(fromLine, toLine);
2585 if (newLine != wxSCI_INVALID_POSITION)
2586 {
2587 p_Control->GotoLine(newLine);
2588 p_Control->MakeNearbyLinesVisible(p_Control->GetCurrentLine());
2589 }
2590 }
2591
GotoPreviousChanged()2592 void cbEditor::GotoPreviousChanged()
2593 {
2594 cbAssert(GetControl());
2595 cbStyledTextCtrl* p_Control = GetControl();
2596 int fromLine = p_Control->LineFromPosition(p_Control->GetCurrentPos());
2597 int toLine = 0;
2598 if (fromLine == toLine)
2599 fromLine = p_Control->GetLineCount() - 1;
2600 else
2601 fromLine--;
2602
2603 int newLine = p_Control->FindChangedLine(fromLine, toLine);
2604 if (newLine != wxSCI_INVALID_POSITION)
2605 {
2606 p_Control->GotoLine(newLine);
2607 p_Control->MakeNearbyLinesVisible(p_Control->GetCurrentLine());
2608 }
2609 }
2610
SetChangeCollection(bool collectChange)2611 void cbEditor::SetChangeCollection(bool collectChange)
2612 {
2613 cbAssert(GetControl());
2614 GetControl()->SetChangeCollection(collectChange);
2615 }
2616
Cut()2617 void cbEditor::Cut()
2618 {
2619 cbAssert(GetControl());
2620 GetControl()->Cut();
2621 }
2622
Copy()2623 void cbEditor::Copy()
2624 {
2625 cbAssert(GetControl());
2626 GetControl()->Copy();
2627 }
2628
Paste()2629 void cbEditor::Paste()
2630 {
2631 cbAssert(GetControl());
2632 GetControl()->Paste();
2633 }
2634
CanUndo() const2635 bool cbEditor::CanUndo() const
2636 {
2637 cbAssert(GetControl());
2638 return !IsReadOnly() && GetControl()->CanUndo();
2639 }
2640
CanRedo() const2641 bool cbEditor::CanRedo() const
2642 {
2643 cbAssert(GetControl());
2644 return !IsReadOnly() && GetControl()->CanRedo();
2645 }
2646
HasSelection() const2647 bool cbEditor::HasSelection() const
2648 {
2649 cbAssert(GetControl());
2650 cbStyledTextCtrl* control = GetControl();
2651 return control->GetSelectionStart() != control->GetSelectionEnd();
2652 }
2653
CanPaste() const2654 bool cbEditor::CanPaste() const
2655 {
2656 cbAssert(GetControl());
2657 if (platform::gtk)
2658 return !IsReadOnly();
2659
2660 return GetControl()->CanPaste() && !IsReadOnly();
2661 }
2662
IsReadOnly() const2663 bool cbEditor::IsReadOnly() const
2664 {
2665 cbAssert(GetControl());
2666 return GetControl()->GetReadOnly();
2667 }
2668
SetReadOnly(bool readonly)2669 void cbEditor::SetReadOnly(bool readonly)
2670 {
2671 cbAssert(GetControl());
2672 GetControl()->SetReadOnly(readonly);
2673 }
2674
LineHasMarker(int marker,int line) const2675 bool cbEditor::LineHasMarker(int marker, int line) const
2676 {
2677 if (line == -1)
2678 line = GetControl()->GetCurrentLine();
2679 return m_pControl->MarkerGet(line) & (1 << marker);
2680 }
2681
MarkerToggle(int marker,int line)2682 void cbEditor::MarkerToggle(int marker, int line)
2683 {
2684 if (line == -1)
2685 line = GetControl()->GetCurrentLine();
2686 if (LineHasMarker(marker, line))
2687 GetControl()->MarkerDelete(line, marker);
2688 else
2689 GetControl()->MarkerAdd(line, marker);
2690 }
2691
MarkerNext(int marker)2692 void cbEditor::MarkerNext(int marker)
2693 {
2694 int line = GetControl()->GetCurrentLine() + 1;
2695 int newLine = GetControl()->MarkerNext(line, 1 << marker);
2696
2697 // Start from beginning if at last marker.
2698 if (newLine == -1)
2699 {
2700 line = 0;
2701 newLine = GetControl()->MarkerNext(line, 1 << marker);
2702
2703 if (newLine != -1)
2704 InfoWindow::Display(_("Find bookmark action"), _("Reached the end of the document"), 1000);
2705 }
2706
2707 if (newLine != -1)
2708 GotoLine(newLine);
2709 }
2710
MarkerPrevious(int marker)2711 void cbEditor::MarkerPrevious(int marker)
2712 {
2713 int line = GetControl()->GetCurrentLine() - 1;
2714 int newLine = GetControl()->MarkerPrevious(line, 1 << marker);
2715
2716 // Back to last one if at first marker.
2717 if (newLine == -1)
2718 {
2719 line = GetControl()->GetLineCount();
2720 newLine = GetControl()->MarkerPrevious(line, 1 << marker);
2721
2722 if (newLine != -1)
2723 InfoWindow::Display(_("Find bookmark action"), _("Reached the end of the document"), 1000);
2724 }
2725
2726 if (newLine != -1)
2727 GotoLine(newLine);
2728 }
2729
MarkLine(int marker,int line)2730 void cbEditor::MarkLine(int marker, int line)
2731 {
2732 if (line == -1)
2733 GetControl()->MarkerDeleteAll(marker);
2734 else
2735 GetControl()->MarkerAdd(line, marker);
2736 }
2737
GotoMatchingBrace()2738 void cbEditor::GotoMatchingBrace()
2739 {
2740 cbStyledTextCtrl* control = GetControl();
2741
2742 // this works only when the caret is *before* the brace
2743 int matchingBrace = control->BraceMatch(control->GetCurrentPos());
2744
2745 // if we haven't found it, we 'll search at pos-1 too
2746 if (matchingBrace == wxSCI_INVALID_POSITION)
2747 matchingBrace = control->BraceMatch(control->GetCurrentPos() - 1);
2748 else
2749 ++matchingBrace; // to keep the caret on the same side of the brace
2750
2751 // else look for a matching preprocessor command
2752 if (matchingBrace == wxSCI_INVALID_POSITION)
2753 {
2754 wxRegEx ppIf(wxT("^[ \t]*#[ \t]*if"));
2755 wxRegEx ppElse(wxT("^[ \t]*#[ \t]*el"));
2756 wxRegEx ppEnd(wxT("^[ \t]*#[ \t]*endif"));
2757 wxRegEx pp(wxT("^[ \t]*#[ \t]*[a-z]*")); // generic match to get length
2758 if (ppIf.Matches(control->GetCurLine()) || ppElse.Matches(control->GetCurLine()))
2759 {
2760 int depth = 1; // search forwards
2761 for (int i = control->GetCurrentLine() + 1; i < control->GetLineCount(); ++i)
2762 {
2763 if (control->GetLine(i).Find(wxT('#')) != wxNOT_FOUND) // limit testing due to performance cost
2764 {
2765 if (ppIf.Matches(control->GetLine(i))) // ignore else's, elif's, ...
2766 ++depth;
2767 else if (ppEnd.Matches(control->GetLine(i)))
2768 --depth;
2769 }
2770 if (depth == 0)
2771 {
2772 pp.Matches(control->GetLine(i));
2773 matchingBrace = control->PositionFromLine(i) + pp.GetMatch(control->GetLine(i)).Length();
2774 break;
2775 }
2776 }
2777 }
2778 else if (ppEnd.Matches(control->GetCurLine()))
2779 {
2780 int depth = -1; // search backwards
2781 for (int i = control->GetCurrentLine() - 1; i >= 0; --i)
2782 {
2783 if (control->GetLine(i).Find(wxT('#')) != wxNOT_FOUND) // limit testing due to performance cost
2784 {
2785 if (ppIf.Matches(control->GetLine(i))) // ignore else's, elif's, ...
2786 ++depth;
2787 else if (ppEnd.Matches(control->GetLine(i)))
2788 --depth;
2789 }
2790 if (depth == 0)
2791 {
2792 pp.Matches(control->GetLine(i));
2793 matchingBrace = control->PositionFromLine(i) + pp.GetMatch(control->GetLine(i)).Length();
2794 break;
2795 }
2796 }
2797 }
2798 }
2799
2800 // now, we either found it or not
2801 if (matchingBrace != wxSCI_INVALID_POSITION)
2802 {
2803 // move to the actual position
2804 control->GotoPos(matchingBrace);
2805 control->ChooseCaretX();
2806 // make nearby lines visible
2807 control->MakeNearbyLinesVisible(control->GetCurrentLine());
2808 }
2809 }
2810
HighlightBraces()2811 void cbEditor::HighlightBraces()
2812 {
2813 cbStyledTextCtrl* control = GetControl();
2814
2815 ////// BRACES HIGHLIGHTING ///////
2816 int currPos = control->GetCurrentPos();
2817 int newPos = control->BraceMatch(currPos);
2818 if (newPos == wxSCI_INVALID_POSITION)
2819 {
2820 if (currPos > 0)
2821 currPos--;
2822 newPos = control->BraceMatch(currPos);
2823 }
2824 wxChar ch = control->GetCharAt(currPos);
2825 if (ch == _T('{') || ch == _T('[') || ch == _T('(') ||
2826 ch == _T('}') || ch == _T(']') || ch == _T(')'))
2827 {
2828 if (newPos != wxSCI_INVALID_POSITION)
2829 {
2830 control->BraceHighlight(currPos, newPos);
2831 const int currColum = control->GetColumn(currPos);
2832 const int newColum = control->GetColumn(newPos);
2833 control->SetHighlightGuide((currColum < newColum) ? currColum :newColum);
2834 }
2835 else
2836 {
2837 control->BraceBadLight(currPos);
2838 }
2839 }
2840 else
2841 {
2842 control->BraceHighlight(-1, -1);
2843 }
2844 }
2845
GetLineIndentInSpaces(int line) const2846 int cbEditor::GetLineIndentInSpaces(int line) const
2847 {
2848 cbStyledTextCtrl* control = GetControl();
2849 int currLine = (line == -1)
2850 ? control->LineFromPosition(control->GetCurrentPos())
2851 : line;
2852 wxString text = control->GetLine(currLine);
2853 unsigned int len = text.Length();
2854 int spaceCount = 0;
2855 for (unsigned int i = 0; i < len; ++i)
2856 {
2857 if (text[i] == _T(' '))
2858 {
2859 ++spaceCount;
2860 }
2861 else if (text[i] == _T('\t'))
2862 {
2863 spaceCount += control->GetTabWidth();
2864 }
2865 else
2866 {
2867 break;
2868 }
2869 }
2870 return spaceCount;
2871 }
2872
GetLineIndentString(int line) const2873 wxString cbEditor::GetLineIndentString(int line) const
2874 {
2875 return cbEditorInternalData::GetLineIndentString(line, GetControl());
2876 }
2877
2878 // Creates a submenu for a Context Menu based on the submenu's specific Id
CreateContextSubMenu(long id)2879 wxMenu* cbEditor::CreateContextSubMenu(long id)
2880 {
2881 wxMenu* menu = nullptr;
2882 if (id == idInsert)
2883 {
2884 menu = new wxMenu;
2885 menu->Append(idEmptyMenu, _("Empty"));
2886 menu->Enable(idEmptyMenu, false);
2887 }
2888 else if (id == idEdit)
2889 {
2890 menu = new wxMenu;
2891 menu->Append(idUndo, _("Undo"));
2892 menu->Append(idRedo, _("Redo"));
2893 menu->Append(idClearHistory, _("Clear changes history"));
2894 menu->AppendSeparator();
2895 menu->Append(idDelete, _("Delete"));
2896 menu->Append(idSelectAll, _("Select all"));
2897 menu->AppendSeparator();
2898 menu->Append(idUpperCase, _("UPPERCASE"));
2899 menu->Append(idLowerCase, _("lowercase"));
2900
2901 cbStyledTextCtrl* control = GetControl();
2902
2903 menu->Enable(idUndo, control->CanUndo());
2904 menu->Enable(idRedo, control->CanRedo());
2905 menu->Enable(idClearHistory, control->CanUndo() || control->CanRedo());
2906
2907 const bool hasSelection = !control->GetSelectionEmpty();
2908 menu->Enable(idDelete, !control->GetReadOnly() && hasSelection);
2909 menu->Enable(idUpperCase, !control->GetReadOnly() && hasSelection);
2910 menu->Enable(idLowerCase, !control->GetReadOnly() && hasSelection);
2911 }
2912 else if (id == idBookmarks)
2913 {
2914 menu = new wxMenu;
2915 menu->Append(idBookmarksToggle, _("Toggle bookmark"));
2916 menu->Append(idBookmarksPrevious, _("Goto previous bookmark"));
2917 menu->Append(idBookmarksNext, _("Goto next bookmark"));
2918 menu->Append(idBookmarksClearAll, _("Clear all bookmarks"));
2919 }
2920 else if (id == idFolding)
2921 {
2922 menu = new wxMenu;
2923 menu->Append(idFoldingFoldAll, _("Fold all"));
2924 menu->Append(idFoldingUnfoldAll, _("Unfold all"));
2925 menu->Append(idFoldingToggleAll, _("Toggle all"));
2926 menu->AppendSeparator();
2927 menu->Append(idFoldingFoldCurrent, _("Fold current block"));
2928 menu->Append(idFoldingUnfoldCurrent, _("Unfold current block"));
2929 menu->Append(idFoldingToggleCurrent, _("Toggle current block"));
2930 }
2931 else
2932 menu = EditorBase::CreateContextSubMenu(id);
2933
2934 return menu;
2935 }
2936
2937 // Adds menu items to context menu (both before and after loading plugins' items)
AddToContextMenu(wxMenu * popup,ModuleType type,bool pluginsdone)2938 void cbEditor::AddToContextMenu(wxMenu* popup,ModuleType type,bool pluginsdone)
2939 {
2940 PluginManager *pluginManager = Manager::Get()->GetPluginManager();
2941 bool noeditor = (type != mtEditorManager);
2942 if (!pluginsdone)
2943 {
2944 wxMenu *bookmarks = nullptr, *folding = nullptr, *editsubmenu = nullptr, *insert = nullptr;
2945 if (!noeditor)
2946 {
2947 insert = CreateContextSubMenu(idInsert);
2948 editsubmenu = CreateContextSubMenu(idEdit);
2949 bookmarks = CreateContextSubMenu(idBookmarks);
2950 if (Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/folding/show_folds"), false))
2951 folding = CreateContextSubMenu(idFolding);
2952 }
2953
2954 if (editsubmenu)
2955 {
2956 popup->Append(idCut, _("Cut"));
2957 popup->Append(idCopy, _("Copy"));
2958 popup->Append(idPaste, _("Paste"));
2959 popup->Append(idEdit, _("Edit"), editsubmenu);
2960 popup->AppendSeparator();
2961
2962 cbStyledTextCtrl* control = GetControl();
2963 const bool hasSelection = !control->GetSelectionEmpty();
2964 popup->Enable(idCut, !control->GetReadOnly() && hasSelection);
2965 popup->Enable(idCopy, hasSelection);
2966
2967 if (platform::gtk) // a wxGTK bug causes the triggering of unexpected events
2968 popup->Enable(idPaste, !control->GetReadOnly());
2969 else
2970 popup->Enable(idPaste, !control->GetReadOnly() && control->CanPaste());
2971 pluginManager->RegisterLastNonPluginMenuItem(idPaste);
2972 }
2973 if (insert)
2974 {
2975 popup->Append(idInsert, _("Insert/Refactor"), insert);
2976 pluginManager->RegisterLastNonPluginMenuItem(idInsert);
2977 }
2978 if (bookmarks)
2979 {
2980 popup->Append(idBookmarks, _("Bookmarks"), bookmarks);
2981 pluginManager->RegisterLastNonPluginMenuItem(idBookmarks);
2982 }
2983 if (folding)
2984 {
2985 popup->Append(idFolding, _("Folding"), folding);
2986 pluginManager->RegisterLastNonPluginMenuItem(idFolding);
2987 }
2988
2989 if (insert || bookmarks || folding)
2990 popup->AppendSeparator();
2991 }
2992 else
2993 {
2994 if (!noeditor && !m_pData->GetUrl().IsEmpty())
2995 {
2996 popup->InsertSeparator(0);
2997 popup->Insert(0, idOpenUrl, _("Open link in browser"));
2998 pluginManager->RegisterFindMenuItems(true, 2);
2999 }
3000 // remove "Insert/Empty" if more than one entry
3001 wxMenu* insert = nullptr;
3002 wxMenuItem* insertitem = popup->FindItem(idInsert);
3003 if (insertitem)
3004 insert = insertitem->GetSubMenu();
3005 if (insert)
3006 {
3007 if (insert->GetMenuItemCount() > 1)
3008 insert->Delete(idEmptyMenu);
3009 }
3010 }
3011 }
3012
OnBeforeBuildContextMenu(const wxPoint & position,ModuleType type)3013 bool cbEditor::OnBeforeBuildContextMenu(const wxPoint& position, ModuleType type)
3014 {
3015 bool noeditor = (type != mtEditorManager);
3016 if (!noeditor && position!=wxDefaultPosition)
3017 {
3018 // right mouse click inside the editor
3019
3020 // because here the focus has not switched yet (i.e. the left control has the focus,
3021 // but the user right-clicked inside the right control), we find out the active control differently...
3022 wxPoint clientpos(ScreenToClient(position));
3023 const int margin = m_pControl->GetMarginWidth(C_LINE_MARGIN) + // numbers, if present
3024 m_pControl->GetMarginWidth(C_MARKER_MARGIN) + // breakpoints, bookmarks... if present
3025 m_pControl->GetMarginWidth(C_FOLDING_MARGIN) + // folding, if present
3026 m_pControl->GetMarginWidth(C_CHANGEBAR_MARGIN); // changebar, if present
3027 wxRect r = m_pControl->GetRect();
3028
3029 bool inside1 = r.Contains(clientpos);
3030
3031 cbStyledTextCtrl* control = !m_pControl2 || inside1 ? m_pControl : m_pControl2;
3032 // control->SetFocus();
3033
3034 clientpos = control->ScreenToClient(position);
3035 if (clientpos.x < margin)
3036 {
3037 // keep the line
3038 int pos = control->PositionFromPoint(clientpos);
3039 m_pData->m_LastMarginMenuLine = control->LineFromPosition(pos);
3040
3041 // create special menu
3042 wxMenu* popup = new wxMenu;
3043
3044 cbDebuggerPlugin *plugin = Manager::Get()->GetDebuggerManager()->GetActiveDebugger();
3045 if (plugin && plugin->SupportsFeature(cbDebuggerFeature::Breakpoints))
3046 {
3047 bool hasBreak = LineHasMarker(BREAKPOINT_MARKER, m_pData->m_LastMarginMenuLine);
3048 bool hasBreakDisabled = LineHasMarker(BREAKPOINT_DISABLED_MARKER, m_pData->m_LastMarginMenuLine);
3049
3050 if (hasBreak || hasBreakDisabled)
3051 {
3052 popup->Append(idBreakpointEdit, _("Edit breakpoint"));
3053 popup->Append(idBreakpointRemove, _("Remove breakpoint"));
3054 if (hasBreak)
3055 popup->Append(idBreakpointDisable, _("Disable breakpoint"));
3056 if (hasBreakDisabled)
3057 popup->Append(idBreakpointEnable, _("Enable breakpoint"));
3058 }
3059 else
3060 popup->Append(idBreakpointAdd, _("Add breakpoint"));
3061 popup->AppendSeparator();
3062 }
3063
3064 if (LineHasMarker(BOOKMARK_MARKER, m_pData->m_LastMarginMenuLine))
3065 {
3066 popup->Append(idBookmarkRemove, _("Remove bookmark"));
3067 }
3068 else
3069 {
3070 popup->Append(idBookmarkAdd, _("Add bookmark"));
3071 }
3072
3073 popup->Append(idBookmarkRemoveAll, _("Remove all bookmark"));
3074
3075 // display menu... wxWindows help says not to force the position
3076 PopupMenu(popup);
3077
3078 delete popup;
3079 return false;
3080 }
3081
3082 // before the context menu creation, move the caret to where mouse is
3083
3084 // get caret position and line from mouse cursor
3085 const int pos = control->PositionFromPoint(control->ScreenToClient(wxGetMousePosition()));
3086
3087 // this re-enables 1-click "Find declaration of..."
3088 // but avoids losing selection for cut/copy
3089 if (control->GetSelectionStart() > pos ||
3090 control->GetSelectionEnd() < pos)
3091 {
3092 control->GotoPos(pos);
3093 }
3094 }
3095
3096 // follow default strategy
3097 return EditorBase::OnBeforeBuildContextMenu(position, type);
3098 }
3099
OnAfterBuildContextMenu(cb_unused ModuleType type)3100 void cbEditor::OnAfterBuildContextMenu(cb_unused ModuleType type)
3101 {
3102 // we don't care
3103 }
3104
Print(bool selectionOnly,PrintColourMode pcm,bool line_numbers)3105 void cbEditor::Print(bool selectionOnly, PrintColourMode pcm, bool line_numbers)
3106 {
3107 cbStyledTextCtrl * control = GetControl();
3108 if (!control)
3109 return;
3110
3111 // Remember same settings, so we can restore them.
3112 int oldMarginWidth = control->GetMarginWidth(C_LINE_MARGIN);
3113 int oldMarginType = control->GetMarginType(C_LINE_MARGIN);
3114 int oldEdgeMode = control->GetEdgeMode();
3115
3116 // print line numbers?
3117 control->SetMarginType(C_LINE_MARGIN, wxSCI_MARGIN_NUMBER);
3118 if (!line_numbers)
3119 {
3120 control->SetPrintMagnification(-1);
3121 control->SetMarginWidth(C_LINE_MARGIN, 0);
3122 }
3123 else
3124 {
3125 control->SetPrintMagnification(-2);
3126 control->SetMarginWidth(C_LINE_MARGIN, 1);
3127 }
3128 // never print the gutter line
3129 control->SetEdgeMode(wxSCI_EDGE_NONE);
3130
3131 switch (pcm)
3132 {
3133 case pcmAsIs:
3134 control->SetPrintColourMode(wxSCI_PRINT_NORMAL);
3135 break;
3136 case pcmBlackAndWhite:
3137 control->SetPrintColourMode(wxSCI_PRINT_BLACKONWHITE);
3138 break;
3139 case pcmColourOnWhite:
3140 control->SetPrintColourMode(wxSCI_PRINT_COLOURONWHITE);
3141 break;
3142 case pcmInvertColours:
3143 control->SetPrintColourMode(wxSCI_PRINT_INVERTLIGHT);
3144 break;
3145 default:
3146 break;
3147 }
3148 InitPrinting();
3149 cbEditorPrintout printout(m_Filename, control, selectionOnly);
3150 if (!g_printer->Print(this, &printout, true))
3151 {
3152 if (wxPrinter::GetLastError() == wxPRINTER_ERROR)
3153 {
3154 cbMessageBox(_("There was a problem printing.\n"
3155 "Perhaps your current printer is not set correctly?"), _("Printing"), wxICON_ERROR);
3156 DeInitPrinting();
3157 }
3158 }
3159 else
3160 {
3161 wxPrintData* ppd = &(g_printer->GetPrintDialogData().GetPrintData());
3162 Manager::Get()->GetConfigManager(_T("app"))->Write(_T("/printerdialog/paperid"), (int)ppd->GetPaperId());
3163 Manager::Get()->GetConfigManager(_T("app"))->Write(_T("/printerdialog/paperorientation"), (int)ppd->GetOrientation());
3164 }
3165
3166 // revert line number settings
3167 control->SetMarginType(C_LINE_MARGIN, oldMarginType);
3168 control->SetMarginWidth(C_LINE_MARGIN, oldMarginWidth);
3169
3170 // revert gutter settings
3171 control->SetEdgeMode(oldEdgeMode);
3172
3173 // restore line numbers if needed
3174 m_pData->SetLineNumberColWidth(m_pControl && m_pControl2);
3175 }
3176
3177 // events
3178
OnContextMenuEntry(wxCommandEvent & event)3179 void cbEditor::OnContextMenuEntry(wxCommandEvent& event)
3180 {
3181 cbStyledTextCtrl* control = GetControl();
3182
3183 // we have a single event handler for all popup menu entries,
3184 // so that we can add/remove options without the need to recompile
3185 // the whole project (almost) but more importantly, to
3186 // *not* break cbEditor's interface for such a trivial task...
3187 const int id = event.GetId();
3188
3189 if (id == idUndo)
3190 control->Undo();
3191 else if (id == idRedo)
3192 control->Redo();
3193 else if (id == idClearHistory)
3194 control->EmptyUndoBuffer(Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/margin/use_changebar"), true));
3195 else if (id == idCut)
3196 control->Cut();
3197 else if (id == idCopy)
3198 control->Copy();
3199 else if (id == idPaste)
3200 control->Paste();
3201 else if (id == idDelete)
3202 control->ReplaceSelection(wxEmptyString);
3203 else if (id == idUpperCase)
3204 control->UpperCase();
3205 else if (id == idLowerCase)
3206 control->LowerCase();
3207 else if (id == idSelectAll)
3208 control->SelectAll();
3209 else if (id == idSwapHeaderSource)
3210 Manager::Get()->GetEditorManager()->SwapActiveHeaderSource();
3211 else if (id == idOpenContainingFolder)
3212 Manager::Get()->GetEditorManager()->OpenContainingFolder();
3213 else if (id == idBookmarkAdd)
3214 control->MarkerAdd(m_pData->m_LastMarginMenuLine, BOOKMARK_MARKER);
3215 else if (id == idBookmarkRemove)
3216 control->MarkerDelete(m_pData->m_LastMarginMenuLine, BOOKMARK_MARKER);
3217 else if (id == idBookmarkRemoveAll)
3218 control->MarkerDeleteAll(BOOKMARK_MARKER);
3219 else if (id == idBookmarksToggle)
3220 MarkerToggle(BOOKMARK_MARKER);
3221 else if (id == idBookmarksNext)
3222 MarkerNext(BOOKMARK_MARKER);
3223 else if (id == idBookmarksPrevious)
3224 MarkerPrevious(BOOKMARK_MARKER);
3225 else if (id == idBookmarksClearAll)
3226 control->MarkerDeleteAll(BOOKMARK_MARKER);
3227 else if (id == idFoldingFoldAll)
3228 FoldAll();
3229 else if (id == idFoldingUnfoldAll)
3230 UnfoldAll();
3231 else if (id == idFoldingToggleAll)
3232 ToggleAllFolds();
3233 else if (id == idFoldingFoldCurrent)
3234 FoldBlockFromLine();
3235 else if (id == idFoldingUnfoldCurrent)
3236 UnfoldBlockFromLine();
3237 else if (id == idFoldingToggleCurrent)
3238 ToggleFoldBlockFromLine();
3239 else if (id == idOpenUrl)
3240 wxLaunchDefaultBrowser(m_pData->GetUrl());
3241 else if (id == idSplitHorz)
3242 Split(stHorizontal);
3243 else if (id == idSplitVert)
3244 Split(stVertical);
3245 else if (id == idUnsplit)
3246 Unsplit();
3247 else if (id == idProperties)
3248 {
3249 if (m_pProjectFile)
3250 m_pProjectFile->ShowOptions(this);
3251 else
3252 {
3253 // active editor not-in-project
3254 ProjectFileOptionsDlg dlg(this, GetFilename());
3255 PlaceWindow(&dlg);
3256 dlg.ShowModal();
3257 }
3258 }
3259 else if (id == idAddFileToProject)
3260 {
3261 cbProject *prj = Manager::Get()->GetProjectManager()->GetActiveProject();
3262
3263 wxArrayInt targets;
3264 if (Manager::Get()->GetProjectManager()->AddFileToProject(m_Filename, prj, targets) != 0)
3265 {
3266 ProjectFile* pf = prj->GetFileByFilename(m_Filename, false);
3267 SetProjectFile(pf);
3268 Manager::Get()->GetProjectManager()->GetUI().RebuildTree();
3269 }
3270 }
3271 else if (id == idRemoveFileFromProject)
3272 {
3273 if (m_pProjectFile)
3274 {
3275 cbProject *prj = m_pProjectFile->GetParentProject();
3276 Manager::Get()->GetProjectManager()->RemoveFileFromProject(m_pProjectFile, prj);
3277 Manager::Get()->GetProjectManager()->GetUI().RebuildTree();
3278 }
3279 }
3280 else if (id == idShowFileInProject)
3281 {
3282 cbProjectManagerUI& ui = Manager::Get()->GetProjectManager()->GetUI();
3283 ui.SwitchToProjectsPage();
3284 ui.ShowFileInTree(*m_pProjectFile);
3285 }
3286 else if (id == idBreakpointAdd)
3287 AddBreakpoint(m_pData->m_LastMarginMenuLine);
3288 else if (id == idBreakpointEdit)
3289 {
3290 cbBreakpointsDlg *dialog = Manager::Get()->GetDebuggerManager()->GetBreakpointDialog();
3291 dialog->EditBreakpoint(m_Filename, m_pData->m_LastMarginMenuLine + 1);
3292 }
3293 else if (id == idBreakpointRemove)
3294 RemoveBreakpoint(m_pData->m_LastMarginMenuLine);
3295 else if (id == idBreakpointEnable)
3296 {
3297 cbBreakpointsDlg *dialog = Manager::Get()->GetDebuggerManager()->GetBreakpointDialog();
3298 dialog->EnableBreakpoint(m_Filename, m_pData->m_LastMarginMenuLine + 1, true);
3299 }
3300 else if (id == idBreakpointDisable)
3301 {
3302 cbBreakpointsDlg *dialog = Manager::Get()->GetDebuggerManager()->GetBreakpointDialog();
3303 dialog->EnableBreakpoint(m_Filename, m_pData->m_LastMarginMenuLine + 1, false);
3304 }
3305 else
3306 event.Skip();
3307 //Manager::Get()->GetLogManager()->DebugLog(_T("Leaving OnContextMenuEntry"));
3308 }
3309
OnMarginClick(wxScintillaEvent & event)3310 void cbEditor::OnMarginClick(wxScintillaEvent& event)
3311 {
3312 switch (event.GetMargin())
3313 {
3314 case C_MARKER_MARGIN: // bookmarks and breakpoints margin
3315 {
3316 int lineYpix = event.GetPosition();
3317 int line = GetControl()->LineFromPosition(lineYpix);
3318
3319 ToggleBreakpoint(line);
3320 break;
3321 }
3322 case C_FOLDING_MARGIN: // folding margin
3323 {
3324 int lineYpix = event.GetPosition();
3325 int line = GetControl()->LineFromPosition(lineYpix);
3326
3327 GetControl()->ToggleFold(line);
3328 break;
3329 }
3330 default:
3331 break;
3332 }
3333 OnScintillaEvent(event);
3334 }
3335
OnEditorUpdateUI(wxScintillaEvent & event)3336 void cbEditor::OnEditorUpdateUI(wxScintillaEvent& event)
3337 {
3338 if (Manager::Get()->GetEditorManager()->GetActiveEditor() == this)
3339 {
3340 NotifyPlugins(cbEVT_EDITOR_UPDATE_UI);
3341 HighlightBraces(); // brace highlighting
3342 }
3343 OnScintillaEvent(event);
3344 }
3345
OnEditorChange(wxScintillaEvent & event)3346 void cbEditor::OnEditorChange(wxScintillaEvent& event)
3347 {
3348 SetModified(m_pControl->GetModify());
3349 OnScintillaEvent(event);
3350 }
3351
OnEditorCharAdded(wxScintillaEvent & event)3352 void cbEditor::OnEditorCharAdded(wxScintillaEvent& event)
3353 {
3354 // if message manager is auto-hiding, this will close it if not needed open
3355 // Manager::Get()->GetLogManager()->Close();
3356
3357 m_autoIndentDone = false;
3358 OnScintillaEvent(event); // smart indent plugins will be called here
3359 if (!m_autoIndentDone)
3360 {
3361 const wxChar ch = event.GetKey();
3362 cbStyledTextCtrl* control = GetControl();
3363 // auto indent
3364 if ( (ch == _T('\n')) || ( (control->GetEOLMode() == wxSCI_EOL_CR) && (ch == _T('\r')) ) )
3365 {
3366 const int pos = control->GetCurrentPos();
3367 const int currLine = control->LineFromPosition(pos);
3368 const bool autoIndent = Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/auto_indent"), true);
3369 if (autoIndent && currLine > 0)
3370 {
3371 wxString indent;
3372 if (control->GetCurLine().Trim().IsEmpty())
3373 {
3374 // copy the indentation of the last non-empty line
3375 for (int i = currLine - 1; i >= 0; --i)
3376 {
3377 const wxString& prevLineStr = control->GetLine(i);
3378 if (!(prevLineStr.IsEmpty() || prevLineStr[0] == _T('\n') || prevLineStr[0] == _T('\r')))
3379 {
3380 indent = GetLineIndentString(i);
3381 break;
3382 }
3383 }
3384 }
3385 else
3386 indent = GetLineIndentString(currLine - 1);
3387 if (!indent.IsEmpty())
3388 {
3389 control->BeginUndoAction();
3390
3391 control->InsertText(pos, indent);
3392 control->GotoPos(pos + indent.Length());
3393 control->ChooseCaretX();
3394
3395 control->EndUndoAction();
3396 }
3397 }
3398 }
3399
3400 // selection brace completion
3401 bool braceCompleted = false;
3402 if ( Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/selection_brace_completion"), false)
3403 || control->IsBraceShortcutActive() )
3404 {
3405 braceCompleted = control->DoSelectionBraceCompletion(ch);
3406 }
3407
3408 // brace completion
3409 if ( !braceCompleted
3410 && Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/brace_completion"), true) )
3411 {
3412 control->DoBraceCompletion(ch);
3413 }
3414 }
3415 }
3416
AutoIndentDone()3417 void cbEditor::AutoIndentDone()
3418 {
3419 m_autoIndentDone = true;
3420 }
3421
OnEditorDwellStart(wxScintillaEvent & event)3422 void cbEditor::OnEditorDwellStart(wxScintillaEvent& event)
3423 {
3424 if ( !wxTheApp->IsActive() )
3425 return;
3426
3427 cbStyledTextCtrl* control = GetControl();
3428 if (!control)
3429 return;
3430
3431 wxRect screenRect = control->GetScreenRect();
3432 wxPoint ptEvent(event.GetX(), event.GetY());
3433 ptEvent = control->ClientToScreen(ptEvent);
3434 wxPoint ptScreen = wxGetMousePosition();
3435 wxPoint ptClient = control->ScreenToClient(ptScreen);
3436
3437 double distance = sqrt( (ptScreen.x - ptEvent.x) * (ptScreen.x - ptEvent.x)
3438 + (ptScreen.y - ptEvent.y) * (ptScreen.y - ptEvent.y) );
3439 if (!screenRect.Contains(ptScreen) || distance > 10)
3440 return;
3441
3442 int pos = control->PositionFromPoint(ptClient);
3443 int style = control->GetStyleAt(pos);
3444 NotifyPlugins(cbEVT_EDITOR_TOOLTIP, style, wxEmptyString, ptClient.x, ptClient.y);
3445 wxScintillaEvent newEvent(event);
3446 newEvent.SetX(ptClient.x);
3447 newEvent.SetY(ptClient.y);
3448 OnScintillaEvent(event);
3449 }
3450
OnEditorDwellEnd(wxScintillaEvent & event)3451 void cbEditor::OnEditorDwellEnd(wxScintillaEvent& event)
3452 {
3453 NotifyPlugins(cbEVT_EDITOR_TOOLTIP_CANCEL);
3454 OnScintillaEvent(event);
3455 }
3456
OnEditorModified(wxScintillaEvent & event)3457 void cbEditor::OnEditorModified(wxScintillaEvent& event)
3458 {
3459 // wxString txt = _T("OnEditorModified(): ");
3460 // int flags = event.GetModificationType();
3461 // if (flags & wxSCI_MOD_CHANGEMARKER) txt << _T("wxSCI_MOD_CHANGEMARKER, ");
3462 // if (flags & wxSCI_MOD_INSERTTEXT) txt << _T("wxSCI_MOD_INSERTTEXT, ");
3463 // if (flags & wxSCI_MOD_DELETETEXT) txt << _T("wxSCI_MOD_DELETETEXT, ");
3464 // if (flags & wxSCI_MOD_CHANGEFOLD) txt << _T("wxSCI_MOD_CHANGEFOLD, ");
3465 // if (flags & wxSCI_PERFORMED_USER) txt << _T("wxSCI_PERFORMED_USER, ");
3466 // if (flags & wxSCI_MOD_BEFOREINSERT) txt << _T("wxSCI_MOD_BEFOREINSERT, ");
3467 // if (flags & wxSCI_MOD_BEFOREDELETE) txt << _T("wxSCI_MOD_BEFOREDELETE, ");
3468 // txt << _T("pos=")
3469 // << wxString::Format(_T("%d"), event.GetPosition())
3470 // << _T(", line=")
3471 // << wxString::Format(_T("%d"), event.GetLine())
3472 // << _T(", linesAdded=")
3473 // << wxString::Format(_T("%d"), event.GetLinesAdded());
3474 // Manager::Get()->GetLogManager()->DebugLog(txt);
3475
3476 // whenever event.GetLinesAdded() != 0, we must re-set breakpoints for lines greater
3477 // than LineFromPosition(event.GetPosition())
3478 int linesAdded = event.GetLinesAdded();
3479 bool isAdd = event.GetModificationType() & wxSCI_MOD_INSERTTEXT;
3480 bool isDel = event.GetModificationType() & wxSCI_MOD_DELETETEXT;
3481 if ((isAdd || isDel) && linesAdded != 0)
3482 {
3483 // whether to show line-numbers or not is handled in SetLineNumberColWidth() now
3484 m_pData->SetLineNumberColWidth();
3485
3486 // NB: I don't think polling for each debugger every time will slow things down enough
3487 // to worry about unless there are automated tasks that call this routine regularly
3488 //
3489 // well, scintilla events happen regularly
3490 // although we only reach this part of the code only if a line has been added/removed
3491 // so, yes, it might not be that bad after all
3492 int startline = m_pControl->LineFromPosition(event.GetPosition());
3493 if (m_pControl == event.GetEventObject())
3494 {
3495 const DebuggerManager::RegisteredPlugins &plugins = Manager::Get()->GetDebuggerManager()->GetAllDebuggers();
3496 cbDebuggerPlugin *active = Manager::Get()->GetDebuggerManager()->GetActiveDebugger();
3497 for (DebuggerManager::RegisteredPlugins::const_iterator it = plugins.begin(); it != plugins.end(); ++it)
3498 {
3499 if (it->first != active)
3500 it->first->EditorLinesAddedOrRemoved(this, startline + 1, linesAdded);
3501 }
3502 if (active)
3503 active->EditorLinesAddedOrRemoved(this, startline + 1, linesAdded);
3504
3505 cbBreakpointsDlg *dlg = Manager::Get()->GetDebuggerManager()->GetBreakpointDialog();
3506 if (dlg)
3507 dlg->Reload();
3508 RefreshBreakpointMarkers();
3509 }
3510 }
3511 // If we remove the folding-point (the brace or whatever) from a folded block,
3512 // we have to make the hidden lines visible, otherwise, they
3513 // will no longer be reachable, until the editor is closed and reopened again
3514 if ( (event.GetModificationType() & wxSCI_MOD_CHANGEFOLD)
3515 && (event.GetFoldLevelPrev() & wxSCI_FOLDLEVELHEADERFLAG) )
3516 {
3517 cbStyledTextCtrl* control = GetControl();
3518 int line = event.GetLine();
3519 if (! control->GetFoldExpanded(line))
3520 {
3521 control->SetFoldExpanded(line, true);
3522 control->ShowLines(line, control->GetLastChild(line, -1));
3523 }
3524 }
3525 OnScintillaEvent(event);
3526 } // end of OnEditorModified
3527
OnUserListSelection(wxScintillaEvent & event)3528 void cbEditor::OnUserListSelection(wxScintillaEvent& event)
3529 {
3530 OnScintillaEvent(event);
3531 }
3532
OnClose(cb_unused wxCloseEvent & event)3533 void cbEditor::OnClose(cb_unused wxCloseEvent& event)
3534 {
3535 Manager::Get()->GetEditorManager()->Close(this);
3536 }
3537
DoIndent()3538 void cbEditor::DoIndent()
3539 {
3540 cbStyledTextCtrl* control = GetControl();
3541 if (control)
3542 control->SendMsg(wxSCI_CMD_TAB);
3543 }
3544
DoUnIndent()3545 void cbEditor::DoUnIndent()
3546 {
3547 cbStyledTextCtrl* control = GetControl();
3548 if (control)
3549 control->SendMsg(wxSCI_CMD_BACKTAB);
3550 }
3551
OnZoom(wxScintillaEvent & event)3552 void cbEditor::OnZoom(wxScintillaEvent& event)
3553 {
3554 ConfigManager* mgr = Manager::Get()->GetConfigManager(_T("editor"));
3555
3556 int zoom = GetControl()->GetZoom();
3557 Manager::Get()->GetEditorManager()->SetZoom(zoom);
3558 // if all editors should be zoomed, we call cbAuiNotebooks SetZoom()
3559 bool both = mgr->ReadBool(_T("/zoom_all"));
3560 if (both)
3561 Manager::Get()->GetEditorManager()->GetNotebook()->SetZoom(zoom);
3562
3563 m_pData->SetLineNumberColWidth(both);
3564
3565 if (mgr->ReadBool(_T("/folding/show_folds"), true))
3566 m_pData->SetColumnWidth(C_FOLDING_MARGIN, foldingMarginBaseWidth, 1, both);
3567
3568 if (mgr->ReadBool(_T("/margin/use_changebar"), true))
3569 m_pData->SetColumnWidth(C_CHANGEBAR_MARGIN, changeBarMarginBaseWidth, 1, both);
3570
3571 m_pData->SetMarkerColumnWidth(both);
3572
3573 OnScintillaEvent(event);
3574 }
3575
3576 // used to set zoom for both (if splitted) or just the last active control,
3577 // called from cbAuiNotebook
SetZoom(int zoom,bool both)3578 void cbEditor::SetZoom(int zoom, bool both)
3579 {
3580 if (both)
3581 {
3582 if (m_pControl->GetZoom() != zoom)
3583 m_pControl->SetZoom(zoom);
3584 if (m_pControl2 && (m_pControl2->GetZoom() != zoom))
3585 m_pControl2->SetZoom(zoom);
3586 }
3587 else
3588 {
3589 if (GetControl()->GetZoom() != zoom)
3590 GetControl()->SetZoom(zoom);
3591 }
3592 }
3593
3594 // generic scintilla event handler
OnScintillaEvent(wxScintillaEvent & event)3595 void cbEditor::OnScintillaEvent(wxScintillaEvent& event)
3596 {
3597 // wxString txt;
3598 // wxEventType type = event.GetEventType();
3599 // if (type == wxEVT_SCI_CHANGE) txt << _T("wxEVT_SCI_CHANGE");
3600 // else if (type == wxEVT_SCI_STYLENEEDED) txt << _T("wxEVT_SCI_STYLENEEDED");
3601 // else if (type == wxEVT_SCI_CHARADDED) txt << _T("wxEVT_SCI_CHARADDED");
3602 // else if (type == wxEVT_SCI_SAVEPOINTREACHED) txt << _T("wxEVT_SCI_SAVEPOINTREACHED");
3603 // else if (type == wxEVT_SCI_SAVEPOINTLEFT) txt << _T("wxEVT_SCI_SAVEPOINTLEFT");
3604 // else if (type == wxEVT_SCI_ROMODIFYATTEMPT) txt << _T("wxEVT_SCI_ROMODIFYATTEMPT");
3605 // else if (type == wxEVT_SCI_KEY) txt << _T("wxEVT_SCI_KEY");
3606 // else if (type == wxEVT_SCI_DOUBLECLICK) txt << _T("wxEVT_SCI_DOUBLECLICK");
3607 // else if (type == wxEVT_SCI_UPDATEUI) txt << _T("wxEVT_SCI_UPDATEUI");
3608 // else if (type == wxEVT_SCI_MODIFIED) txt << _T("wxEVT_SCI_MODIFIED");
3609 // else if (type == wxEVT_SCI_MACRORECORD) txt << _T("wxEVT_SCI_MACRORECORD");
3610 // else if (type == wxEVT_SCI_MARGINCLICK) txt << _T("wxEVT_SCI_MARGINCLICK");
3611 // else if (type == wxEVT_SCI_NEEDSHOWN) txt << _T("wxEVT_SCI_NEEDSHOWN");
3612 // else if (type == wxEVT_SCI_PAINTED) txt << _T("wxEVT_SCI_PAINTED");
3613 // else if (type == wxEVT_SCI_USERLISTSELECTION) txt << _T("wxEVT_SCI_USERLISTSELECTION");
3614 // else if (type == wxEVT_SCI_URIDROPPED) txt << _T("wxEVT_SCI_URIDROPPED");
3615 // else if (type == wxEVT_SCI_DWELLSTART) txt << _T("wxEVT_SCI_DWELLSTART");
3616 // else if (type == wxEVT_SCI_DWELLEND) txt << _T("wxEVT_SCI_DWELLEND");
3617 // else if (type == wxEVT_SCI_START_DRAG) txt << _T("wxEVT_SCI_START_DRAG");
3618 // else if (type == wxEVT_SCI_DRAG_OVER) txt << _T("wxEVT_SCI_DRAG_OVER");
3619 // else if (type == wxEVT_SCI_DO_DROP) txt << _T("wxEVT_SCI_DO_DROP");
3620 // else if (type == wxEVT_SCI_ZOOM) txt << _T("wxEVT_SCI_ZOOM");
3621 // else if (type == wxEVT_SCI_HOTSPOT_CLICK) txt << _T("wxEVT_SCI_HOTSPOT_CLICK");
3622 // else if (type == wxEVT_SCI_HOTSPOT_DCLICK) txt << _T("wxEVT_SCI_HOTSPOT_DCLICK");
3623 // else if (type == wxEVT_SCI_CALLTIP_CLICK) txt << _T("wxEVT_SCI_CALLTIP_CLICK");
3624 // else if (type == wxEVT_SCI_AUTOCOMP_SELECTION) txt << _T("wxEVT_SCI_AUTOCOMP_SELECTION");
3625 // else if (type == wxEVT_SCI_INDICATOR_CLICK) txt << _T("wxEVT_SCI_INDICATOR_CLICK");
3626 // else if (type == wxEVT_SCI_INDICATOR_RELEASE) txt << _T("wxEVT_SCI_INDICATOR_RELEASE");
3627 // Manager::Get()->GetLogManager()->DebugLog(txt);
3628
3629 // call any hooked functors
3630 if (!ProjectManager::IsBusy() && EditorHooks::HasRegisteredHooks())
3631 {
3632 EditorHooks::CallHooks(this, event);
3633 }
3634 }
3635
CanSelectAll() const3636 bool cbEditor::CanSelectAll() const
3637 {
3638 int res = 0;
3639 cbStyledTextCtrl* control = GetControl();
3640 if (control)
3641 res = control->GetLength();
3642 return res > 0;
3643 }
3644
SelectAll()3645 void cbEditor::SelectAll()
3646 {
3647 cbStyledTextCtrl* control = GetControl();
3648 if (control)
3649 control->SelectAll();
3650 }
3651