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/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"
33 #include "yaml-cpp/node.h"
34 #include "yaml-cpp/parser.h"
36 #include <QApplication>
40 #include <QHBoxLayout>
41 #include <QHeaderView>
45 #include <QStandardItemModel>
48 #include <QTreeWidget>
49 #include <QVBoxLayout>
54 class DocumentSelectPopUp
: public QDialog
57 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 bool event(QEvent
* event
)
93 if (event
->type() == QEvent::ShortcutOverride
) {
97 return QWidget::event(event
);
100 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
106 && (ke
->key() == Qt::Key_Alt
|| ke
->key() == -1))) {
110 QDialog::keyReleaseEvent(ke
);
113 void keyPressEvent(QKeyEvent
* ke
)
123 case Qt::Key_Backtab
:
136 QDialog::keyPressEvent(ke
);
139 void paintEvent( QPaintEvent
* )
141 QPainter
painter(this);
142 painter
.setBrush(Qt::NoBrush
);
143 painter
.setPen(palette().color(QPalette::Dark
));
144 painter
.drawRect(rect().adjusted(0,0,-1,-1));
149 int row
= mListView
->currentIndex().row() + 1;
150 if (!mModel
->hasIndex(row
, 0))
153 QModelIndex nextIndex
= mModel
->index(row
, 0);
154 mListView
->setCurrentIndex(nextIndex
);
159 int row
= mListView
->currentIndex().row() - 1;
160 if (!mModel
->hasIndex(row
, 0))
161 row
= mModel
->rowCount() - 1;
163 QModelIndex nextIndex
= mModel
->index(row
, 0);
164 mListView
->setCurrentIndex(nextIndex
);
167 Document
* currentDocument()
169 QStandardItem
* currentItem
= mModel
->itemFromIndex(mListView
->currentIndex());
170 return currentItem
? currentItem
->data().value
<Document
*>()
174 void populateModel( const CodeEditorBox::History
& history
)
176 QList
<Document
*> displayDocuments
;
177 foreach(ScCodeEditor
*editor
, history
)
178 displayDocuments
<< editor
->document();
180 QList
<Document
*> managerDocuments
= Main::documentManager()->documents();
181 foreach(Document
*document
, managerDocuments
)
182 if (!displayDocuments
.contains(document
))
183 displayDocuments
<< document
;
185 foreach (Document
* document
, displayDocuments
) {
186 QStandardItem
* item
= new QStandardItem(document
->title());
187 item
->setData(QVariant::fromValue(document
));
188 mModel
->appendRow(item
);
192 QListView
*mListView
;
193 QStandardItemModel
*mModel
;
196 MultiEditor::MultiEditor( Main
*main
, QWidget
* parent
) :
198 mSigMux(new SignalMultiplexer(this)),
199 mBoxSigMux(new SignalMultiplexer(this)),
200 mDocModifiedIcon( QApplication::style()->standardIcon(QStyle::SP_DialogSaveButton
) )
203 mTabs
->setDocumentMode(true);
204 mTabs
->setTabsClosable(true);
205 mTabs
->setMovable(true);
206 mTabs
->setUsesScrollButtons(true);
207 mTabs
->setDrawBase(false);
209 CodeEditorBox
*defaultBox
= newBox();
211 mSplitter
= new MultiSplitter();
212 mSplitter
->addWidget(defaultBox
);
214 QVBoxLayout
*l
= new QVBoxLayout
;
215 l
->setContentsMargins(0,0,0,0);
218 l
->addWidget(mSplitter
);
221 makeSignalConnections();
223 mSigMux
->connect(SIGNAL(modificationChanged(bool)),
224 this, SLOT(onModificationChanged(bool)));
226 mBoxSigMux
->connect(SIGNAL(currentChanged(ScCodeEditor
*)),
227 this, SLOT(onCurrentEditorChanged(ScCodeEditor
*)));
231 setCurrentBox( defaultBox
); // will updateActions();
233 applySettings(main
->settings());
236 void MultiEditor::makeSignalConnections()
238 DocumentManager
*docManager
= Main::documentManager();
240 connect(docManager
, SIGNAL(opened(Document
*, int, int)),
241 this, SLOT(onOpen(Document
*, int, int)));
242 connect(docManager
, SIGNAL(closed(Document
*)),
243 this, SLOT(onClose(Document
*)));
244 connect(docManager
, SIGNAL(saved(Document
*)),
245 this, SLOT(update(Document
*)));
246 connect(docManager
, SIGNAL(showRequest(Document
*, int, int)),
247 this, SLOT(show(Document
*, int, int)));
249 connect(mTabs
, SIGNAL(currentChanged(int)),
250 this, SLOT(onCurrentTabChanged(int)));
251 connect(mTabs
, SIGNAL(tabCloseRequested(int)),
252 this, SLOT(onCloseRequest(int)));
255 void MultiEditor::breakSignalConnections()
257 DocumentManager
*docManager
= Main::documentManager();
258 docManager
->disconnect(this);
259 mTabs
->disconnect(this);
262 void MultiEditor::createActions()
264 Settings::Manager
*settings
= Main::settings();
265 settings
->beginGroup("IDE/shortcuts");
271 mActions
[Undo
] = act
= new QAction(
272 QIcon::fromTheme("edit-undo"), tr("&Undo"), this);
273 act
->setShortcut(tr("Ctrl+Z", "Undo"));
274 act
->setStatusTip(tr("Undo last editing action"));
275 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(undo()));
276 mSigMux
->connect(SIGNAL(undoAvailable(bool)), act
, SLOT(setEnabled(bool)));
278 mActions
[Redo
] = act
= new QAction(
279 QIcon::fromTheme("edit-redo"), tr("Re&do"), this);
280 act
->setShortcut(tr("Ctrl+Shift+Z", "Redo"));
281 act
->setStatusTip(tr("Redo next editing action"));
282 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(redo()));
283 mSigMux
->connect(SIGNAL(redoAvailable(bool)), act
, SLOT(setEnabled(bool)));
285 mActions
[Cut
] = act
= new QAction(
286 QIcon::fromTheme("edit-cut"), tr("Cu&t"), this);
287 act
->setShortcut(tr("Ctrl+X", "Cut"));
288 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
289 act
->setStatusTip(tr("Cut text to clipboard"));
290 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(cut()));
291 mSigMux
->connect(SIGNAL(copyAvailable(bool)), act
, SLOT(setEnabled(bool)));
293 mActions
[Copy
] = act
= new QAction(
294 QIcon::fromTheme("edit-copy"), tr("&Copy"), this);
295 act
->setShortcut(tr("Ctrl+C", "Copy"));
296 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
297 act
->setStatusTip(tr("Copy text to clipboard"));
298 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(copy()));
299 mSigMux
->connect(SIGNAL(copyAvailable(bool)), act
, SLOT(setEnabled(bool)));
301 mActions
[Paste
] = act
= new QAction(
302 QIcon::fromTheme("edit-paste"), tr("&Paste"), this);
303 act
->setShortcut(tr("Ctrl+V", "Paste"));
304 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
305 act
->setStatusTip(tr("Paste text from clipboard"));
306 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(paste()));
308 mActions
[IndentLineOrRegion
] = act
= new QAction(
309 QIcon::fromTheme("format-indent-line"), tr("Indent Line or Region"), this);
310 act
->setStatusTip(tr("Indent Line or Region"));
311 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(indent()));
313 mActions
[TriggerAutoCompletion
] = act
= new QAction(tr("Trigger Autocompletion"), this);
314 act
->setStatusTip(tr("Suggest possible completions of text at cursor"));
315 act
->setShortcut(tr("Ctrl+Space", "Trigger Autocompletion"));
316 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
317 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(triggerAutoCompletion()));
319 mActions
[TriggerMethodCallAid
] = act
= new QAction(tr("Trigger Method Call Aid"), this);
320 act
->setStatusTip(tr("Show arguments for currently typed method call"));
321 act
->setShortcut(tr("Ctrl+Shift+Space", "Trigger Method Call Aid"));
322 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
323 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(triggerMethodCallAid()));
325 mActions
[ToggleComment
] = act
= new QAction(
326 QIcon::fromTheme("edit-comment"), tr("Toggle &Comment"), this);
327 act
->setShortcut(tr("Ctrl+/", "Toggle Comment"));
328 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
329 act
->setStatusTip(tr("Toggle Comment"));
330 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(toggleComment()));
332 mActions
[ToggleOverwriteMode
] = act
= new QAction(
333 QIcon::fromTheme("edit-overwrite"), tr("Toggle &Overwrite Mode"), this);
334 act
->setShortcut(tr("Insert", "Toggle Overwrite Mode"));
335 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
336 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(toggleOverwriteMode()));
338 mActions
[CopyLineUp
] = act
= new QAction(
339 QIcon::fromTheme("edit-copylineup"), tr("Copy Line Up"), this);
340 act
->setShortcut(tr("Ctrl+Alt+Up", "Copy Line Up"));
341 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
342 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(copyLineUp()));
344 mActions
[CopyLineDown
] = act
= new QAction(
345 QIcon::fromTheme("edit-copylinedown"), tr("Copy Line Down"), this);
346 act
->setShortcut(tr("Ctrl+Alt+Down", "Copy Line Up"));
347 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
348 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(copyLineDown()));
350 mActions
[MoveLineUp
] = act
= new QAction(
351 QIcon::fromTheme("edit-movelineup"), tr("move Line Up"), this);
352 act
->setShortcut(tr("Ctrl+Shift+Up", "Move Line Up"));
353 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
354 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(moveLineUp()));
356 mActions
[MoveLineDown
] = act
= new QAction(
357 QIcon::fromTheme("edit-movelinedown"), tr("Move Line Down"), this);
358 act
->setShortcut(tr("Ctrl+Shift+Down", "Move Line Up"));
359 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
360 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(moveLineDown()));
362 mActions
[GotoPreviousBlock
] = act
= new QAction(
363 QIcon::fromTheme("edit-gotopreviousblock"), tr("Go to Previous Block"), this);
364 act
->setShortcut(tr("Ctrl+[", "Go to Previous Block"));
365 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
366 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(gotoPreviousBlock()));
368 mActions
[GotoNextBlock
] = act
= new QAction(
369 QIcon::fromTheme("edit-gotonextblock"), tr("Go to Next Block"), this);
370 act
->setShortcut(tr("Ctrl+]", "Go to Next Block"));
371 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
372 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(gotoNextBlock()));
374 mActions
[GotoPreviousRegion
] = act
= new QAction(
375 QIcon::fromTheme("edit-gotopreviousregion"), tr("Go to Previous Region"), this);
376 act
->setShortcut(tr("Alt+[", "Go to Previous Region"));
377 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
378 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(gotoPreviousRegion()));
380 mActions
[GotoNextRegion
] = act
= new QAction(
381 QIcon::fromTheme("edit-gotonextregion"), tr("Go to Next Region"), this);
382 act
->setShortcut(tr("Alt+]", "Go to Next Region"));
383 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
384 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(gotoNextRegion()));
386 mActions
[GotoPreviousEmptyLine
] = act
= new QAction( tr("Go to Previous Empty Line"), this);
387 act
->setShortcut(tr("Ctrl+Up", "Go to Previous Empty Line"));
388 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
389 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(gotoPreviousEmptyLine()));
391 mActions
[GotoNextEmptyLine
] = act
= new QAction( tr("Go to Next Empty Line"), this);
392 act
->setShortcut(tr("Ctrl+Down", "Go to Next Empty Line"));
393 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
394 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(gotoNextEmptyLine()));
396 mActions
[SelectRegion
] = act
= new QAction( tr("Select Region"), this);
397 act
->setShortcut(tr("Ctrl+Shift+R", "Select Region"));
398 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
399 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(selectCurrentRegion()));
403 mActions
[EnlargeFont
] = act
= new QAction(
404 QIcon::fromTheme("zoom-in"), tr("&Enlarge Font"), this);
405 act
->setShortcut(tr("Ctrl++", "Enlarge font"));
406 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
407 act
->setStatusTip(tr("Increase displayed font size"));
408 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(zoomIn()));
410 mActions
[ShrinkFont
] = act
= new QAction(
411 QIcon::fromTheme("zoom-out"), tr("&Shrink Font"), this);
412 act
->setShortcut( tr("Ctrl+-", "Shrink font"));
413 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
414 act
->setStatusTip(tr("Decrease displayed font size"));
415 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(zoomOut()));
417 mActions
[ResetFontSize
] = act
= new QAction(
418 QIcon::fromTheme("zoom-reset"), tr("&Reset Font Size"), this);
419 act
->setShortcut( tr("Ctrl+0", "Reset font"));
420 act
->setStatusTip(tr("Reset displayed font size"));
421 mSigMux
->connect(act
, SIGNAL(triggered()), SLOT(resetFontSize()));
423 mActions
[ShowWhitespace
] = act
= new QAction(tr("Show Spaces and Tabs"), this);
424 act
->setCheckable(true);
425 mSigMux
->connect(act
, SIGNAL(triggered(bool)), SLOT(setShowWhitespace(bool)));
427 mActions
[NextDocument
] = act
= new QAction(tr("Next Document"), this);
428 act
->setShortcut( tr("Alt+Right", "Next Document"));
429 connect(act
, SIGNAL(triggered()), this, SLOT(showNextDocument()));
431 mActions
[PreviousDocument
] = act
= new QAction(tr("Previous Document"), this);
432 act
->setShortcut( tr("Alt+Left", "Next Document"));
433 connect(act
, SIGNAL(triggered()), this, SLOT(showPreviousDocument()));
435 mActions
[SwitchDocument
] = act
= new QAction(tr("Switch Document"), this);
436 act
->setShortcut( tr("Ctrl+Tab", "Switch Document"));
437 connect(act
, SIGNAL(triggered()), this, SLOT(switchDocument()));
439 mActions
[SplitHorizontally
] = act
= new QAction(tr("Split To Right"), this);
440 act
->setShortcut( tr("Ctrl+P, 3", "Split To Right"));
441 connect(act
, SIGNAL(triggered()), this, SLOT(splitHorizontally()));
443 mActions
[SplitVertically
] = act
= new QAction(tr("Split To Bottom"), this);
444 act
->setShortcut( tr("Ctrl+P, 2", "Split To Bottom"));
445 connect(act
, SIGNAL(triggered()), this, SLOT(splitVertically()));
447 mActions
[RemoveCurrentSplit
] = act
= new QAction(tr("Remove Current Split"), this);
448 act
->setShortcut( tr("Ctrl+P, 1", "Remove Current Split"));
449 connect(act
, SIGNAL(triggered()), this, SLOT(removeCurrentSplit()));
451 mActions
[RemoveAllSplits
] = act
= new QAction(tr("Remove All Splits"), this);
452 act
->setShortcut( tr("Ctrl+P, 0", "Remove All Splits"));
453 connect(act
, SIGNAL(triggered()), this, SLOT(removeAllSplits()));
457 mActions
[EvaluateCurrentDocument
] = act
= new QAction(
458 QIcon::fromTheme("media-playback-start"), tr("Evaluate &File"), this);
459 act
->setStatusTip(tr("Evaluate current File"));
460 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
461 connect(act
, SIGNAL(triggered()), this, SLOT(evaluateDocument()));
463 mActions
[EvaluateRegion
] = act
= new QAction(
464 QIcon::fromTheme("media-playback-start"), tr("&Evaluate Selection, Line or Region"), this);
465 act
->setShortcut(tr("Ctrl+Return", "Evaluate region"));
466 act
->setStatusTip(tr("Evaluate current region"));
467 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
468 connect(act
, SIGNAL(triggered()), this, SLOT(evaluateRegion()));
470 mActions
[EvaluateLine
] = act
= new QAction(
471 QIcon::fromTheme("media-playback-startline"), tr("&Evaluate Line"), this);
472 act
->setShortcut(tr("Shift+Ctrl+Return", "Evaluate line"));
473 act
->setStatusTip(tr("Evaluate current line"));
474 act
->setShortcutContext(Qt::WidgetWithChildrenShortcut
);
475 connect(act
, SIGNAL(triggered()), this, SLOT(evaluateLine()));
477 settings
->endGroup(); // IDE/shortcuts
479 for (int i
= 0; i
< ActionRoleCount
; ++i
)
480 settings
->addAction( mActions
[i
] );
482 // These actions are not added to any menu, so they have to be added
483 // at least to this widget, in order for the shortcuts to always respond:
484 addAction(mActions
[TriggerAutoCompletion
]);
485 addAction(mActions
[TriggerMethodCallAid
]);
486 addAction(mActions
[SwitchDocument
]);
488 // These actions have to be added because to the widget because they have
489 // Qt::WidgetWithChildrenShortcut context:
490 addAction(mActions
[Cut
]);
491 addAction(mActions
[Copy
]);
492 addAction(mActions
[Paste
]);
493 addAction(mActions
[EnlargeFont
]);
494 addAction(mActions
[ShrinkFont
]);
495 addAction(mActions
[EvaluateCurrentDocument
]);
496 addAction(mActions
[EvaluateRegion
]);
497 addAction(mActions
[EvaluateLine
]);
498 addAction(mActions
[ToggleComment
]);
499 addAction(mActions
[ToggleOverwriteMode
]);
500 addAction(mActions
[CopyLineUp
]);
501 addAction(mActions
[CopyLineDown
]);
502 addAction(mActions
[MoveLineUp
]);
503 addAction(mActions
[MoveLineDown
]);
504 addAction(mActions
[GotoPreviousBlock
]);
505 addAction(mActions
[GotoNextBlock
]);
506 addAction(mActions
[GotoPreviousRegion
]);
507 addAction(mActions
[GotoNextRegion
]);
508 addAction(mActions
[GotoPreviousEmptyLine
]);
509 addAction(mActions
[GotoNextEmptyLine
]);
510 addAction(mActions
[SelectRegion
]);
513 void MultiEditor::updateActions()
515 ScCodeEditor
*editor
= currentEditor();
516 QTextDocument
*doc
= editor
? editor
->textDocument() : 0;
518 mActions
[Undo
]->setEnabled( doc
&& doc
->isUndoAvailable() );
519 mActions
[Redo
]->setEnabled( doc
&& doc
->isRedoAvailable() );
520 mActions
[Copy
]->setEnabled( editor
&& editor
->textCursor().hasSelection() );
521 mActions
[Cut
]->setEnabled( mActions
[Copy
]->isEnabled() );
522 mActions
[Paste
]->setEnabled( editor
);
523 mActions
[ToggleComment
]->setEnabled( editor
);
524 mActions
[ToggleOverwriteMode
]->setEnabled( editor
);
525 mActions
[CopyLineUp
]->setEnabled( editor
);
526 mActions
[CopyLineDown
]->setEnabled( editor
);
527 mActions
[MoveLineUp
]->setEnabled( editor
);
528 mActions
[MoveLineDown
]->setEnabled( editor
);
529 mActions
[GotoPreviousBlock
]->setEnabled( editor
);
530 mActions
[GotoNextBlock
]->setEnabled( editor
);
531 mActions
[GotoPreviousRegion
]->setEnabled( editor
);
532 mActions
[GotoNextRegion
]->setEnabled( editor
);
533 mActions
[GotoPreviousEmptyLine
]->setEnabled( editor
);
534 mActions
[GotoNextEmptyLine
]->setEnabled( editor
);
535 mActions
[SelectRegion
]->setEnabled( editor
);
537 mActions
[IndentLineOrRegion
]->setEnabled( editor
);
538 mActions
[EnlargeFont
]->setEnabled( editor
);
539 mActions
[ShrinkFont
]->setEnabled( editor
);
540 mActions
[EvaluateCurrentDocument
]->setEnabled( editor
);
541 mActions
[EvaluateRegion
]->setEnabled( editor
);
542 mActions
[EvaluateLine
]->setEnabled( editor
);
543 mActions
[ResetFontSize
]->setEnabled( editor
);
544 mActions
[ShowWhitespace
]->setEnabled( editor
);
545 mActions
[ShowWhitespace
]->setChecked( editor
&& editor
->showWhitespace() );
548 void MultiEditor::applySettings( Settings::Manager
*s
)
550 s
->beginGroup("IDE/editor");
551 mStepForwardEvaluation
= s
->value("stepForwardEvaluation").toBool();
555 static QVariantList
saveBoxState( CodeEditorBox
*box
, const QList
<Document
*> & documentList
)
557 // save editors in reverse order - first one is last shown.
558 QVariantList boxData
;
559 int idx
= box
->history().count();
561 ScCodeEditor
*editor
= box
->history()[idx
];
562 if (!editor
->document()->filePath().isEmpty()) {
563 int documentIndex
= documentList
.indexOf( editor
->document() );
564 Q_ASSERT(documentIndex
>= 0);
565 QVariantMap editorData
;
566 editorData
.insert("documentIndex", documentIndex
);
567 editorData
.insert("position", editor
->textCursor().position());
568 boxData
.append( editorData
);
574 static QVariantMap
saveSplitterState( QSplitter
*splitter
, const QList
<Document
*> & documentList
)
576 QVariantMap splitterData
;
578 splitterData
.insert( "state", splitter
->saveState().toBase64() );
580 QVariantList childrenData
;
582 int childCount
= splitter
->count();
583 for (int idx
= 0; idx
< childCount
; idx
++) {
584 QWidget
*child
= splitter
->widget(idx
);
586 CodeEditorBox
*box
= qobject_cast
<CodeEditorBox
*>(child
);
588 QVariantList boxData
= saveBoxState(box
, documentList
);
589 childrenData
.append( QVariant(boxData
) );
593 QSplitter
*childSplitter
= qobject_cast
<QSplitter
*>(child
);
595 QVariantMap childSplitterData
= saveSplitterState(childSplitter
, documentList
);
596 childrenData
.append( QVariant(childSplitterData
) );
600 splitterData
.insert( "elements", childrenData
);
605 void MultiEditor::saveSession( Session
*session
)
607 QList
<Document
*> documentList
;
609 QVariantList tabsData
;
610 int tabCount
= mTabs
->count();
611 for (int tabIdx
= 0; tabIdx
< tabCount
; ++tabIdx
) {
612 Document
*doc
= documentForTab(tabIdx
);
614 tabsData
<< doc
->filePath();
617 session
->setValue( "documents", QVariant::fromValue(tabsData
) );
619 session
->remove( "editors" );
620 session
->setValue( "editors", saveSplitterState(mSplitter
, documentList
) );
623 void MultiEditor::loadBoxState( CodeEditorBox
*box
,
624 const QVariantList
& data
, const QList
<Document
*> & documentList
)
626 int docCount
= documentList
.count();
627 foreach( QVariant docVar
, data
)
629 QVariantMap docData
= docVar
.value
<QVariantMap
>();
630 int docIndex
= docData
.value("documentIndex").toInt();
631 int docPos
= docData
.value("position").toInt();
632 if (docIndex
>= 0 && docIndex
< docCount
)
633 box
->setDocument( documentList
[docIndex
], docPos
);
637 void MultiEditor::loadSplitterState( QSplitter
*splitter
,
638 const QVariantMap
& data
, const QList
<Document
*> & documentList
)
640 QByteArray state
= QByteArray::fromBase64( data
.value("state").value
<QByteArray
>() );
642 QVariantList childrenData
= data
.value("elements").value
<QVariantList
>();
643 foreach (const QVariant
& childVar
, childrenData
) {
644 if (childVar
.type() == QVariant::List
) {
645 CodeEditorBox
*childBox
= newBox();
646 splitter
->addWidget(childBox
);
647 QVariantList childBoxData
= childVar
.value
<QVariantList
>();
648 loadBoxState( childBox
, childBoxData
, documentList
);
650 else if (childVar
.type() == QVariant::Map
) {
651 QSplitter
*childSplitter
= new QSplitter
;
652 splitter
->addWidget(childSplitter
);
653 QVariantMap childSplitterData
= childVar
.value
<QVariantMap
>();
654 loadSplitterState( childSplitter
, childSplitterData
, documentList
);
658 if (!splitter
->restoreState(state
))
659 qWarning("MultiEditor: could not restore splitter state!");
662 void MultiEditor::switchSession( Session
*session
)
664 ///// Going offline...
666 breakSignalConnections();
668 DocumentManager
*docManager
= Main::documentManager();
670 QList
<Document
*> documentList
= docManager
->documents();
673 foreach (Document
*doc
, documentList
)
674 docManager
->close(doc
);
677 while (mTabs
->count())
680 // remove all editors
683 documentList
.clear();
685 mSplitter
= new MultiSplitter();
687 CodeEditorBox
*firstBox
= 0;
691 // open documents saved in the session
692 QVariantList docDataList
= session
->value("documents").value
<QVariantList
>();
693 foreach( const QVariant
& docData
, docDataList
) {
694 QString filePath
= docData
.toString();
695 Document
* doc
= docManager
->open(filePath
, -1, 0, false);
700 foreach ( Document
* doc
, documentList
) {
703 int newTabIndex
= mTabs
->addTab( doc
->title() );
704 mTabs
->setTabData( newTabIndex
, QVariant::fromValue
<Document
*>(doc
) );
708 if (session
->contains("editors")) {
709 QVariantMap splitterData
= session
->value("editors").value
<QVariantMap
>();
710 loadSplitterState( mSplitter
, splitterData
, documentList
);
712 if (mSplitter
->count()) {
713 firstBox
= mSplitter
->findChild
<CodeEditorBox
>();
715 qWarning("Session seems to contain invalid editor split data!");
717 mSplitter
= new MultiSplitter();
724 // Restoring the session didn't result in any editor box, so create one:
726 mSplitter
->addWidget( firstBox
);
729 layout()->addWidget(mSplitter
);
731 makeSignalConnections();
735 mCurrentEditorBox
= 0; // ensure complete update
736 setCurrentBox( firstBox
);
739 // create a document on new session
740 docManager
->create();
742 firstBox
->setFocus(Qt::OtherFocusReason
); // ensure focus
745 void MultiEditor::setCurrent( Document
*doc
)
747 int tabIdx
= tabForDocument(doc
);
749 mTabs
->setCurrentIndex(tabIdx
);
752 void MultiEditor::showNextDocument()
754 int currentIndex
= mTabs
->currentIndex();
755 mTabs
->setCurrentIndex( qMin(currentIndex
+ 1, mTabs
->count() - 1) );
758 void MultiEditor::showPreviousDocument()
760 int currentIndex
= mTabs
->currentIndex();
761 mTabs
->setCurrentIndex( qMax(0, currentIndex
- 1) );
764 void MultiEditor::switchDocument()
766 CodeEditorBox
*box
= currentBox();
768 DocumentSelectPopUp
* popup
= new DocumentSelectPopUp(box
->history(), this);
770 QRect
popupRect(0,0,300,200);
771 popupRect
.moveCenter(rect().center());
772 popup
->resize(popupRect
.size());
773 QPoint globalPosition
= mapToGlobal(popupRect
.topLeft());
775 Document
* selectedDocument
= popup
->exec(globalPosition
);
777 if (selectedDocument
)
778 box
->setDocument(selectedDocument
);
781 void MultiEditor::onOpen( Document
*doc
, int initialCursorPosition
, int selectionLength
)
783 QTextDocument
*tdoc
= doc
->textDocument();
786 if(tdoc
->isModified())
787 icon
= mDocModifiedIcon
;
789 int newTabIndex
= mTabs
->addTab( icon
, doc
->title() );
790 mTabs
->setTabData( newTabIndex
, QVariant::fromValue
<Document
*>(doc
) );
792 currentBox()->setDocument(doc
, initialCursorPosition
, selectionLength
);
793 currentBox()->setFocus(Qt::OtherFocusReason
);
796 void MultiEditor::onClose( Document
*doc
)
798 int tabIdx
= tabForDocument(doc
);
800 mTabs
->removeTab(tabIdx
);
801 // TODO: each box should switch document according to their own history
804 void MultiEditor::show( Document
*doc
, int pos
, int selectionLength
)
806 currentBox()->setDocument(doc
, pos
, selectionLength
);
807 currentBox()->setFocus(Qt::OtherFocusReason
);
810 void MultiEditor::update( Document
*doc
)
812 int tabIdx
= tabForDocument(doc
);
814 mTabs
->setTabText(tabIdx
, doc
->title());
817 void MultiEditor::onCloseRequest( int index
)
819 Document
*doc
= documentForTab(index
);
821 MainWindow::close(doc
);
824 void MultiEditor::onCurrentTabChanged( int index
)
829 Document
*doc
= documentForTab(index
);
833 CodeEditorBox
*curBox
= currentBox();
834 curBox
->setDocument(doc
);
835 curBox
->setFocus(Qt::OtherFocusReason
);
838 void MultiEditor::onCurrentEditorChanged(ScCodeEditor
*editor
)
840 setCurrentEditor(editor
);
843 void MultiEditor::onBoxActivated(CodeEditorBox
*box
)
848 void MultiEditor::onModificationChanged( bool modified
)
850 Q_ASSERT(currentEditor());
852 int tabIdx
= tabForDocument( currentEditor()->document() );
858 icon
= mDocModifiedIcon
;
859 mTabs
->setTabIcon( tabIdx
, icon
);
862 void MultiEditor::evaluateRegion()
864 ScCodeEditor
* editor
= currentEditor();
870 // Try current selection
871 QTextCursor cursor
= editor
->textCursor();
872 if (cursor
.hasSelection())
873 text
= cursor
.selectedText();
875 // If no selection, try current region
876 cursor
= editor
->currentRegion();
877 if (!cursor
.isNull()) {
878 text
= cursor
.selectedText();
880 // If no current region, try current line
881 cursor
= editor
->textCursor();
882 text
= cursor
.block().text();
883 if( mStepForwardEvaluation
) {
884 QTextCursor newCursor
= cursor
;
885 newCursor
.movePosition(QTextCursor::NextBlock
);
886 editor
->setTextCursor(newCursor
);
888 // Adjust cursor for code blinking:
889 cursor
.movePosition(QTextCursor::StartOfBlock
);
890 cursor
.movePosition(QTextCursor::EndOfBlock
, QTextCursor::KeepAnchor
);
897 text
.replace( QChar( 0x2029 ), QChar( '\n' ) );
899 Main::evaluateCode(text
);
901 editor
->blinkCode( cursor
);
904 void MultiEditor::evaluateLine()
906 ScCodeEditor
* editor
= currentEditor();
912 // Try current selection
913 QTextCursor cursor
= editor
->textCursor();
914 cursor
.select(QTextCursor::LineUnderCursor
);
915 text
= cursor
.selectedText();
917 if( mStepForwardEvaluation
) {
918 QTextCursor newCursor
= cursor
;
919 newCursor
.movePosition(QTextCursor::NextBlock
);
920 editor
->setTextCursor(newCursor
);
926 // Adjust cursor for code blinking:
927 cursor
.movePosition(QTextCursor::StartOfBlock
);
928 cursor
.movePosition(QTextCursor::EndOfBlock
, QTextCursor::KeepAnchor
);
930 text
.replace( QChar( 0x2029 ), QChar( '\n' ) );
932 Main::evaluateCode(text
);
934 editor
->blinkCode( cursor
);
937 void MultiEditor::evaluateDocument()
939 ScCodeEditor
* editor
= currentEditor();
943 QString documentText
= editor
->textDocument()->toPlainText();
944 Main::evaluateCode(documentText
);
947 Document
* MultiEditor::documentForTab( int index
)
949 return mTabs
->tabData(index
).value
<Document
*>();
952 int MultiEditor::tabForDocument( Document
* doc
)
954 int tabCount
= mTabs
->count();
955 for (int idx
= 0; idx
< tabCount
; ++idx
) {
956 Document
*tabDoc
= documentForTab(idx
);
957 if (tabDoc
&& tabDoc
== doc
)
963 CodeEditorBox
*MultiEditor::newBox()
965 CodeEditorBox
*box
= new CodeEditorBox();
967 connect(box
, SIGNAL(activated(CodeEditorBox
*)),
968 this, SLOT(onBoxActivated(CodeEditorBox
*)));
973 void MultiEditor::setCurrentBox( CodeEditorBox
* box
)
975 if (mCurrentEditorBox
== box
)
978 mCurrentEditorBox
= box
;
979 mBoxSigMux
->setCurrentObject(box
);
980 setCurrentEditor( box
->currentEditor() );
982 mCurrentEditorBox
->setActive();
985 void MultiEditor::setCurrentEditor( ScCodeEditor
* editor
)
988 int tabIndex
= tabForDocument(editor
->document());
990 mTabs
->setCurrentIndex(tabIndex
);
993 mSigMux
->setCurrentObject(editor
);
996 Document
*currentDocument
= editor
? editor
->document() : 0;
997 Main::scProcess()->setActiveDocument(currentDocument
);
998 emit
currentDocumentChanged(currentDocument
);
1001 ScCodeEditor
*MultiEditor::currentEditor()
1003 return currentBox()->currentEditor();
1006 void MultiEditor::split( Qt::Orientation splitDirection
)
1008 CodeEditorBox
*box
= newBox();
1009 CodeEditorBox
*curBox
= currentBox();
1010 ScCodeEditor
*curEditor
= curBox
->currentEditor();
1013 box
->setDocument(curEditor
->document(), curEditor
->textCursor().position());
1015 mSplitter
->insertWidget(box
, curBox
, splitDirection
);
1016 box
->setFocus( Qt::OtherFocusReason
);
1019 void MultiEditor::removeCurrentSplit()
1021 int boxCount
= mSplitter
->findChildren
<CodeEditorBox
*>().count();
1023 // Do not allow removing the one and only box.
1026 CodeEditorBox
*box
= currentBox();
1027 mSplitter
->removeWidget(box
);
1029 // switch current box to first box found:
1030 box
= mSplitter
->findChild
<CodeEditorBox
>();
1033 box
->setFocus( Qt::OtherFocusReason
);
1036 void MultiEditor::removeAllSplits()
1038 CodeEditorBox
*box
= currentBox();
1040 Q_ASSERT(mSplitter
->count());
1041 if (mSplitter
->count() == 1 && mSplitter
->widget(0) == box
)
1045 MultiSplitter
*newSplitter
= new MultiSplitter
;
1046 newSplitter
->addWidget(box
);
1049 mSplitter
= newSplitter
;
1050 layout()->addWidget(newSplitter
);
1052 box
->setFocus( Qt::OtherFocusReason
);
1055 } // namespace ScIDE