3 Copyright (c) 2012 Jakob Leben & Tim Blechmann
4 http://www.audiosynth.com
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.
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.
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
21 #include "multi_editor.hpp"
22 #include "editor_box.hpp"
23 #include "main_window.hpp"
24 #include "lookup_dialog.hpp"
25 #include "code_editor/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"
33 #include "yaml-cpp/node.h"
34 #include "yaml-cpp/parser.h"
36 #include <QApplication>
38 #include <QHBoxLayout>
39 #include <QVBoxLayout>
46 #include <QHeaderView>
48 #include <QTreeWidget>
49 #include <QStandardItemModel>
54 class DocumentSelectPopUp
: public QDialog
57 explicit DocumentSelectPopUp(const CodeEditorBox::History
& history
, QWidget
* parent
):
58 QDialog(parent
, Qt::Popup
)
60 mModel
= new QStandardItemModel(this);
61 populateModel(history
);
63 mListView
= new QListView();
64 mListView
->setModel(mModel
);
65 mListView
->setFrameShape(QFrame::NoFrame
);
67 QHBoxLayout
*layout
= new QHBoxLayout(this);
68 layout
->addWidget(mListView
);
69 layout
->setContentsMargins(1,1,1,1);
71 connect(mListView
, SIGNAL(activated(QModelIndex
)), this, SLOT(accept()));
73 mListView
->setFocus(Qt::OtherFocusReason
);
75 QModelIndex nextIndex
= mModel
->index(1, 0);
76 mListView
->setCurrentIndex(nextIndex
);
78 mListView
->setEditTriggers(QAbstractItemView::NoEditTriggers
);
81 Document
* exec( const QPoint
& pos
)
85 return currentDocument();
91 void keyReleaseEvent (QKeyEvent
* ke
)
93 // adapted from qtcreator
94 if (ke
->modifiers() == 0
95 /*HACK this is to overcome some event inconsistencies between platforms*/
96 || (ke
->modifiers() == Qt::AltModifier
97 && (ke
->key() == Qt::Key_Alt
|| ke
->key() == -1))) {
101 QDialog::keyPressEvent(ke
);
104 void keyPressEvent(QKeyEvent
* ke
)
108 int row
= mListView
->currentIndex().row() + 1;
109 if (!mModel
->hasIndex(row
, 0))
112 QModelIndex nextIndex
= mModel
->index(row
, 0);
113 mListView
->setCurrentIndex(nextIndex
);
118 case Qt::Key_Backtab
: {
119 int row
= mListView
->currentIndex().row() - 1;
120 if (!mModel
->hasIndex(row
, 0))
121 row
= mModel
->rowCount() - 1;
123 QModelIndex nextIndex
= mModel
->index(row
, 0);
124 mListView
->setCurrentIndex(nextIndex
);
137 QDialog::keyPressEvent(ke
);
140 Document
* currentDocument()
142 QStandardItem
* currentItem
= mModel
->itemFromIndex(mListView
->currentIndex());
144 return currentItem
? currentItem
->data().value
<Document
*>()
148 void populateModel( const CodeEditorBox::History
& history
)
150 QList
<Document
*> displayDocuments
;
151 foreach(CodeEditor
*editor
, history
)
152 displayDocuments
<< editor
->document();
154 QList
<Document
*> managerDocuments
= Main::instance()->documentManager()->documents();
155 foreach(Document
*document
, managerDocuments
)
156 if (!displayDocuments
.contains(document
))
157 displayDocuments
<< document
;
159 foreach (Document
* document
, displayDocuments
) {
160 QStandardItem
* item
= new QStandardItem(document
->title());
161 item
->setData(QVariant::fromValue(document
));
162 mModel
->appendRow(item
);
166 QListView
*mListView
;
167 QStandardItemModel
*mModel
;
170 MultiEditor::MultiEditor( Main
*main
, QWidget
* parent
) :
172 mDocManager(main
->documentManager()),
173 mSigMux(new SignalMultiplexer(this)),
174 mBoxSigMux(new SignalMultiplexer(this)),
175 mDocModifiedIcon( QApplication::style()->standardIcon(QStyle::SP_DialogSaveButton
) )
178 mTabs
->setDocumentMode(true);
179 mTabs
->setTabsClosable(true);
180 mTabs
->setMovable(true);
181 mTabs
->setUsesScrollButtons(true);
182 mTabs
->setDrawBase(false);
184 CodeEditorBox
*defaultBox
= newBox();
186 mSplitter
= new MultiSplitter();
187 mSplitter
->addWidget(defaultBox
);
189 QVBoxLayout
*l
= new QVBoxLayout
;
190 l
->setContentsMargins(0,0,0,0);
193 l
->addWidget(mSplitter
);
196 connect(mDocManager
, SIGNAL(opened(Document
*, int)),
197 this, SLOT(onOpen(Document
*, int)));
198 connect(mDocManager
, SIGNAL(closed(Document
*)),
199 this, SLOT(onClose(Document
*)));
200 connect(mDocManager
, SIGNAL(saved(Document
*)),
201 this, SLOT(update(Document
*)));
202 connect(mDocManager
, SIGNAL(showRequest(Document
*,int)),
203 this, SLOT(show(Document
*,int))),
205 connect(mTabs
, SIGNAL(currentChanged(int)),
206 this, SLOT(onCurrentTabChanged(int)));
207 connect(mTabs
, SIGNAL(tabCloseRequested(int)),
208 this, SLOT(onCloseRequest(int)));
210 mSigMux
->connect(SIGNAL(modificationChanged(bool)),
211 this, SLOT(onModificationChanged(bool)));
213 mBoxSigMux
->connect(SIGNAL(currentChanged(CodeEditor
*)),
214 this, SLOT(onCurrentEditorChanged(CodeEditor
*)));
216 connect(this, SIGNAL(currentDocumentChanged(Document
*)), mDocManager
, SLOT(activeDocumentChanged(Document
*)));
220 setCurrentBox( defaultBox
); // will updateActions();
222 applySettings(main
->settings());
225 void MultiEditor::createActions()
227 Settings::Manager
*settings
= Main::instance()->settings();
228 settings
->beginGroup("IDE/shortcuts");
234 mActions
[Undo
] = act
= new QAction(
235 QIcon::fromTheme("edit-undo"), tr("&Undo"), this);
236 act
->setShortcut(tr("Ctrl+Z", "Undo"));
237 act
->setStatusTip(tr("Undo last editing action"));
238 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(undo()));
239 mSigMux
->connect(SIGNAL(undoAvailable(bool)), act
, SLOT(setEnabled(bool)));
241 mActions
[Redo
] = act
= new QAction(
242 QIcon::fromTheme("edit-redo"), tr("Re&do"), this);
243 act
->setShortcut(tr("Ctrl+Shift+Z", "Redo"));
244 act
->setStatusTip(tr("Redo next editing action"));
245 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(redo()));
246 mSigMux
->connect(SIGNAL(redoAvailable(bool)), act
, SLOT(setEnabled(bool)));
248 mActions
[Cut
] = act
= new QAction(
249 QIcon::fromTheme("edit-cut"), tr("Cu&t"), this);
250 act
->setShortcut(tr("Ctrl+X", "Cut"));
251 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
252 act
->setStatusTip(tr("Cut text to clipboard"));
253 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(cut()));
254 mSigMux
->connect(SIGNAL(copyAvailable(bool)), act
, SLOT(setEnabled(bool)));
256 mActions
[Copy
] = act
= new QAction(
257 QIcon::fromTheme("edit-copy"), tr("&Copy"), this);
258 act
->setShortcut(tr("Ctrl+C", "Copy"));
259 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
260 act
->setStatusTip(tr("Copy text to clipboard"));
261 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(copy()));
262 mSigMux
->connect(SIGNAL(copyAvailable(bool)), act
, SLOT(setEnabled(bool)));
264 mActions
[Paste
] = act
= new QAction(
265 QIcon::fromTheme("edit-paste"), tr("&Paste"), this);
266 act
->setShortcut(tr("Ctrl+V", "Paste"));
267 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
268 act
->setStatusTip(tr("Paste text from clipboard"));
269 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(paste()));
271 mActions
[IndentLineOrRegion
] = act
= new QAction(
272 QIcon::fromTheme("format-indent-line"), tr("Indent Line or Region"), this);
273 act
->setStatusTip(tr("Indent Line or Region"));
274 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(indent()));
276 mActions
[TriggerAutoCompletion
] = act
= new QAction(tr("Trigger Autocompletion"), this);
277 act
->setStatusTip(tr("Suggest possible completions of text at cursor"));
278 act
->setShortcut(tr("Ctrl+Space", "Trigger Autocompletion"));
279 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
280 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(triggerAutoCompletion()));
282 mActions
[TriggerMethodCallAid
] = act
= new QAction(tr("Trigger Method Call Aid"), this);
283 act
->setStatusTip(tr("Show arguments for currently typed method call"));
284 act
->setShortcut(tr("Alt+Space", "Trigger Method Call Aid"));
285 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
286 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(triggerMethodCallAid()));
288 mActions
[ToggleComment
] = act
= new QAction(
289 QIcon::fromTheme("edit-comment"), tr("Toggle &Comment"), this);
290 act
->setShortcut(tr("Ctrl+/", "Toggle Comment"));
291 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
292 act
->setStatusTip(tr("Toggle Comment"));
293 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(toggleComment()));
295 mActions
[ToggleOverwriteMode
] = act
= new QAction(
296 QIcon::fromTheme("edit-overwrite"), tr("Toggle &Overwrite Mode"), this);
297 act
->setShortcut(tr("Insert", "Toggle Overwrite Mode"));
298 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
299 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(toggleOverwriteMode()));
301 mActions
[CopyLineUp
] = act
= new QAction(
302 QIcon::fromTheme("edit-copylineup"), tr("Copy Line Up"), this);
303 act
->setShortcut(tr("Ctrl+Alt+Up", "Copy Line Up"));
304 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
305 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(copyLineUp()));
307 mActions
[CopyLineDown
] = act
= new QAction(
308 QIcon::fromTheme("edit-copylinedown"), tr("Copy Line Down"), this);
309 act
->setShortcut(tr("Ctrl+Alt+Down", "Copy Line Up"));
310 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
311 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(copyLineDown()));
313 mActions
[MoveLineUp
] = act
= new QAction(
314 QIcon::fromTheme("edit-movelineup"), tr("move Line Up"), this);
315 act
->setShortcut(tr("Ctrl+Shift+Up", "Move Line Up"));
316 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
317 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(moveLineUp()));
319 mActions
[MoveLineDown
] = act
= new QAction(
320 QIcon::fromTheme("edit-movelinedown"), tr("Move Line Down"), this);
321 act
->setShortcut(tr("Ctrl+Shift+Down", "Move Line Up"));
322 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
323 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(moveLineDown()));
325 mActions
[GotoPreviousBlock
] = act
= new QAction(
326 QIcon::fromTheme("edit-gotopreviousblock"), tr("Go to Previous Block"), this);
327 act
->setShortcut(tr("Ctrl+[", "Go to Previous Block"));
328 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
329 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(gotoPreviousBlock()));
331 mActions
[GotoNextBlock
] = act
= new QAction(
332 QIcon::fromTheme("edit-gotonextblock"), tr("Go to Next Block"), this);
333 act
->setShortcut(tr("Ctrl+]", "Go to Next Block"));
334 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
335 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(gotoNextBlock()));
337 mActions
[GotoPreviousEmptyLine
] = act
= new QAction( tr("Go to Previous Empty Line"), this);
338 act
->setShortcut(tr("Ctrl+Up", "Go to Previous Empty Line"));
339 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
340 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(gotoPreviousEmptyLine()));
342 mActions
[GotoNextEmptyLine
] = act
= new QAction( tr("Go to Next Empty Line"), this);
343 act
->setShortcut(tr("Ctrl+Down", "Go to Next Empty Line"));
344 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
345 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(gotoNextEmptyLine()));
347 mActions
[SelectRegion
] = act
= new QAction( tr("Select Region"), this);
348 act
->setShortcut(tr("Ctrl+Shift+R", "Select Region"));
349 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
350 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(selectCurrentRegion()));
354 mActions
[EnlargeFont
] = act
= new QAction(
355 QIcon::fromTheme("zoom-in"), tr("&Enlarge Font"), this);
356 act
->setShortcut(tr("Ctrl++", "Enlarge font"));
357 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
358 act
->setStatusTip(tr("Increase displayed font size"));
359 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(zoomIn()));
361 mActions
[ShrinkFont
] = act
= new QAction(
362 QIcon::fromTheme("zoom-out"), tr("&Shrink Font"), this);
363 act
->setShortcut( tr("Ctrl+-", "Shrink font"));
364 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
365 act
->setStatusTip(tr("Decrease displayed font size"));
366 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(zoomOut()));
368 mActions
[ResetFontSize
] = act
= new QAction(
369 QIcon::fromTheme("zoom-reset"), tr("&Reset Font Size"), this);
370 act
->setShortcut( tr("Ctrl+0", "Reset font"));
371 act
->setStatusTip(tr("Reset displayed font size"));
372 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(resetFontSize()));
374 mActions
[ShowWhitespace
] = act
= new QAction(tr("Show Spaces and Tabs"), this);
375 act
->setCheckable(true);
376 mSigMux
->connect(act
, SIGNAL(triggered(bool)), SLOT(setShowWhitespace(bool)));
378 mActions
[NextDocument
] = act
= new QAction(tr("Next Document"), this);
379 act
->setShortcut( tr("Alt+Right", "Next Document"));
380 connect(act
, SIGNAL(triggered()), this, SLOT(showNextDocument()));
382 mActions
[PreviousDocument
] = act
= new QAction(tr("Previous Document"), this);
383 act
->setShortcut( tr("Alt+Left", "Next Document"));
384 connect(act
, SIGNAL(triggered()), this, SLOT(showPreviousDocument()));
386 mActions
[SwitchDocument
] = act
= new QAction(tr("Switch Document"), this);
387 act
->setShortcut( tr("Ctrl+Tab", "Switch Document"));
388 connect(act
, SIGNAL(triggered()), this, SLOT(switchDocument()));
390 mActions
[SplitHorizontally
] = act
= new QAction(tr("Split To Right"), this);
391 connect(act
, SIGNAL(triggered()), this, SLOT(splitHorizontally()));
393 mActions
[SplitVertically
] = act
= new QAction(tr("Split To Bottom"), this);
394 connect(act
, SIGNAL(triggered()), this, SLOT(splitVertically()));
396 mActions
[RemoveCurrentSplit
] = act
= new QAction(tr("Remove Current Split"), this);
397 connect(act
, SIGNAL(triggered()), this, SLOT(removeCurrentSplit()));
399 mActions
[RemoveAllSplits
] = act
= new QAction(tr("Remove All Splits"), this);
400 connect(act
, SIGNAL(triggered()), this, SLOT(removeAllSplits()));
404 mActions
[EvaluateCurrentDocument
] = act
= new QAction(
405 QIcon::fromTheme("media-playback-start"), tr("Evaluate &File"), this);
406 act
->setStatusTip(tr("Evaluate current File"));
407 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
408 connect(act
, SIGNAL(triggered()), this, SLOT(evaluateDocument()));
410 mActions
[EvaluateRegion
] = act
= new QAction(
411 QIcon::fromTheme("media-playback-start"), tr("&Evaluate Selection, Line or Region"), this);
412 act
->setShortcut(tr("Ctrl+Return", "Evaluate region"));
413 act
->setStatusTip(tr("Evaluate current region"));
414 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
415 connect(act
, SIGNAL(triggered()), this, SLOT(evaluateRegion()));
417 mActions
[EvaluateLine
] = act
= new QAction(
418 QIcon::fromTheme("media-playback-startline"), tr("&Evaluate Line"), this);
419 act
->setShortcut(tr("Shift+Ctrl+Return", "Evaluate line"));
420 act
->setStatusTip(tr("Evaluate current line"));
421 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
422 connect(act
, SIGNAL(triggered()), this, SLOT(evaluateLine()));
424 mActions
[OpenDefinition
] = act
= new QAction(tr("Open Class/Method Definition"), this);
425 act
->setShortcut(tr("Ctrl+I", "Open definition of selected class or method"));
426 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
427 connect(act
, SIGNAL(triggered(bool)), this, SLOT(openDefinition()));
429 settings
->endGroup(); // IDE/shortcuts
431 for (int i
= 0; i
< ActionRoleCount
; ++i
)
432 settings
->addAction( mActions
[i
] );
434 // These actions are not added to any menu, so they have to be added
435 // at least to this widget, in order for the shortcuts to always respond:
436 addAction(mActions
[TriggerAutoCompletion
]);
437 addAction(mActions
[TriggerMethodCallAid
]);
438 addAction(mActions
[SwitchDocument
]);
440 // These actions have to be added because to the widget because they have
441 // Qt::WidgetWithChildrenShortcut context:
442 addAction(mActions
[Cut
]);
443 addAction(mActions
[Copy
]);
444 addAction(mActions
[Paste
]);
445 addAction(mActions
[EnlargeFont
]);
446 addAction(mActions
[ShrinkFont
]);
447 addAction(mActions
[OpenDefinition
]);
448 addAction(mActions
[EvaluateCurrentDocument
]);
449 addAction(mActions
[EvaluateRegion
]);
450 addAction(mActions
[EvaluateLine
]);
451 addAction(mActions
[ToggleComment
]);
452 addAction(mActions
[ToggleOverwriteMode
]);
453 addAction(mActions
[CopyLineUp
]);
454 addAction(mActions
[CopyLineDown
]);
455 addAction(mActions
[MoveLineUp
]);
456 addAction(mActions
[MoveLineDown
]);
457 addAction(mActions
[GotoPreviousBlock
]);
458 addAction(mActions
[GotoNextBlock
]);
459 addAction(mActions
[GotoPreviousEmptyLine
]);
460 addAction(mActions
[GotoNextEmptyLine
]);
461 addAction(mActions
[SelectRegion
]);
464 void MultiEditor::updateActions()
466 CodeEditor
*editor
= currentEditor();
467 QTextDocument
*doc
= editor
? editor
->textDocument() : 0;
469 mActions
[Undo
]->setEnabled( doc
&& doc
->isUndoAvailable() );
470 mActions
[Redo
]->setEnabled( doc
&& doc
->isRedoAvailable() );
471 mActions
[Copy
]->setEnabled( editor
&& editor
->textCursor().hasSelection() );
472 mActions
[Cut
]->setEnabled( mActions
[Copy
]->isEnabled() );
473 mActions
[Paste
]->setEnabled( editor
);
474 mActions
[ToggleComment
]->setEnabled( editor
);
475 mActions
[ToggleOverwriteMode
]->setEnabled( editor
);
476 mActions
[CopyLineUp
]->setEnabled( editor
);
477 mActions
[CopyLineDown
]->setEnabled( editor
);
478 mActions
[MoveLineUp
]->setEnabled( editor
);
479 mActions
[MoveLineDown
]->setEnabled( editor
);
480 mActions
[GotoPreviousBlock
]->setEnabled( editor
);
481 mActions
[GotoNextBlock
]->setEnabled( editor
);
482 mActions
[GotoPreviousEmptyLine
]->setEnabled( editor
);
483 mActions
[GotoNextEmptyLine
]->setEnabled( editor
);
484 mActions
[SelectRegion
]->setEnabled( editor
);
486 mActions
[IndentLineOrRegion
]->setEnabled( editor
);
487 mActions
[EnlargeFont
]->setEnabled( editor
);
488 mActions
[ShrinkFont
]->setEnabled( editor
);
489 mActions
[OpenDefinition
]->setEnabled( editor
);
490 mActions
[EvaluateCurrentDocument
]->setEnabled( editor
);
491 mActions
[EvaluateRegion
]->setEnabled( editor
);
492 mActions
[EvaluateLine
]->setEnabled( editor
);
493 mActions
[ResetFontSize
]->setEnabled( editor
);
494 mActions
[ShowWhitespace
]->setEnabled( editor
);
495 mActions
[ShowWhitespace
]->setChecked( editor
&& editor
->showWhitespace() );
498 void MultiEditor::applySettings( Settings::Manager
*s
)
500 s
->beginGroup("IDE/editor");
501 mStepForwardEvaluation
= s
->value("stepForwardEvaluation").toBool();
505 static QVariantList
saveBoxState( CodeEditorBox
*box
)
507 // save editors in reverse order - first one is last shown.
508 QVariantList boxData
;
509 int idx
= box
->history().count();
511 CodeEditor
*editor
= box
->history()[idx
];
512 if (!editor
->document()->filePath().isEmpty()) {
513 QVariantMap editorData
;
514 editorData
.insert("file", editor
->document()->filePath());
515 editorData
.insert("position", editor
->textCursor().position());
516 boxData
.append( editorData
);
522 static QVariantMap
saveSplitterState( QSplitter
*splitter
)
524 QVariantMap splitterData
;
526 splitterData
.insert( "state", splitter
->saveState().toBase64() );
528 QVariantList childrenData
;
530 int childCount
= splitter
->count();
531 for (int idx
= 0; idx
< childCount
; idx
++) {
532 QWidget
*child
= splitter
->widget(idx
);
534 CodeEditorBox
*box
= qobject_cast
<CodeEditorBox
*>(child
);
536 QVariantList boxData
= saveBoxState(box
);
537 childrenData
.append( QVariant(boxData
) );
541 QSplitter
*childSplitter
= qobject_cast
<QSplitter
*>(child
);
543 QVariantMap childSplitterData
= saveSplitterState(childSplitter
);
544 childrenData
.append( QVariant(childSplitterData
) );
548 splitterData
.insert( "elements", childrenData
);
553 void MultiEditor::saveSession( Session
*session
)
555 session
->remove( "editors" );
556 session
->setValue( "editors", saveSplitterState(mSplitter
) );
559 void MultiEditor::loadBoxState( CodeEditorBox
*box
, const QVariantList
& data
)
561 mCurrentEditorBox
= box
;
562 foreach( QVariant docVar
, data
)
564 QVariantMap docData
= docVar
.value
<QVariantMap
>();
565 QString docPath
= docData
.value("file").toString();
566 int docPos
= docData
.value("position").toInt();
567 Main::instance()->documentManager()->open( docPath
, docPos
);
571 void MultiEditor::loadSplitterState( QSplitter
*splitter
, const QVariantMap
& data
)
573 QByteArray state
= QByteArray::fromBase64( data
.value("state").value
<QByteArray
>() );
575 QVariantList childrenData
= data
.value("elements").value
<QVariantList
>();
576 foreach (const QVariant
& childVar
, childrenData
) {
577 if (childVar
.type() == QVariant::List
) {
578 CodeEditorBox
*childBox
= newBox();
579 splitter
->addWidget(childBox
);
580 QVariantList childBoxData
= childVar
.value
<QVariantList
>();
581 loadBoxState( childBox
, childBoxData
);
583 else if (childVar
.type() == QVariant::Map
) {
584 QSplitter
*childSplitter
= new QSplitter
;
585 splitter
->addWidget(childSplitter
);
586 QVariantMap childSplitterData
= childVar
.value
<QVariantMap
>();
587 loadSplitterState( childSplitter
, childSplitterData
);
591 if (!splitter
->restoreState(state
))
592 qWarning("MultiEditor: could not restore splitter state!");
595 void MultiEditor::switchSession( Session
*session
)
597 DocumentManager
*docManager
= Main::instance()->documentManager();
598 QList
<Document
*> docs
= docManager
->documents();
599 foreach (Document
*doc
, docs
)
600 docManager
->close(doc
);
603 mSplitter
= new MultiSplitter();
605 CodeEditorBox
*firstBox
= 0;
607 if (session
&& session
->contains("editors")) {
608 QVariantMap splitterData
= session
->value("editors").value
<QVariantMap
>();
609 loadSplitterState( mSplitter
, splitterData
);
611 if (mSplitter
->count()) {
612 firstBox
= mSplitter
->findChild
<CodeEditorBox
>();
614 qWarning("Session seems to contain invalid editor split data!");
616 mSplitter
= new MultiSplitter();
623 mSplitter
->addWidget( firstBox
);
626 mCurrentEditorBox
= 0; // ensure complete update
627 setCurrentBox( firstBox
);
629 layout()->addWidget(mSplitter
);
632 // create a document on new session
633 docManager
->create();
635 firstBox
->setFocus(Qt::OtherFocusReason
); // ensure focus
638 void MultiEditor::setCurrent( Document
*doc
)
640 int tabIdx
= tabForDocument(doc
);
642 mTabs
->setCurrentIndex(tabIdx
);
645 void MultiEditor::showNextDocument()
647 int currentIndex
= mTabs
->currentIndex();
648 mTabs
->setCurrentIndex( qMin(currentIndex
+ 1, mTabs
->count() - 1) );
651 void MultiEditor::showPreviousDocument()
653 int currentIndex
= mTabs
->currentIndex();
654 mTabs
->setCurrentIndex( qMax(0, currentIndex
- 1) );
657 void MultiEditor::switchDocument()
659 CodeEditorBox
*box
= currentBox();
661 DocumentSelectPopUp
* popup
= new DocumentSelectPopUp(box
->history(), this);
663 QRect
popupRect(0,0,300,200);
664 popupRect
.moveCenter(rect().center());
665 popup
->resize(popupRect
.size());
666 QPoint globalPosition
= mapToGlobal(popupRect
.topLeft());
668 Document
* selectedDocument
= popup
->exec(globalPosition
);
670 if (selectedDocument
)
671 box
->setDocument(selectedDocument
);
674 void MultiEditor::onOpen( Document
*doc
, int pos
)
676 QTextDocument
*tdoc
= doc
->textDocument();
679 if(tdoc
->isModified())
680 icon
= mDocModifiedIcon
;
682 int newTabIndex
= mTabs
->addTab( icon
, doc
->title() );
683 mTabs
->setTabData( newTabIndex
, QVariant::fromValue
<Document
*>(doc
) );
685 currentBox()->setDocument(doc
, pos
);
686 currentBox()->setFocus(Qt::OtherFocusReason
);
689 void MultiEditor::onClose( Document
*doc
)
691 int tabIdx
= tabForDocument(doc
);
693 mTabs
->removeTab(tabIdx
);
694 // TODO: each box should switch document according to their own history
697 void MultiEditor::show( Document
*doc
, int pos
)
699 currentBox()->setDocument(doc
, pos
);
700 currentBox()->setFocus(Qt::OtherFocusReason
);
703 void MultiEditor::update( Document
*doc
)
705 int tabIdx
= tabForDocument(doc
);
707 mTabs
->setTabText(tabIdx
, doc
->title());
710 void MultiEditor::onCloseRequest( int index
)
712 Document
*doc
= documentForTab(index
);
714 MainWindow::close(doc
);
717 void MultiEditor::onCurrentTabChanged( int index
)
722 Document
*doc
= documentForTab(index
);
726 CodeEditorBox
*curBox
= currentBox();
727 curBox
->setDocument(doc
);
728 curBox
->setFocus(Qt::OtherFocusReason
);
731 void MultiEditor::onCurrentEditorChanged(CodeEditor
*editor
)
733 setCurrentEditor(editor
);
736 void MultiEditor::onBoxActivated(CodeEditorBox
*box
)
741 void MultiEditor::onModificationChanged( bool modified
)
743 Q_ASSERT(currentEditor());
745 int tabIdx
= tabForDocument( currentEditor()->document() );
751 icon
= mDocModifiedIcon
;
752 mTabs
->setTabIcon( tabIdx
, icon
);
755 void MultiEditor::evaluateRegion()
757 CodeEditor
* editor
= currentEditor();
763 // Try current selection
764 QTextCursor cursor
= editor
->textCursor();
765 if (cursor
.hasSelection())
766 text
= cursor
.selectedText();
768 // If no selection, try current region
769 cursor
= editor
->currentRegion();
770 if (!cursor
.isNull()) {
771 // if region is in a single line, evaluate complete line
773 QTextCursor
selectionStart (cursor
);
774 selectionStart
.setPosition(cursor
.selectionStart());
776 QTextCursor
selectionEnd (cursor
);
777 selectionEnd
.setPosition(cursor
.selectionEnd());
779 if (selectionStart
.block() == selectionEnd
.block())
780 cursor
.select(QTextCursor::LineUnderCursor
);
782 text
= cursor
.selectedText();
784 // If no current region, try current line
785 cursor
= editor
->textCursor();
786 text
= cursor
.block().text();
787 if( mStepForwardEvaluation
) {
788 QTextCursor newCursor
= cursor
;
789 newCursor
.movePosition(QTextCursor::NextBlock
);
790 newCursor
.movePosition(QTextCursor::EndOfBlock
);
791 editor
->setTextCursor(newCursor
);
793 // Adjust cursor for code blinking:
794 cursor
.movePosition(QTextCursor::StartOfBlock
);
795 cursor
.movePosition(QTextCursor::EndOfBlock
, QTextCursor::KeepAnchor
);
802 text
.replace( QChar( 0x2029 ), QChar( '\n' ) );
804 Main::evaluateCode(text
);
806 editor
->blinkCode( cursor
);
809 void MultiEditor::evaluateLine()
811 CodeEditor
* editor
= currentEditor();
817 // Try current selection
818 QTextCursor cursor
= editor
->textCursor();
819 cursor
.select(QTextCursor::LineUnderCursor
);
820 text
= cursor
.selectedText();
822 if( mStepForwardEvaluation
) {
823 QTextCursor newCursor
= cursor
;
824 newCursor
.movePosition(QTextCursor::NextBlock
);
825 newCursor
.movePosition(QTextCursor::EndOfBlock
);
826 editor
->setTextCursor(newCursor
);
832 // Adjust cursor for code blinking:
833 cursor
.movePosition(QTextCursor::StartOfBlock
);
834 cursor
.movePosition(QTextCursor::EndOfBlock
, QTextCursor::KeepAnchor
);
836 text
.replace( QChar( 0x2029 ), QChar( '\n' ) );
838 Main::evaluateCode(text
);
840 editor
->blinkCode( cursor
);
843 void MultiEditor::evaluateDocument()
845 CodeEditor
* editor
= currentEditor();
849 QString documentText
= editor
->textDocument()->toPlainText();
850 Main::evaluateCode(documentText
);
853 void MultiEditor::openDefinition(const QString
&string
)
855 QString definitionString
= string
.trimmed();
856 if (definitionString
.isEmpty())
859 LookupDialog
dialog(this);
860 dialog
.query(definitionString
);
864 void MultiEditor::openDefinition()
866 CodeEditor
* editor
= currentEditor();
867 QTextCursor textCursor
= editor
->textCursor();
869 if (!textCursor
.hasSelection())
870 textCursor
.select(QTextCursor::WordUnderCursor
);
872 openDefinition(textCursor
.selectedText());
875 bool MultiEditor::openDocumentation(const QString
&string
)
877 QString symbol
= string
.trimmed();
878 if (symbol
.isEmpty())
881 QString code
= QString("HelpBrowser.openHelpFor(\"%1\")").arg(symbol
);
882 Main::evaluateCode(code
, true);
886 bool MultiEditor::openDocumentation()
888 CodeEditor
* editor
= currentEditor();
892 QTextCursor textCursor
= editor
->textCursor();
894 if (!textCursor
.hasSelection())
895 textCursor
.select(QTextCursor::WordUnderCursor
);
897 return openDocumentation(textCursor
.selectedText());
900 Document
* MultiEditor::documentForTab( int index
)
902 return mTabs
->tabData(index
).value
<Document
*>();
905 int MultiEditor::tabForDocument( Document
* doc
)
907 int tabCount
= mTabs
->count();
908 for (int idx
= 0; idx
< tabCount
; ++idx
) {
909 Document
*tabDoc
= documentForTab(idx
);
910 if (tabDoc
&& tabDoc
== doc
)
916 CodeEditorBox
*MultiEditor::newBox()
918 CodeEditorBox
*box
= new CodeEditorBox();
920 connect(box
, SIGNAL(activated(CodeEditorBox
*)),
921 this, SLOT(onBoxActivated(CodeEditorBox
*)));
926 void MultiEditor::setCurrentBox( CodeEditorBox
* box
)
928 if (mCurrentEditorBox
== box
)
931 mCurrentEditorBox
= box
;
932 mBoxSigMux
->setCurrentObject(box
);
933 setCurrentEditor( box
->currentEditor() );
935 mCurrentEditorBox
->setActive();
938 void MultiEditor::setCurrentEditor( CodeEditor
* editor
)
941 int tabIndex
= tabForDocument(editor
->document());
943 mTabs
->setCurrentIndex(tabIndex
);
946 mSigMux
->setCurrentObject(editor
);
949 Document
*currentDocument
= editor
? editor
->document() : 0;
950 Main::scProcess()->setActiveDocument(currentDocument
);
951 emit
currentDocumentChanged(currentDocument
);
954 CodeEditor
*MultiEditor::currentEditor()
956 return currentBox()->currentEditor();
959 void MultiEditor::split( Qt::Orientation splitDirection
)
961 CodeEditorBox
*box
= newBox();
962 CodeEditorBox
*curBox
= currentBox();
963 CodeEditor
*curEditor
= curBox
->currentEditor();
966 box
->setDocument(curEditor
->document(), curEditor
->textCursor().position());
968 mSplitter
->insertWidget(box
, curBox
, splitDirection
);
969 box
->setFocus( Qt::OtherFocusReason
);
972 void MultiEditor::removeCurrentSplit()
974 int boxCount
= mSplitter
->findChildren
<CodeEditorBox
*>().count();
976 // Do not allow removing the one and only box.
979 CodeEditorBox
*box
= currentBox();
980 mSplitter
->removeWidget(box
);
982 // switch current box to first box found:
983 box
= mSplitter
->findChild
<CodeEditorBox
>();
986 box
->setFocus( Qt::OtherFocusReason
);
989 void MultiEditor::removeAllSplits()
991 CodeEditorBox
*box
= currentBox();
993 Q_ASSERT(mSplitter
->count());
994 if (mSplitter
->count() == 1 && mSplitter
->widget(0) == box
)
998 MultiSplitter
*newSplitter
= new MultiSplitter
;
999 newSplitter
->addWidget(box
);
1002 mSplitter
= newSplitter
;
1003 layout()->addWidget(newSplitter
);
1005 box
->setFocus( Qt::OtherFocusReason
);
1008 } // namespace ScIDE