1 /*
2 SuperCollider Qt IDE
3 Copyright (c) 2012 Jakob Leben & Tim Blechmann
4 http://www.audiosynth.com
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include "multi_editor.hpp"
22 #include "editor_box.hpp"
23 #include "main_window.hpp"
24 #include "lookup_dialog.hpp"
25 #include "code_editor/sc_editor.hpp"
26 #include "util/multi_splitter.hpp"
27 #include "../core/doc_manager.hpp"
28 #include "../core/sig_mux.hpp"
29 #include "../core/main.hpp"
30 #include "../core/sc_process.hpp"
31 #include "../core/session_manager.hpp"
32
33 #include <yaml-cpp/node/node.h>
34 #include <yaml-cpp/parser.h>
35
36 #include <QApplication>
37 #include <QDebug>
38 #include <QDialog>
39 #include <QFileInfo>
40 #include <QHBoxLayout>
41 #include <QHeaderView>
42 #include <QListView>
43 #include <QMenu>
44 #include <QPainter>
45 #include <QStandardItemModel>
46 #include <QShortcut>
47 #include <QStyle>
48 #include <QTreeWidget>
49 #include <QVBoxLayout>
50
51
52 namespace ScIDE {
53
54 class DocumentSelectPopUp : public QDialog {
55 public:
DocumentSelectPopUp(const CodeEditorBox::History & history,QWidget * parent)56 DocumentSelectPopUp(const CodeEditorBox::History& history, QWidget* parent):
57 #ifndef Q_OS_MAC
58 QDialog(parent, Qt::Popup | Qt::FramelessWindowHint)
59 #else
60 QDialog(parent, Qt::Dialog | Qt::FramelessWindowHint)
61 #endif
62 {
63 mModel = new QStandardItemModel(this);
64 populateModel(history);
65
66 mListView = new QListView();
67 mListView->setModel(mModel);
68 mListView->setFrameShape(QFrame::NoFrame);
69
70 QHBoxLayout* layout = new QHBoxLayout(this);
71 layout->addWidget(mListView);
72 layout->setContentsMargins(1, 1, 1, 1);
73
74 connect(mListView, SIGNAL(activated(QModelIndex)), this, SLOT(accept()));
75
76 mListView->setFocus(Qt::OtherFocusReason);
77
78 QModelIndex nextIndex = mModel->index(1, 0);
79 mListView->setCurrentIndex(nextIndex);
80
81 mListView->setEditTriggers(QAbstractItemView::NoEditTriggers);
82 }
83
exec(const QPoint & pos)84 Document* exec(const QPoint& pos) {
85 move(pos);
86 if (QDialog::exec())
87 return currentDocument();
88 else
89 return 0;
90 }
91
92 private:
event(QEvent * event)93 bool event(QEvent* event) {
94 if (event->type() == QEvent::ShortcutOverride) {
95 event->accept();
96 return true;
97 }
98 return QWidget::event(event);
99 }
100
keyReleaseEvent(QKeyEvent * ke)101 void keyReleaseEvent(QKeyEvent* ke) {
102 // adapted from qtcreator
103 if (ke->modifiers() == 0
104 /*HACK this is to overcome some event inconsistencies between platforms*/
105 || (ke->modifiers() == Qt::AltModifier && (ke->key() == Qt::Key_Alt || ke->key() == -1))) {
106 ke->accept();
107 accept();
108 }
109 QDialog::keyReleaseEvent(ke);
110 }
111
keyPressEvent(QKeyEvent * ke)112 void keyPressEvent(QKeyEvent* ke) {
113 switch (ke->key()) {
114 case Qt::Key_Down:
115 case Qt::Key_Tab:
116 cycleDown();
117 ke->accept();
118 return;
119
120 case Qt::Key_Up:
121 case Qt::Key_Backtab:
122 cycleUp();
123 ke->accept();
124 return;
125
126 case Qt::Key_Escape:
127 reject();
128 return;
129
130 default:;
131 }
132
133 QDialog::keyPressEvent(ke);
134 }
135
paintEvent(QPaintEvent *)136 void paintEvent(QPaintEvent*) {
137 QPainter painter(this);
138 painter.setBrush(Qt::NoBrush);
139 painter.setPen(palette().color(QPalette::Dark));
140 painter.drawRect(rect().adjusted(0, 0, -1, -1));
141 }
142
cycleDown()143 void cycleDown() {
144 int row = mListView->currentIndex().row() + 1;
145 if (!mModel->hasIndex(row, 0))
146 row = 0;
147
148 QModelIndex nextIndex = mModel->index(row, 0);
149 mListView->setCurrentIndex(nextIndex);
150 }
151
cycleUp()152 void cycleUp() {
153 int row = mListView->currentIndex().row() - 1;
154 if (!mModel->hasIndex(row, 0))
155 row = mModel->rowCount() - 1;
156
157 QModelIndex nextIndex = mModel->index(row, 0);
158 mListView->setCurrentIndex(nextIndex);
159 }
160
currentDocument()161 Document* currentDocument() {
162 QStandardItem* currentItem = mModel->itemFromIndex(mListView->currentIndex());
163 return currentItem ? currentItem->data().value<Document*>() : NULL;
164 }
165
populateModel(const CodeEditorBox::History & history)166 void populateModel(const CodeEditorBox::History& history) {
167 QList<Document*> displayDocuments;
168 foreach (GenericCodeEditor* editor, history)
169 displayDocuments << editor->document();
170
171 QList<Document*> managerDocuments = Main::documentManager()->documents();
172 foreach (Document* document, managerDocuments)
173 if (!displayDocuments.contains(document))
174 displayDocuments << document;
175
176 foreach (Document* document, displayDocuments) {
177 QStandardItem* item = new QStandardItem(document->title());
178 item->setData(QVariant::fromValue(document));
179 mModel->appendRow(item);
180 }
181 }
182
183 QListView* mListView;
184 QStandardItemModel* mModel;
185 };
186
EditorTabBar(QWidget * parent)187 EditorTabBar::EditorTabBar(QWidget* parent): QTabBar(parent) {
188 setDocumentMode(true);
189 setTabsClosable(true);
190 setMovable(true);
191 setUsesScrollButtons(true);
192 setDrawBase(false);
193 setElideMode(Qt::ElideNone);
194 }
195
mousePressEvent(QMouseEvent * event)196 void EditorTabBar::mousePressEvent(QMouseEvent* event) {
197 if (event->button() == Qt::RightButton) {
198 showContextMenu(event);
199 event->accept();
200 return;
201 } else if (event->button() == Qt::MiddleButton) {
202 mTabUnderCursor = tabAt(event->pos());
203 onCloseTab();
204 event->accept();
205 return;
206 }
207
208 QTabBar::mousePressEvent(event);
209 }
210
211
mouseDoubleClickEvent(QMouseEvent * event)212 void EditorTabBar::mouseDoubleClickEvent(QMouseEvent* event) {
213 if (event->button() == Qt::LeftButton) {
214 if (tabAt(event->pos()) == -1) { // no tab under cursor
215 MainWindow::instance()->newDocument();
216 event->accept();
217 return;
218 }
219 }
220
221 QTabBar::mouseDoubleClickEvent(event);
222 }
223
showContextMenu(QMouseEvent * event)224 void EditorTabBar::showContextMenu(QMouseEvent* event) {
225 mTabUnderCursor = tabAt(event->pos());
226
227 QMenu* menu = new QMenu(this);
228 // Cannot have a close tab action if we are not over a tab
229 if (mTabUnderCursor == -1) {
230 menu->addAction(tr("Close All Tabs"), this, SLOT(onCloseOtherTabs()));
231 } else {
232 menu->addAction(tr("Close"), this, SLOT(onCloseTab()));
233 menu->addAction(tr("Close Other Tabs"), this, SLOT(onCloseOtherTabs()));
234 menu->addAction(tr("Close Tabs to the Right"), this, SLOT(onCloseTabsToTheRight()));
235 }
236
237 menu->popup(event->screenPos().toPoint());
238 }
239
onCloseTab()240 void EditorTabBar::onCloseTab() {
241 Document* doc = tabData(mTabUnderCursor).value<Document*>();
242 assert(doc);
243
244 MainWindow::close(doc);
245 }
246
onCloseOtherTabs()247 void EditorTabBar::onCloseOtherTabs() {
248 QVector<Document*> docsToClose;
249
250 for (int currentTab = 0; currentTab != count(); ++currentTab) {
251 if (currentTab != mTabUnderCursor)
252 docsToClose.append(tabData(currentTab).value<Document*>());
253 }
254
255 for (Document* doc : docsToClose)
256 MainWindow::close(doc);
257 }
258
onCloseTabsToTheRight()259 void EditorTabBar::onCloseTabsToTheRight() {
260 QVector<Document*> docsToClose;
261
262 for (int currentTab = mTabUnderCursor + 1; currentTab != count(); ++currentTab)
263 docsToClose.append(tabData(currentTab).value<Document*>());
264
265 for (Document* doc : docsToClose)
266 MainWindow::close(doc);
267 }
268
269
MultiEditor(Main * main,QWidget * parent)270 MultiEditor::MultiEditor(Main* main, QWidget* parent):
271 QWidget(parent),
272 mEditorSigMux(new SignalMultiplexer(this)),
273 mBoxSigMux(new SignalMultiplexer(this)),
274 #ifdef __APPLE__
275 mDocModifiedIcon(QApplication::style()->standardIcon(QStyle::SP_DriveHDIcon))
276 #else
277 mDocModifiedIcon(QIcon::fromTheme("document-save"))
278 #endif
279 {
280 mTabs = new EditorTabBar;
281
282 mSplitter = new MultiSplitter(this);
283 CodeEditorBox* defaultBox = newBox(mSplitter);
284
285 mSplitter->addWidget(defaultBox);
286
287 multiEditorLayout = new QVBoxLayout;
288 multiEditorLayout->setContentsMargins(0, 0, 0, 0);
289 multiEditorLayout->setSpacing(0);
290 multiEditorLayout->addWidget(mTabs);
291 multiEditorLayout->addWidget(mSplitter);
292 setLayout(multiEditorLayout);
293
294 makeSignalConnections();
295
296 connect(main, SIGNAL(applySettingsRequest(Settings::Manager*)), this, SLOT(applySettings(Settings::Manager*)));
297
298 createActions();
299
300 setCurrentBox(defaultBox); // will updateActions();
301
302 applySettings(main->settings());
303 }
304
makeSignalConnections()305 void MultiEditor::makeSignalConnections() {
306 DocumentManager* docManager = Main::documentManager();
307
308 connect(docManager, SIGNAL(opened(Document*, int, int)), this, SLOT(onOpen(Document*, int, int)));
309 connect(docManager, SIGNAL(closed(Document*)), this, SLOT(onClose(Document*)));
310 connect(docManager, SIGNAL(saved(Document*)), this, SLOT(update(Document*)));
311 connect(docManager, SIGNAL(showRequest(Document*, int, int)), this, SLOT(show(Document*, int, int)));
312 connect(docManager, SIGNAL(titleChanged(Document*)), this, SLOT(update(Document*)));
313
314 connect(mTabs, SIGNAL(currentChanged(int)), this, SLOT(onCurrentTabChanged(int)));
315 connect(mTabs, SIGNAL(tabCloseRequested(int)), this, SLOT(onCloseRequest(int)));
316 connect(mTabs, SIGNAL(tabMoved(int, int)), this, SLOT(updateDocOrder(int, int)));
317
318 mBoxSigMux->connect(SIGNAL(currentChanged(GenericCodeEditor*)), this,
319 SLOT(onCurrentEditorChanged(GenericCodeEditor*)));
320 }
321
updateDocOrder(int from,int to)322 void MultiEditor::updateDocOrder(int from, int to) { Q_EMIT(updateDockletOrder(from, to)); }
323
breakSignalConnections()324 void MultiEditor::breakSignalConnections() {
325 DocumentManager* docManager = Main::documentManager();
326 docManager->disconnect(this);
327 mTabs->disconnect(this);
328 mBoxSigMux->disconnect(this);
329 }
330
createActions()331 void MultiEditor::createActions() {
332 Settings::Manager* settings = Main::settings();
333
334 QAction* action;
335 const QString editorCategory(tr("Text Editor"));
336
337 // Edit
338
339 mActions[Undo] = action = new QAction(QIcon::fromTheme("edit-undo"), tr("&Undo"), this);
340 action->setShortcut(tr("Ctrl+Z", "Undo"));
341 action->setStatusTip(tr("Undo last editing action"));
342 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(undo()));
343 mEditorSigMux->connect(SIGNAL(undoAvailable(bool)), action, SLOT(setEnabled(bool)));
344 settings->addAction(action, "editor-undo", editorCategory);
345
346 mActions[Redo] = action = new QAction(QIcon::fromTheme("edit-redo"), tr("Re&do"), this);
347 action->setShortcut(tr("Ctrl+Shift+Z", "Redo"));
348 action->setStatusTip(tr("Redo next editing action"));
349 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(redo()));
350 mEditorSigMux->connect(SIGNAL(redoAvailable(bool)), action, SLOT(setEnabled(bool)));
351 settings->addAction(action, "editor-redo", editorCategory);
352
353 mActions[Cut] = action = new QAction(QIcon::fromTheme("edit-cut"), tr("Cu&t"), this);
354 action->setShortcut(tr("Ctrl+X", "Cut"));
355 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
356 action->setStatusTip(tr("Cut text to clipboard"));
357 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(cut()));
358 mEditorSigMux->connect(SIGNAL(copyAvailable(bool)), action, SLOT(setEnabled(bool)));
359 settings->addAction(action, "editor-cut", editorCategory);
360
361 mActions[Copy] = action = new QAction(QIcon::fromTheme("edit-copy"), tr("&Copy"), this);
362 action->setShortcut(tr("Ctrl+C", "Copy"));
363 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
364 action->setStatusTip(tr("Copy text to clipboard"));
365 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(copy()));
366 mEditorSigMux->connect(SIGNAL(copyAvailable(bool)), action, SLOT(setEnabled(bool)));
367 settings->addAction(action, "editor-copy", editorCategory);
368
369 mActions[Paste] = action = new QAction(QIcon::fromTheme("edit-paste"), tr("&Paste"), this);
370 action->setShortcut(tr("Ctrl+V", "Paste"));
371 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
372 action->setStatusTip(tr("Paste text from clipboard"));
373 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(paste()));
374 settings->addAction(action, "editor-paste", editorCategory);
375
376 mActions[IndentLineOrRegion] = action =
377 new QAction(QIcon::fromTheme("format-indent-line"), tr("Autoindent Line or Region"), this);
378 action->setStatusTip(tr("Autoindent Line or Region"));
379 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(indent()), SignalMultiplexer::ConnectionOptional);
380 settings->addAction(action, "editor-indent-auto", editorCategory);
381
382 mActions[TriggerAutoCompletion] = action = new QAction(tr("Trigger Autocompletion"), this);
383 action->setStatusTip(tr("Suggest possible completions of text at cursor"));
384 action->setShortcut(tr("Ctrl+Space", "Trigger Autocompletion"));
385 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
386 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(triggerAutoCompletion()),
387 SignalMultiplexer::ConnectionOptional);
388 settings->addAction(action, "editor-autocompletion", editorCategory);
389
390 mActions[TriggerMethodCallAid] = action = new QAction(tr("Trigger Method Call Aid"), this);
391 action->setStatusTip(tr("Show arguments for currently typed method call"));
392 action->setShortcut(tr("Ctrl+Shift+Space", "Trigger Method Call Aid"));
393 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
394 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(triggerMethodCallAid()),
395 SignalMultiplexer::ConnectionOptional);
396 settings->addAction(action, "editor-method-call-assist", editorCategory);
397
398 mActions[ToggleComment] = action = new QAction(QIcon::fromTheme("edit-comment"), tr("Toggle &Comment"), this);
399 action->setShortcut(tr("Ctrl+/", "Toggle Comment"));
400 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
401 action->setStatusTip(tr("Toggle Comment"));
402 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(toggleComment()), SignalMultiplexer::ConnectionOptional);
403 settings->addAction(action, "editor-toggle-comment", editorCategory);
404
405 mActions[ToggleOverwriteMode] = action =
406 new QAction(QIcon::fromTheme("edit-overwrite"), tr("Toggle &Overwrite Mode"), this);
407 action->setShortcut(tr("Insert", "Toggle Overwrite Mode"));
408 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
409 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(toggleOverwriteMode()));
410 settings->addAction(action, "editor-toggle-overwrite", editorCategory);
411
412 mActions[CopyLineUp] = action = new QAction(QIcon::fromTheme("edit-copylineup"), tr("Copy Line Up"), this);
413 action->setShortcut(tr("Ctrl+Alt+Up", "Copy Line Up"));
414 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
415 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(copyLineUp()));
416 settings->addAction(action, "editor-copy-line-up", editorCategory);
417
418 mActions[CopyLineDown] = action = new QAction(QIcon::fromTheme("edit-copylinedown"), tr("Copy Line Down"), this);
419 action->setShortcut(tr("Ctrl+Alt+Down", "Copy Line Up"));
420 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
421 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(copyLineDown()));
422 settings->addAction(action, "editor-copy-line-down", editorCategory);
423
424 mActions[MoveLineUp] = action = new QAction(QIcon::fromTheme("edit-movelineup"), tr("Move Line Up"), this);
425 action->setShortcut(tr("Ctrl+Shift+Up", "Move Line Up"));
426 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
427 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(moveLineUp()));
428 settings->addAction(action, "editor-move-line-up", editorCategory);
429
430 mActions[MoveLineDown] = action = new QAction(QIcon::fromTheme("edit-movelinedown"), tr("Move Line Down"), this);
431 action->setShortcut(tr("Ctrl+Shift+Down", "Move Line Up"));
432 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
433 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(moveLineDown()));
434 settings->addAction(action, "editor-move-line-down", editorCategory);
435
436 mActions[DeleteWord] = action = new QAction(QIcon::fromTheme("edit-deleteword"), tr("Delete Word"), this);
437 #ifdef Q_OS_MAC
438 action->setShortcut(tr("Meta+W", "Delete Word"));
439 #endif
440 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
441 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(deleteWord()));
442 settings->addAction(action, "delete-word", editorCategory);
443
444 mActions[GotoPreviousBlock] = action =
445 new QAction(QIcon::fromTheme("edit-gotopreviousblock"), tr("Go to Previous Block"), this);
446 action->setShortcut(tr("Ctrl+[", "Go to Previous Block"));
447 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
448 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(gotoPreviousBlock()),
449 SignalMultiplexer::ConnectionOptional);
450 settings->addAction(action, "editor-go-to-prev-block", editorCategory);
451
452 mActions[GotoNextBlock] = action =
453 new QAction(QIcon::fromTheme("edit-gotonextblock"), tr("Go to Next Block"), this);
454 action->setShortcut(tr("Ctrl+]", "Go to Next Block"));
455 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
456 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(gotoNextBlock()), SignalMultiplexer::ConnectionOptional);
457 settings->addAction(action, "editor-go-to-next-block", editorCategory);
458
459 mActions[SelectEnclosingBlock] = action = new QAction(tr("Select Enclosing Block"), this);
460 action->setShortcut(tr("Ctrl+Shift+B", "Select Enclosing Block"));
461 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
462 action->setStatusTip(tr("Select everything between brackets that contain cursor"));
463 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(selectBlockAroundCursor()),
464 SignalMultiplexer::ConnectionOptional);
465 settings->addAction(action, "editor-select-enclosing-block", editorCategory);
466
467 mActions[GotoPreviousRegion] = action =
468 new QAction(QIcon::fromTheme("edit-gotopreviousregion"), tr("Go to Previous Region"), this);
469 action->setShortcut(tr("Ctrl+Alt+[", "Go to Previous Region"));
470 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
471 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(gotoPreviousRegion()),
472 SignalMultiplexer::ConnectionOptional);
473 settings->addAction(action, "editor-go-to-prev-region", editorCategory);
474
475 mActions[GotoNextRegion] = action =
476 new QAction(QIcon::fromTheme("edit-gotonextregion"), tr("Go to Next Region"), this);
477 action->setShortcut(tr("Ctrl+Alt+]", "Go to Next Region"));
478 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
479 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(gotoNextRegion()), SignalMultiplexer::ConnectionOptional);
480 settings->addAction(action, "editor-go-to-next-region", editorCategory);
481
482 mActions[GotoPreviousEmptyLine] = action = new QAction(tr("Go to Previous Empty Line"), this);
483 action->setShortcut(tr("Ctrl+Up", "Go to Previous Empty Line"));
484 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
485 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(gotoPreviousEmptyLine()));
486 settings->addAction(action, "editor-go-to-prev-empty-line", editorCategory);
487
488 mActions[GotoNextEmptyLine] = action = new QAction(tr("Go to Next Empty Line"), this);
489 action->setShortcut(tr("Ctrl+Down", "Go to Next Empty Line"));
490 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
491 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(gotoNextEmptyLine()));
492 settings->addAction(action, "editor-go-to-next-empty-line", editorCategory);
493
494 mActions[SelectRegion] = action = new QAction(tr("Select Region"), this);
495 action->setShortcut(tr("Ctrl+Shift+R", "Select Region"));
496 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
497 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(selectCurrentRegion()),
498 SignalMultiplexer::ConnectionOptional);
499 settings->addAction(action, "editor-select-region", editorCategory);
500
501 // View
502
503 mActions[DocClose] = action = new QAction(QIcon::fromTheme("window-close"), tr("&Close"), this);
504 action->setShortcut(tr("Ctrl+W", "Close document"));
505 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
506 action->setStatusTip(tr("Close the current document"));
507 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(closeDocument()));
508 settings->addAction(action, "ide-document-close", editorCategory);
509
510 mActions[EnlargeFont] = action = new QAction(QIcon::fromTheme("zoom-in"), tr("&Enlarge Font"), this);
511 action->setShortcut(tr("Ctrl++", "Enlarge font"));
512 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
513 action->setStatusTip(tr("Increase displayed font size"));
514 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(zoomIn()));
515 settings->addAction(action, "editor-enlarge-font", editorCategory);
516
517 mActions[ShrinkFont] = action = new QAction(QIcon::fromTheme("zoom-out"), tr("&Shrink Font"), this);
518 action->setShortcut(tr("Ctrl+-", "Shrink font"));
519 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
520 action->setStatusTip(tr("Decrease displayed font size"));
521 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(zoomOut()));
522 settings->addAction(action, "editor-shrink-font", editorCategory);
523
524 mActions[ResetFontSize] = action = new QAction(QIcon::fromTheme("zoom-reset"), tr("&Reset Font Size"), this);
525 action->setShortcut(tr("Ctrl+0", "Reset font"));
526 action->setStatusTip(tr("Reset displayed font size"));
527 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(resetFontSize()));
528 settings->addAction(action, "editor-reset-font-size", editorCategory);
529
530 mActions[ShowWhitespace] = action = new QAction(tr("Show Spaces and Tabs"), this);
531 action->setCheckable(true);
532 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
533 #ifdef Q_OS_MAC
534 action->setShortcut(QKeySequence(Qt::META | Qt::Key_E, Qt::META | Qt::Key_V));
535 #else
536 action->setShortcut(QKeySequence(Qt::ALT | Qt::Key_E, Qt::ALT | Qt::Key_V));
537 #endif
538 connect(action, SIGNAL(triggered(bool)), this, SLOT(setShowWhitespace(bool)));
539 settings->addAction(action, "editor-toggle-show-whitespace", editorCategory);
540
541 mActions[ShowLinenumber] = action = new QAction(tr("Show Line Number"), this);
542 action->setCheckable(true);
543 action->setShortcut(tr("Ctrl+Alt+#", "Show Line Number"));
544 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
545 connect(action, SIGNAL(triggered(bool)), this, SLOT(setShowLinenumber(bool)));
546 settings->addAction(action, "editor-toggle-show-line-number", editorCategory);
547
548 mActions[ShowAutocompleteHelp] = action = new QAction(tr("Show Autocomplete Help"), this);
549 action->setCheckable(true);
550 connect(action, SIGNAL(triggered(bool)), this, SLOT(setShowAutocompleteHelp(bool)));
551 settings->addAction(action, "editor-toggle-show-autocomplete-help", editorCategory);
552
553 mActions[IndentWithSpaces] = action = new QAction(tr("Use Spaces for Indentation"), this);
554 action->setCheckable(true);
555 action->setStatusTip(tr("Indent with spaces instead of tabs"));
556 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
557 mEditorSigMux->connect(action, SIGNAL(triggered(bool)), SLOT(setSpaceIndent(bool)),
558 SignalMultiplexer::ConnectionOptional);
559 settings->addAction(action, "editor-toggle-space-indent", editorCategory);
560
561 mActions[NextDocument] = action = new QAction(tr("Next Document"), this);
562 #ifndef Q_OS_MAC
563 action->setShortcut(tr("Alt+Right", "Next Document"));
564 #else
565 action->setShortcut(tr("Ctrl+Alt+Right", "Next Document"));
566 #endif
567 connect(action, SIGNAL(triggered()), this, SLOT(showNextDocument()));
568 settings->addAction(action, "editor-document-next", editorCategory);
569
570 mActions[PreviousDocument] = action = new QAction(tr("Previous Document"), this);
571 #ifndef Q_OS_MAC
572 action->setShortcut(tr("Alt+Left", "Previous Document"));
573 #else
574 action->setShortcut(tr("Ctrl+Alt+Left", "Previous Document"));
575 #endif
576 connect(action, SIGNAL(triggered()), this, SLOT(showPreviousDocument()));
577 settings->addAction(action, "editor-document-previous", editorCategory);
578
579 mActions[SwitchDocument] = action = new QAction(tr("Switch Document"), this);
580 #ifndef Q_OS_MAC
581 action->setShortcut(tr("Ctrl+Tab", "Switch Document"));
582 #else
583 action->setShortcut(tr("Alt+Tab", "Switch Document"));
584 #endif
585 connect(action, SIGNAL(triggered()), this, SLOT(switchDocument()));
586 settings->addAction(action, "editor-document-switch", editorCategory);
587
588 mActions[SplitHorizontally] = action = new QAction(tr("Split To Right"), this);
589 // action->setShortcut( tr("Ctrl+P, 3", "Split To Right"));
590 connect(action, SIGNAL(triggered()), this, SLOT(splitHorizontally()));
591 settings->addAction(action, "editor-split-right", editorCategory);
592
593 mActions[SplitVertically] = action = new QAction(tr("Split To Bottom"), this);
594 // action->setShortcut( tr("Ctrl+P, 2", "Split To Bottom"));
595 connect(action, SIGNAL(triggered()), this, SLOT(splitVertically()));
596 settings->addAction(action, "editor-split-bottom", editorCategory);
597
598 mActions[RemoveCurrentSplit] = action = new QAction(tr("Remove Current Split"), this);
599 // action->setShortcut( tr("Ctrl+P, 1", "Remove Current Split"));
600 connect(action, SIGNAL(triggered()), this, SLOT(removeCurrentSplit()));
601 settings->addAction(action, "editor-split-remove", editorCategory);
602
603 mActions[RemoveAllSplits] = action = new QAction(tr("Remove All Splits"), this);
604 // action->setShortcut( tr("Ctrl+P, 0", "Remove All Splits"));
605 connect(action, SIGNAL(triggered()), this, SLOT(removeAllSplits()));
606 settings->addAction(action, "editor-split-remove-all", editorCategory);
607
608 // Language
609
610 mActions[EvaluateCurrentDocument] = action =
611 new QAction(QIcon::fromTheme("media-playback-start"), tr("Evaluate &File"), this);
612 action->setStatusTip(tr("Evaluate current File"));
613 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
614 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(evaluateDocument()),
615 SignalMultiplexer::ConnectionOptional);
616 settings->addAction(action, "editor-eval-file", editorCategory);
617
618 mActions[EvaluateRegion] = action =
619 new QAction(QIcon::fromTheme("media-playback-start"), tr("&Evaluate Selection, Line or Region"), this);
620 action->setShortcut(tr("Ctrl+Return", "Evaluate region"));
621 action->setStatusTip(tr("Evaluate current region"));
622 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
623 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(evaluateRegion()), SignalMultiplexer::ConnectionOptional);
624 settings->addAction(action, "editor-eval-smart", editorCategory);
625
626 mActions[EvaluateLine] = action =
627 new QAction(QIcon::fromTheme("media-playback-start"), tr("&Evaluate Selection or Line"), this);
628 action->setShortcut(tr("Shift+Return", "Evaluate selection/line"));
629 action->setStatusTip(tr("Evaluate current selection/line"));
630 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
631 mEditorSigMux->connect(action, SIGNAL(triggered()), SLOT(evaluateLine()), SignalMultiplexer::ConnectionOptional);
632 settings->addAction(action, "editor-eval-line", editorCategory);
633
634 // These actions are not added to any menu, so they have to be added
635 // at least to this widget, in order for the shortcuts to always respond:
636 addAction(mActions[TriggerAutoCompletion]);
637 addAction(mActions[TriggerMethodCallAid]);
638 addAction(mActions[SwitchDocument]);
639
640 // These actions have to be added because to the widget because they have
641 // Qt::WidgetWithChildrenShortcut context:
642 addAction(mActions[Cut]);
643 addAction(mActions[Copy]);
644 addAction(mActions[Paste]);
645 addAction(mActions[DocClose]);
646 addAction(mActions[EnlargeFont]);
647 addAction(mActions[ShrinkFont]);
648 addAction(mActions[ShowWhitespace]);
649 addAction(mActions[ShowLinenumber]);
650 addAction(mActions[IndentWithSpaces]);
651 addAction(mActions[EvaluateCurrentDocument]);
652 addAction(mActions[EvaluateRegion]);
653 addAction(mActions[EvaluateLine]);
654 addAction(mActions[ToggleComment]);
655 addAction(mActions[ToggleOverwriteMode]);
656 addAction(mActions[CopyLineUp]);
657 addAction(mActions[CopyLineDown]);
658 addAction(mActions[MoveLineUp]);
659 addAction(mActions[MoveLineDown]);
660 addAction(mActions[DeleteWord]);
661 addAction(mActions[GotoPreviousBlock]);
662 addAction(mActions[GotoNextBlock]);
663 addAction(mActions[SelectEnclosingBlock]);
664 addAction(mActions[GotoPreviousRegion]);
665 addAction(mActions[GotoNextRegion]);
666 addAction(mActions[GotoPreviousEmptyLine]);
667 addAction(mActions[GotoNextEmptyLine]);
668 addAction(mActions[SelectRegion]);
669 }
670
updateActions()671 void MultiEditor::updateActions() {
672 GenericCodeEditor* editor = currentEditor();
673 ScCodeEditor* scEditor = qobject_cast<ScCodeEditor*>(editor);
674 QTextDocument* doc = editor ? editor->textDocument() : 0;
675
676 mActions[Undo]->setEnabled(doc && doc->isUndoAvailable());
677 mActions[Redo]->setEnabled(doc && doc->isRedoAvailable());
678 mActions[Copy]->setEnabled(editor && editor->textCursor().hasSelection());
679 mActions[Cut]->setEnabled(mActions[Copy]->isEnabled());
680 mActions[Paste]->setEnabled(editor);
681 mActions[ToggleOverwriteMode]->setEnabled(editor);
682 mActions[CopyLineUp]->setEnabled(editor);
683 mActions[CopyLineDown]->setEnabled(editor);
684 mActions[MoveLineUp]->setEnabled(editor);
685 mActions[MoveLineDown]->setEnabled(editor);
686 mActions[DeleteWord]->setEnabled(editor);
687 mActions[GotoPreviousEmptyLine]->setEnabled(editor);
688 mActions[GotoNextEmptyLine]->setEnabled(editor);
689 mActions[DocClose]->setEnabled(editor);
690 mActions[EnlargeFont]->setEnabled(editor);
691 mActions[ShrinkFont]->setEnabled(editor);
692 mActions[ResetFontSize]->setEnabled(editor);
693 mActions[IndentWithSpaces]->setEnabled(scEditor);
694 mActions[IndentWithSpaces]->setChecked(scEditor && scEditor->spaceIndent());
695
696 // ScLang-specific actions
697 bool editorIsScCodeEditor = qobject_cast<ScCodeEditor*>(editor); // NOOP at the moment, but
698 mActions[ToggleComment]->setEnabled(editor && editorIsScCodeEditor);
699 mActions[GotoPreviousBlock]->setEnabled(editor && editorIsScCodeEditor);
700 mActions[GotoNextBlock]->setEnabled(editor && editorIsScCodeEditor);
701 mActions[SelectEnclosingBlock]->setEnabled(editor && editorIsScCodeEditor);
702 mActions[GotoPreviousRegion]->setEnabled(editor && editorIsScCodeEditor);
703 mActions[GotoNextRegion]->setEnabled(editor && editorIsScCodeEditor);
704 mActions[SelectRegion]->setEnabled(editor && editorIsScCodeEditor);
705 mActions[IndentLineOrRegion]->setEnabled(editor && editorIsScCodeEditor);
706
707 mActions[EvaluateCurrentDocument]->setEnabled(editor && editorIsScCodeEditor);
708 mActions[EvaluateRegion]->setEnabled(editor && editorIsScCodeEditor);
709 mActions[EvaluateLine]->setEnabled(editor && editorIsScCodeEditor);
710 }
711
applySettings(Settings::Manager * settings)712 void MultiEditor::applySettings(Settings::Manager* settings) {
713 bool show_whitespace = settings->value("IDE/editor/showWhitespace").toBool();
714 bool show_linenumber = settings->value("IDE/editor/showLinenumber").toBool();
715 bool show_autocompletehelp = settings->value("IDE/editor/showAutocompleteHelp").toBool();
716 mActions[ShowWhitespace]->setChecked(show_whitespace);
717 mActions[ShowLinenumber]->setChecked(show_linenumber);
718 mActions[ShowAutocompleteHelp]->setChecked(show_autocompletehelp);
719
720 setMainComboBoxOption();
721
722 int boxCount = mSplitter->findChildren<CodeEditorBox*>().count();
723 if (boxCount > 1) {
724 activateComboBoxWhenSplitting();
725 }
726 }
727
activateComboBoxWhenSplitting()728 void MultiEditor::activateComboBoxWhenSplitting() {
729 emit splitViewActivated();
730 bool comboBoxInUse = Main::settings()->value("IDE/editor/useComboBox").toBool();
731 if (!comboBoxInUse) {
732 bool comboBoxWhenSplitting = Main::settings()->value("IDE/editor/useComboBoxWhenSplitting").toBool();
733 showEditorTabs(comboBoxWhenSplitting);
734 }
735 }
736
setMainComboBoxOption()737 void MultiEditor::setMainComboBoxOption() {
738 bool comboBoxInUse = Main::settings()->value("IDE/editor/useComboBox").toBool();
739 showEditorTabs(comboBoxInUse);
740 }
741
showEditorTabs(bool condition)742 void MultiEditor::showEditorTabs(bool condition) {
743 if (condition)
744 mTabs->hide();
745 else
746 mTabs->show();
747 }
748
saveBoxState(CodeEditorBox * box,const QList<Document * > & documentList)749 static QVariantList saveBoxState(CodeEditorBox* box, const QList<Document*>& documentList) {
750 // save editors in reverse order - first one is last shown.
751 QVariantList boxData;
752 int idx = box->history().count();
753 while (idx--) {
754 GenericCodeEditor* editor = box->history()[idx];
755 if (!editor->document()->filePath().isEmpty()) {
756 int documentIndex = documentList.indexOf(editor->document());
757 Q_ASSERT(documentIndex >= 0);
758 QVariantMap editorData;
759 editorData.insert("documentIndex", documentIndex);
760 editorData.insert("position", editor->textCursor().position());
761 boxData.append(editorData);
762 }
763 }
764 return boxData;
765 }
766
saveSplitterState(QSplitter * splitter,const QList<Document * > & documentList)767 static QVariantMap saveSplitterState(QSplitter* splitter, const QList<Document*>& documentList) {
768 QVariantMap splitterData;
769
770 splitterData.insert("state", splitter->saveState().toBase64());
771
772 QVariantList childrenData;
773
774 int childCount = splitter->count();
775 for (int idx = 0; idx < childCount; idx++) {
776 QWidget* child = splitter->widget(idx);
777
778 CodeEditorBox* box = qobject_cast<CodeEditorBox*>(child);
779 if (box) {
780 QVariantList boxData = saveBoxState(box, documentList);
781 childrenData.append(QVariant(boxData));
782 continue;
783 }
784
785 QSplitter* childSplitter = qobject_cast<QSplitter*>(child);
786 if (childSplitter) {
787 QVariantMap childSplitterData = saveSplitterState(childSplitter, documentList);
788 childrenData.append(QVariant(childSplitterData));
789 }
790 }
791
792 splitterData.insert("elements", childrenData);
793
794 return splitterData;
795 }
796
saveSession(Session * session)797 void MultiEditor::saveSession(Session* session) {
798 QList<Document*> documentList;
799
800 QVariantList tabsData;
801 int tabCount = mTabs->count();
802 for (int tabIdx = 0; tabIdx < tabCount; ++tabIdx) {
803 Document* doc = documentForTab(tabIdx);
804 if (doc) {
805 documentList << doc;
806 tabsData << doc->filePath();
807 }
808 }
809
810 session->setValue("documents", QVariant::fromValue(tabsData));
811
812 session->remove("editors");
813 session->setValue("editors", saveSplitterState(mSplitter, documentList));
814 }
815
loadBoxState(CodeEditorBox * box,const QVariantList & data,const QList<Document * > & documentList)816 void MultiEditor::loadBoxState(CodeEditorBox* box, const QVariantList& data, const QList<Document*>& documentList) {
817 int docCount = documentList.count();
818 foreach (QVariant docVar, data) {
819 QVariantMap docData = docVar.value<QVariantMap>();
820 int docIndex = docData.value("documentIndex").toInt();
821 int docPos = docData.value("position").toInt();
822 if (docIndex >= 0 && docIndex < docCount)
823 box->setDocument(documentList[docIndex], docPos);
824 }
825 }
826
loadSplitterState(MultiSplitter * splitter,const QVariantMap & data,const QList<Document * > & documentList)827 void MultiEditor::loadSplitterState(MultiSplitter* splitter, const QVariantMap& data,
828 const QList<Document*>& documentList) {
829 QByteArray state = QByteArray::fromBase64(data.value("state").value<QByteArray>());
830
831 QVariantList childrenData = data.value("elements").value<QVariantList>();
832 foreach (const QVariant& childVar, childrenData) {
833 if (childVar.type() == QVariant::List) {
834 CodeEditorBox* childBox = newBox(splitter);
835 splitter->addWidget(childBox);
836 QVariantList childBoxData = childVar.value<QVariantList>();
837 loadBoxState(childBox, childBoxData, documentList);
838 } else if (childVar.type() == QVariant::Map) {
839 MultiSplitter* childSplitter = new MultiSplitter(this);
840 splitter->addWidget(childSplitter);
841 QVariantMap childSplitterData = childVar.value<QVariantMap>();
842 loadSplitterState(childSplitter, childSplitterData, documentList);
843 }
844 }
845
846 if (!splitter->restoreState(state))
847 qWarning("MultiEditor: could not restore splitter state!");
848 }
849
switchSession(Session * session)850 void MultiEditor::switchSession(Session* session) {
851 ///// Going offline...
852
853 breakSignalConnections();
854
855 DocumentManager* docManager = Main::documentManager();
856
857 QList<Document*> documentList = docManager->documents();
858
859 // close all docs
860 foreach (Document* doc, documentList)
861 docManager->close(doc);
862
863 // remove all tabs
864 while (mTabs->count())
865 mTabs->removeTab(0);
866
867 // remove all editors
868 delete mSplitter;
869
870 documentList.clear();
871
872 mSplitter = new MultiSplitter(this);
873
874 CodeEditorBox* firstBox = 0;
875
876 if (session) {
877 // open documents saved in the session
878 QVariantList docDataList = session->value("documents").value<QVariantList>();
879 foreach (const QVariant& docData, docDataList) {
880 QString filePath = docData.toString();
881 Document* doc = docManager->open(filePath, -1, 0, true);
882 documentList << doc;
883 }
884
885 // restore tabs
886 for (int i = 0; i < documentList.size(); ++i)
887 insertTab(documentList[i], i);
888
889 // restore editors
890 if (session->contains("editors")) {
891 QVariantMap splitterData = session->value("editors").value<QVariantMap>();
892 loadSplitterState(mSplitter, splitterData, documentList);
893
894 if (mSplitter->count()) {
895 firstBox = mSplitter->findChild<CodeEditorBox>();
896 if (!firstBox) {
897 qWarning("Session seems to contain invalid editor split data!");
898 delete mSplitter;
899 mSplitter = new MultiSplitter(this);
900 }
901 }
902 }
903 }
904
905 if (!firstBox) {
906 // Restoring the session didn't result in any editor box, so create one:
907 firstBox = newBox(mSplitter);
908 mSplitter->addWidget(firstBox);
909 }
910
911 layout()->addWidget(mSplitter);
912
913 makeSignalConnections();
914
915 ///// Back online.
916
917 mCurrentEditorBox = 0; // ensure complete update
918 setCurrentBox(firstBox);
919
920 if (!session)
921 // create a document on new session
922 docManager->create();
923
924 firstBox->setFocus(Qt::OtherFocusReason); // ensure focus
925
926 setMainComboBoxOption();
927 if (mSplitter->count() > 1)
928 activateComboBoxWhenSplitting();
929 else
930 emit splitViewDeactivated();
931 }
932
insertTab(Document * doc,int insertIndex)933 int MultiEditor::insertTab(Document* doc, int insertIndex) {
934 if (!doc)
935 return -1;
936
937 int tabIdx = tabForDocument(doc);
938 if (tabIdx != -1)
939 return tabIdx;
940
941 QTextDocument* tdoc = doc->textDocument();
942
943 QIcon icon;
944 if (tdoc->isModified())
945 icon = mDocModifiedIcon;
946
947 insertIndex = insertIndex < 0 ? mTabs->currentIndex() + 1 : insertIndex;
948 tabIdx = mTabs->insertTab(insertIndex, icon, doc->title());
949 mTabs->setTabData(tabIdx, QVariant::fromValue<Document*>(doc));
950
951 connect(tdoc, &QTextDocument::modificationChanged, [this, doc](bool) { onDocModified(doc); });
952
953 return tabIdx;
954 }
955
setCurrent(Document * doc)956 void MultiEditor::setCurrent(Document* doc) {
957 int tabIdx = tabForDocument(doc);
958 if (tabIdx != -1)
959 mTabs->setCurrentIndex(tabIdx);
960 }
961
updateTabsOrder(QList<Document * > docOrder)962 void MultiEditor::updateTabsOrder(QList<Document*> docOrder) {
963 mTabs->blockSignals(true);
964 for (int idx = 0; idx < docOrder.count(); idx++) {
965 if (docOrder.at(idx) != documentForTab(idx)) {
966 Document* doc = docOrder.at(idx);
967 int tabIdx = tabForDocument(doc);
968 mTabs->moveTab(tabIdx, idx);
969 }
970 }
971 mTabs->blockSignals(false);
972 }
973
showNextDocument()974 void MultiEditor::showNextDocument() {
975 int currentIndex = mTabs->currentIndex();
976 mTabs->setCurrentIndex(qMin(currentIndex + 1, mTabs->count() - 1));
977 }
978
showPreviousDocument()979 void MultiEditor::showPreviousDocument() {
980 int currentIndex = mTabs->currentIndex();
981 mTabs->setCurrentIndex(qMax(0, currentIndex - 1));
982 }
983
switchDocument()984 void MultiEditor::switchDocument() {
985 CodeEditorBox* box = currentBox();
986
987 DocumentSelectPopUp* popup = new DocumentSelectPopUp(box->history(), this);
988
989 QRect popupRect(0, 0, 300, 200);
990 popupRect.moveCenter(rect().center());
991 popup->resize(popupRect.size());
992 QPoint globalPosition = mapToGlobal(popupRect.topLeft());
993
994 Document* selectedDocument = popup->exec(globalPosition);
995
996 if (selectedDocument)
997 box->setDocument(selectedDocument);
998 }
999
onOpen(Document * doc,int initialCursorPosition,int selectionLength)1000 void MultiEditor::onOpen(Document* doc, int initialCursorPosition, int selectionLength) {
1001 insertTab(doc);
1002
1003 currentBox()->setDocument(doc, initialCursorPosition, selectionLength);
1004 currentBox()->setFocus(Qt::OtherFocusReason);
1005 }
1006
onClose(Document * doc)1007 void MultiEditor::onClose(Document* doc) {
1008 int tabIdx = tabForDocument(doc);
1009 if (tabIdx != -1)
1010 mTabs->removeTab(tabIdx);
1011 // TODO: each box should switch document according to their own history
1012 }
1013
onDocModified(Document * doc)1014 void MultiEditor::onDocModified(Document* doc) {
1015 if (!doc)
1016 return;
1017
1018 int tabIdx = tabForDocument(doc);
1019 if (tabIdx == -1)
1020 return;
1021
1022 bool isModified = doc->textDocument()->isModified();
1023 QIcon icon;
1024 if (isModified)
1025 icon = mDocModifiedIcon;
1026
1027 Main::evaluateCodeIfCompiled(
1028 QStringLiteral("Document.findByQUuid(\'%1\').prSetEdited(%2)").arg(doc->id().constData()).arg(isModified),
1029 true);
1030
1031 mTabs->setTabIcon(tabIdx, icon);
1032 }
1033
show(Document * doc,int pos,int selectionLength)1034 void MultiEditor::show(Document* doc, int pos, int selectionLength) {
1035 currentBox()->setDocument(doc, pos, selectionLength);
1036 currentBox()->setFocus(Qt::OtherFocusReason);
1037 }
1038
update(Document * doc)1039 void MultiEditor::update(Document* doc) {
1040 int tabIdx = tabForDocument(doc);
1041 if (tabIdx != -1)
1042 mTabs->setTabText(tabIdx, doc->title());
1043
1044 // update thisProcess.nowExecutingPath
1045 GenericCodeEditor* editor = currentEditor();
1046 if (editor->document() == doc)
1047 Main::documentManager()->setActiveDocument(doc);
1048 }
1049
onCloseRequest(int index)1050 void MultiEditor::onCloseRequest(int index) {
1051 Document* doc = documentForTab(index);
1052 if (doc)
1053 MainWindow::close(doc);
1054 }
1055
onCurrentTabChanged(int index)1056 void MultiEditor::onCurrentTabChanged(int index) {
1057 if (index == -1)
1058 return;
1059
1060 Document* doc = documentForTab(index);
1061 if (!doc)
1062 return;
1063
1064 CodeEditorBox* curBox = currentBox();
1065 curBox->setDocument(doc);
1066 curBox->setFocus(Qt::OtherFocusReason);
1067 }
1068
onCurrentEditorChanged(GenericCodeEditor * editor)1069 void MultiEditor::onCurrentEditorChanged(GenericCodeEditor* editor) { setCurrentEditor(editor); }
1070
onBoxActivated(CodeEditorBox * box)1071 void MultiEditor::onBoxActivated(CodeEditorBox* box) { setCurrentBox(box); }
1072
documentForTab(int index)1073 Document* MultiEditor::documentForTab(int index) {
1074 QVariant doc = mTabs->tabData(index);
1075 if (doc.isValid() && !doc.isNull())
1076 return doc.value<Document*>();
1077 else
1078 return NULL;
1079 }
1080
tabForDocument(Document * doc)1081 int MultiEditor::tabForDocument(Document* doc) {
1082 int tabCount = mTabs->count();
1083 for (int idx = 0; idx < tabCount; ++idx) {
1084 Document* tabDoc = documentForTab(idx);
1085 if (tabDoc && tabDoc == doc)
1086 return idx;
1087 }
1088 return -1;
1089 }
1090
newBox(MultiSplitter * currSplitter)1091 CodeEditorBox* MultiEditor::newBox(MultiSplitter* currSplitter) {
1092 CodeEditorBox* box = new CodeEditorBox(currSplitter);
1093
1094 connect(box, SIGNAL(activated(CodeEditorBox*)), this, SLOT(onBoxActivated(CodeEditorBox*)));
1095
1096 return box;
1097 }
1098
setCurrentBox(CodeEditorBox * box)1099 void MultiEditor::setCurrentBox(CodeEditorBox* box) {
1100 if (mCurrentEditorBox == box)
1101 return;
1102
1103 mCurrentEditorBox = box;
1104 mBoxSigMux->setCurrentObject(box);
1105 setCurrentEditor(box->currentEditor());
1106
1107 mCurrentEditorBox->setActive();
1108 }
1109
setCurrentEditor(GenericCodeEditor * editor)1110 void MultiEditor::setCurrentEditor(GenericCodeEditor* editor) {
1111 if (editor) {
1112 int tabIndex = tabForDocument(editor->document());
1113 if (tabIndex != -1)
1114 mTabs->setCurrentIndex(tabIndex);
1115 }
1116
1117 mEditorSigMux->setCurrentObject(editor);
1118 updateActions();
1119
1120 Document* currentDocument = editor ? editor->document() : 0;
1121 Main::documentManager()->setActiveDocument(currentDocument);
1122 emit currentDocumentChanged(currentDocument);
1123 }
1124
currentEditor()1125 GenericCodeEditor* MultiEditor::currentEditor() { return currentBox()->currentEditor(); }
1126
split(Qt::Orientation splitDirection)1127 void MultiEditor::split(Qt::Orientation splitDirection) {
1128 CodeEditorBox* box = newBox(mSplitter);
1129 CodeEditorBox* curBox = currentBox();
1130 GenericCodeEditor* curEditor = curBox->currentEditor();
1131
1132 if (curEditor)
1133 box->setDocument(curEditor->document(), curEditor->textCursor().position());
1134
1135 mSplitter->insertWidget(box, curBox, splitDirection);
1136 box->setFocus(Qt::OtherFocusReason);
1137
1138 activateComboBoxWhenSplitting();
1139 }
1140
removeCurrentSplit()1141 void MultiEditor::removeCurrentSplit() {
1142 int boxCount = mSplitter->findChildren<CodeEditorBox*>().count();
1143 if (boxCount < 2) {
1144 // Do not allow removing the one and only box.
1145 return;
1146 }
1147
1148 CodeEditorBox* box = currentBox();
1149 mSplitter->removeWidget(box);
1150
1151 // switch current box to first box found:
1152 box = mSplitter->findChild<CodeEditorBox>();
1153 Q_ASSERT(box);
1154 setCurrentBox(box);
1155 box->setFocus(Qt::OtherFocusReason);
1156
1157 if (boxCount == 2) {
1158 emit splitViewDeactivated();
1159 setMainComboBoxOption();
1160 } else if (boxCount > 2) {
1161 activateComboBoxWhenSplitting();
1162 }
1163 }
1164
removeAllSplits()1165 void MultiEditor::removeAllSplits() {
1166 CodeEditorBox* box = currentBox();
1167 Q_ASSERT(box);
1168 Q_ASSERT(mSplitter->count());
1169 if (mSplitter->count() == 1 && mSplitter->widget(0) == box)
1170 // Nothing to do.
1171 return;
1172
1173 breakSignalConnections();
1174
1175 MultiSplitter* newSplitter = new MultiSplitter(this);
1176
1177 CodeEditorBox* nBox = newBox(newSplitter);
1178 newSplitter->addWidget(nBox);
1179
1180 GenericCodeEditor* curEditor = box->currentEditor();
1181 if (curEditor)
1182 nBox->setDocument(curEditor->document(), curEditor->textCursor().position());
1183
1184 delete mSplitter;
1185 mSplitter = newSplitter;
1186 layout()->addWidget(mSplitter);
1187
1188 emit splitViewDeactivated();
1189 setMainComboBoxOption();
1190
1191 mCurrentEditorBox = 0; // ensure complete update
1192 setCurrentBox(nBox);
1193
1194 makeSignalConnections(); // ensure signal connections
1195
1196 nBox->setFocus(Qt::OtherFocusReason);
1197 }
1198
setShowWhitespace(bool showWhitespace)1199 void MultiEditor::setShowWhitespace(bool showWhitespace) {
1200 Main::settings()->setValue("IDE/editor/showWhitespace", showWhitespace);
1201 Main::instance()->applySettings();
1202 }
1203
setShowLinenumber(bool showLinenumber)1204 void MultiEditor::setShowLinenumber(bool showLinenumber) {
1205 Main::settings()->setValue("IDE/editor/showLinenumber", showLinenumber);
1206 Main::instance()->applySettings();
1207 }
1208
setShowAutocompleteHelp(bool showAutocompleteHelp)1209 void MultiEditor::setShowAutocompleteHelp(bool showAutocompleteHelp) {
1210 Main::settings()->setValue("IDE/editor/showAutocompleteHelp", showAutocompleteHelp);
1211 }
1212
1213 } // namespace ScIDE
1214