scide: refactoring - move LineIndicator to separate source file
[supercollider.git] / editors / sc-ide / widgets / main_window.cpp
blob8c917d277d94d7d59fa13c26c4a13d70886c4a66
1 /*
2 SuperCollider Qt IDE
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 "cmd_line.hpp"
22 #include "doc_list.hpp"
23 #include "documents_dialog.hpp"
24 #include "find_replace_tool.hpp"
25 #include "goto_line_tool.hpp"
26 #include "lookup_dialog.hpp"
27 #include "main_window.hpp"
28 #include "multi_editor.hpp"
29 #include "popup_text_input.hpp"
30 #include "post_window.hpp"
31 #include "session_switch_dialog.hpp"
32 #include "sessions_dialog.hpp"
33 #include "tool_box.hpp"
34 #include "../core/main.hpp"
35 #include "../core/doc_manager.hpp"
36 #include "../core/session_manager.hpp"
37 #include "../core/sc_server.hpp"
38 #include "code_editor/editor.hpp"
39 #include "settings/dialog.hpp"
41 #include <QAction>
42 #include <QApplication>
43 #include <QFileDialog>
44 #include <QFileInfo>
45 #include <QGridLayout>
46 #include <QInputDialog>
47 #include <QMenu>
48 #include <QMenuBar>
49 #include <QMessageBox>
50 #include <QPointer>
51 #include <QShortcut>
52 #include <QStatusBar>
53 #include <QVBoxLayout>
55 namespace ScIDE {
57 MainWindow * MainWindow::mInstance = 0;
59 MainWindow::MainWindow(Main * main) :
60 mMain(main),
61 mClockLabel(0),
62 mDocDialog(0)
64 Q_ASSERT(!mInstance);
65 mInstance = this;
67 setCorner( Qt::BottomLeftCorner, Qt::LeftDockWidgetArea );
69 // Construct status bar:
71 mLangStatus = new StatusLabel();
72 mLangStatus->setText("Inactive");
73 mServerStatus = new StatusLabel();
74 onServerStatusReply(0, 0, 0, 0, 0, 0);
76 mStatusBar = statusBar();
77 mStatusBar->addPermanentWidget( new QLabel("Interpreter:") );
78 mStatusBar->addPermanentWidget( mLangStatus );
79 mStatusBar->addPermanentWidget( new QLabel("Server:") );
80 mStatusBar->addPermanentWidget( mServerStatus );
82 // Code editor
83 mEditors = new MultiEditor(main);
85 // Tools
87 mCmdLine = new CmdLine("Command Line:");
88 connect(mCmdLine, SIGNAL(invoked(QString, bool)),
89 main->scProcess(), SLOT(evaluateCode(QString, bool)));
91 mFindReplaceTool = new TextFindReplacePanel;
93 mGoToLineTool = new GoToLineTool();
94 connect(mGoToLineTool, SIGNAL(activated(int)), this, SLOT(hideToolBox()));
96 mToolBox = new ToolBox;
97 mToolBox->addWidget(mCmdLine);
98 mToolBox->addWidget(mFindReplaceTool);
99 mToolBox->addWidget(mGoToLineTool);
100 mToolBox->hide();
102 // Docks
103 mDocListDock = new DocumentsDock(main->documentManager(), this);
104 mDocListDock->setObjectName("documents-dock");
105 addDockWidget(Qt::RightDockWidgetArea, mDocListDock);
106 mDocListDock->hide();
109 mPostDock = new PostDock(this);
110 mPostDock->setObjectName("post-dock");
111 addDockWidget(Qt::LeftDockWidgetArea, mPostDock);
113 // Layout
115 QVBoxLayout *center_box = new QVBoxLayout;
116 center_box->setContentsMargins(0,0,0,0);
117 center_box->setSpacing(0);
118 center_box->addWidget(mEditors);
119 center_box->addWidget(mToolBox);
121 QWidget *central = new QWidget;
122 central->setLayout(center_box);
123 setCentralWidget(central);
125 // Session management
126 connect(main->sessionManager(), SIGNAL(saveSessionRequest(Session*)),
127 this, SLOT(saveSession(Session*)));
128 connect(main->sessionManager(), SIGNAL(switchSessionRequest(Session*)),
129 this, SLOT(switchSession(Session*)));
130 connect(main->sessionManager(), SIGNAL(currentSessionNameChanged()),
131 this, SLOT(updateWindowTitle()));
132 // A system for easy evaluation of pre-defined code:
133 connect(&mCodeEvalMapper, SIGNAL(mapped(QString)),
134 this, SIGNAL(evaluateCode(QString)));
135 connect(this, SIGNAL(evaluateCode(QString,bool)),
136 main->scProcess(), SLOT(evaluateCode(QString,bool)));
137 // Interpreter: post output
138 connect(main->scProcess(), SIGNAL( scPost(QString) ),
139 mPostDock->mPostWindow, SLOT( post(QString) ) );
140 // Interpreter: monitor running state
141 connect(main->scProcess(), SIGNAL( stateChanged(QProcess::ProcessState) ),
142 this, SLOT( onInterpreterStateChanged(QProcess::ProcessState) ) );
143 // Interpreter: forward status messages
144 connect(main->scProcess(), SIGNAL(statusMessage(const QString&)),
145 this, SLOT(showMessage(const QString&)));
147 // Document list interaction
148 connect(mDocListDock->list(), SIGNAL(clicked(Document*)),
149 mEditors, SLOT(setCurrent(Document*)));
150 connect(mEditors, SIGNAL(currentDocumentChanged(Document*)),
151 mDocListDock->list(), SLOT(setCurrent(Document*)),
152 Qt::QueuedConnection);
154 // Update actions on document change
155 connect(mEditors, SIGNAL(currentDocumentChanged(Document*)),
156 this, SLOT(onCurrentDocumentChanged(Document*)));
157 // Document management
158 DocumentManager *docMng = main->documentManager();
159 connect(docMng, SIGNAL(changedExternally(Document*)),
160 this, SLOT(onDocumentChangedExternally(Document*)));
161 connect(docMng, SIGNAL(recentsChanged()),
162 this, SLOT(updateRecentDocsMenu()));
164 connect(main, SIGNAL(applySettingsRequest(Settings::Manager*)),
165 this, SLOT(applySettings(Settings::Manager*)));
167 // ToolBox
168 connect(mToolBox->closeButton(), SIGNAL(clicked()), this, SLOT(hideToolBox()));
170 connect(main->scResponder(), SIGNAL(serverRunningChanged(bool,QString,int)), this, SLOT(onServerRunningChanged(bool,QString,int)));
171 connect(main->scServer(), SIGNAL(updateServerStatus(int,int,int,int,float,float)), this, SLOT(onServerStatusReply(int,int,int,int,float,float)));
173 createActions();
174 createMenus();
176 // Must be called after createAtions(), because it accesses an action:
177 onServerRunningChanged(false, "", 0);
179 mServerStatus->addAction( action(ToggleServerRunning) );
180 mServerStatus->setContextMenuPolicy(Qt::ActionsContextMenu);
182 // Initialize recent documents menu
183 updateRecentDocsMenu();
185 QIcon icon;
186 icon.addFile(":/icons/sc-cube-128");
187 icon.addFile(":/icons/sc-cube-48");
188 icon.addFile(":/icons/sc-cube-32");
189 icon.addFile(":/icons/sc-cube-16");
190 QApplication::setWindowIcon(icon);
192 updateWindowTitle();
195 void MainWindow::createActions()
197 Settings::Manager *settings = mMain->settings();
198 settings->beginGroup("IDE/shortcuts");
200 QAction *act;
202 // File
203 mActions[Quit] = act = new QAction(
204 QIcon::fromTheme("application-exit"), tr("&Quit..."), this);
205 act->setShortcut(tr("Ctrl+Q", "Quit application"));
206 act->setStatusTip(tr("Quit SuperCollider IDE"));
207 QObject::connect( act, SIGNAL(triggered()), this, SLOT(onQuit()) );
209 mActions[DocNew] = act = new QAction(
210 QIcon::fromTheme("document-new"), tr("&New"), this);
211 act->setShortcut(tr("Ctrl+N", "New document"));
212 act->setStatusTip(tr("Create a new document"));
213 connect(act, SIGNAL(triggered()), this, SLOT(newDocument()));
215 mActions[DocOpen] = act = new QAction(
216 QIcon::fromTheme("document-open"), tr("&Open..."), this);
217 act->setShortcut(tr("Ctrl+O", "Open document"));
218 act->setStatusTip(tr("Open an existing file"));
219 connect(act, SIGNAL(triggered()), this, SLOT(openDocument()));
221 mActions[DocSave] = act = new QAction(
222 QIcon::fromTheme("document-save"), tr("&Save"), this);
223 act->setShortcut(tr("Ctrl+S", "Save document"));
224 act->setStatusTip(tr("Save the current document"));
225 connect(act, SIGNAL(triggered()), this, SLOT(saveDocument()));
227 mActions[DocSaveAs] = act = new QAction(
228 QIcon::fromTheme("document-save-as"), tr("Save &As..."), this);
229 act->setShortcut(tr("Ctrl+Shift+S", "Save &As..."));
230 act->setStatusTip(tr("Save the current document into a different file"));
231 connect(act, SIGNAL(triggered()), this, SLOT(saveDocumentAs()));
233 mActions[DocSaveAll] = act = new QAction(
234 QIcon::fromTheme("document-save"), tr("Save All..."), this);
235 act->setShortcut(tr("Ctrl+Alt+S", "Save all documents"));
236 act->setStatusTip(tr("Save all open documents"));
237 connect(act, SIGNAL(triggered()), this, SLOT(saveAllDocuments()));
239 mActions[DocClose] = act = new QAction(
240 QIcon::fromTheme("window-close"), tr("&Close"), this);
241 act->setShortcut(tr("Ctrl+W", "Close document"));
242 act->setStatusTip(tr("Close the current document"));
243 connect(act, SIGNAL(triggered()), this, SLOT(closeDocument()));
245 mActions[DocCloseAll] = act = new QAction(
246 QIcon::fromTheme("window-close"), tr("Close All..."), this);
247 act->setShortcut(tr("Ctrl+Shift+W", "Close all documents"));
248 act->setStatusTip(tr("Close all documents"));
249 connect(act, SIGNAL(triggered()), this, SLOT(closeAllDocuments()));
251 mActions[DocReload] = act = new QAction(
252 QIcon::fromTheme("view-refresh"), tr("&Reload"), this);
253 act->setShortcut(tr("F5", "Reload document"));
254 act->setStatusTip(tr("Reload the current document"));
255 connect(act, SIGNAL(triggered()), this, SLOT(reloadDocument()));
257 mActions[ClearRecentDocs] = act = new QAction(tr("Clear", "Clear recent documents"), this);
258 connect(act, SIGNAL(triggered()),
259 Main::instance()->documentManager(), SLOT(clearRecents()));
261 // Sessions
262 mActions[NewSession] = act = new QAction(
263 QIcon::fromTheme("document-new"), tr("&New Session"), this);
264 act->setStatusTip(tr("Open a new session"));
265 connect(act, SIGNAL(triggered()), this, SLOT(newSession()));
267 mActions[SaveSessionAs] = act = new QAction(
268 QIcon::fromTheme("document-save-as"), tr("Save Session &As..."), this);
269 act->setStatusTip(tr("Save the current session with a different name"));
270 connect(act, SIGNAL(triggered()), this, SLOT(saveCurrentSessionAs()));
272 mActions[ManageSessions] = act = new QAction(
273 tr("&Manage Sessions..."), this);
274 connect(act, SIGNAL(triggered()), this, SLOT(openSessionsDialog()));
276 mActions[OpenSessionSwitchDialog] = act = new QAction(
277 tr("&Switch Session..."), this);
278 connect(act, SIGNAL(triggered()), this, SLOT(showSwitchSessionDialog()));
279 act->setShortcut(tr("Ctrl+Shift+Q", "Switch Session"));
281 // Edit
282 mActions[Find] = act = new QAction(
283 QIcon::fromTheme("edit-find"), tr("&Find..."), this);
284 act->setShortcut(tr("Ctrl+F", "Find"));
285 act->setStatusTip(tr("Find text in document"));
286 connect(act, SIGNAL(triggered()), this, SLOT(showFindTool()));
288 mActions[Replace] = act = new QAction(
289 QIcon::fromTheme("edit-replace"), tr("&Replace..."), this);
290 act->setShortcut(tr("Ctrl+R", "Replace"));
291 act->setStatusTip(tr("Find and replace text in document"));
292 connect(act, SIGNAL(triggered()), this, SLOT(showReplaceTool()));
294 // View
295 mActions[ShowCmdLine] = act = new QAction(tr("&Command Line"), this);
296 act->setStatusTip(tr("Command line for quick code evaluation"));
297 act->setShortcut(tr("Ctrl+E", "Show command line"));
298 connect(act, SIGNAL(triggered()), this, SLOT(showCmdLine()));
300 mActions[ShowGoToLineTool] = act = new QAction(tr("&Go To Line"), this);
301 act->setStatusTip(tr("Tool to jump to a line by number"));
302 act->setShortcut(tr("Ctrl+G", "Show go-to-line tool"));
303 connect(act, SIGNAL(triggered()), this, SLOT(showGoToLineTool()));
305 mActions[CloseToolBox] = act = new QAction(
306 QIcon::fromTheme("window-close"), tr("&Close Tool Panel"), this);
307 act->setStatusTip(tr("Close any open tool panel"));
308 act->setShortcut(tr("Esc", "Close tool box"));
309 connect(act, SIGNAL(triggered()), this, SLOT(hideToolBox()));
311 mActions[ShowFullScreen] = act = new QAction(tr("&Full Screen"), this);
312 act->setCheckable(false);
313 act->setShortcut(tr("Ctrl+Shift+F", "Show ScIDE in Full Screen"));
314 connect(act, SIGNAL(triggered()), this, SLOT(toggleFullScreen()));
316 mActions[ClearPostWindow] = act = new QAction(
317 QIcon::fromTheme("window-clearpostwindow"), tr("Clear Post Window"), this);
318 act->setStatusTip(tr("Clear Post Window"));
319 act->setShortcut(tr("Ctrl+Shift+C", "Clear Post Window"));
320 connect(act, SIGNAL(triggered()), mPostDock->mPostWindow, SLOT(clear()));
322 mActions[LookupDefinition] = act = new QAction(
323 QIcon::fromTheme("window-lookupdefinition"), tr("Lookup Definition"), this);
324 act->setShortcut(tr("Ctrl+Shift+I", "Lookup Definition"));
325 connect(act, SIGNAL(triggered()), this, SLOT(lookupDefinition()));
327 mActions[LookupDocumentation] = act = new QAction(
328 QIcon::fromTheme("window-lookupDocumentation"), tr("Lookup Documentation"), this);
329 act->setShortcut(tr("Ctrl+Shift+D", "Lookup Documentation"));
330 connect(act, SIGNAL(triggered()), this, SLOT(lookupDocumentation()));
332 // Language
333 mActions[OpenDefinition] = act = new QAction(tr("Open Class/Method Definition"), this);
334 act->setShortcut(tr("Ctrl+I", "Open definition of selected class or method"));
335 connect(act, SIGNAL(triggered(bool)), this, SLOT(openDefinition()));
337 mActions[FindReferences] = act = new QAction(tr("Find References"), this);
338 act->setShortcut(tr("Ctrl+U", "Find References"));
339 connect(act, SIGNAL(triggered(bool)), this, SLOT(lookupReferences()));
341 // Settings
342 mActions[ShowSettings] = act = new QAction(tr("&Configure IDE..."), this);
343 act->setStatusTip(tr("Show configuration dialog"));
344 connect(act, SIGNAL(triggered()), this, SLOT(showSettings()));
347 // Help
348 mActions[Help] = act = new QAction(
349 QIcon::fromTheme("system-help"), tr("Open Help Browser"), this);
350 act->setStatusTip(tr("Open help."));
351 connect(act, SIGNAL(triggered()), this, SLOT(openHelp()));
353 mActions[HelpForSelection] = act = new QAction(
354 QIcon::fromTheme("system-help"), tr("&Help for Selection"), this);
355 act->setShortcut(tr("Ctrl+D", "Help for selection"));
356 act->setStatusTip(tr("Find help for selected text"));
357 connect(act, SIGNAL(triggered()), this, SLOT(openDocumentation()));
359 // Server
360 mActions[ToggleServerRunning] = act = new QAction(this);
361 connect(act, SIGNAL(triggered()), this, SLOT(toggleServerRunning()));
363 settings->endGroup(); // IDE/shortcuts;
365 // Add actions to settings
366 for (int i = 0; i < ActionCount; ++i)
367 settings->addAction( mActions[i] );
370 void MainWindow::createMenus()
372 QMenu *menu;
373 QMenu *submenu;
375 menu = new QMenu(tr("&File"), this);
376 menu->addAction( mActions[DocNew] );
377 menu->addAction( mActions[DocOpen] );
378 mRecentDocsMenu = menu->addMenu(tr("Open Recent", "Open a recent document"));
379 connect(mRecentDocsMenu, SIGNAL(triggered(QAction*)),
380 this, SLOT(onRecentDocAction(QAction*)));
381 menu->addAction( mActions[DocSave] );
382 menu->addAction( mActions[DocSaveAs] );
383 menu->addAction( mActions[DocSaveAll] );
384 menu->addSeparator();
385 menu->addAction( mActions[DocReload] );
386 menu->addSeparator();
387 menu->addAction( mActions[DocClose] );
388 menu->addAction( mActions[DocCloseAll] );
389 menu->addSeparator();
390 menu->addAction( mActions[Quit] );
392 menuBar()->addMenu(menu);
394 menu = new QMenu(tr("&Session"), this);
395 menu->addAction( mActions[NewSession] );
396 menu->addAction( mActions[SaveSessionAs] );
397 submenu = menu->addMenu(tr("&Open Session"));
398 connect(submenu, SIGNAL(triggered(QAction*)),
399 this, SLOT(onOpenSessionAction(QAction*)));
400 mSessionsMenu = submenu;
401 updateSessionsMenu();
402 menu->addSeparator();
403 menu->addAction( mActions[ManageSessions] );
404 menu->addAction( mActions[OpenSessionSwitchDialog] );
406 menuBar()->addMenu(menu);
408 menu = new QMenu(tr("&Edit"), this);
409 menu->addAction( mEditors->action(MultiEditor::Undo) );
410 menu->addAction( mEditors->action(MultiEditor::Redo) );
411 menu->addSeparator();
412 menu->addAction( mEditors->action(MultiEditor::Cut) );
413 menu->addAction( mEditors->action(MultiEditor::Copy) );
414 menu->addAction( mEditors->action(MultiEditor::Paste) );
415 menu->addSeparator();
416 menu->addAction( mActions[Find] );
417 menu->addAction( mActions[Replace] );
418 menu->addSeparator();
419 menu->addAction( mEditors->action(MultiEditor::IndentLineOrRegion) );
420 menu->addAction( mEditors->action(MultiEditor::ToggleComment) );
421 menu->addAction( mEditors->action(MultiEditor::ToggleOverwriteMode) );
422 menu->addAction( mEditors->action(MultiEditor::SelectRegion) );
423 menu->addSeparator();
424 menu->addAction( mActions[OpenDefinition] );
425 menu->addAction( mActions[FindReferences] );
427 menuBar()->addMenu(menu);
429 menu = new QMenu(tr("&View"), this);
430 submenu = new QMenu(tr("&Docks"), this);
431 submenu->addAction( mPostDock->toggleViewAction() );
432 submenu->addAction( mDocListDock->toggleViewAction() );
433 menu->addMenu(submenu);
434 menu->addSeparator();
435 submenu = menu->addMenu(tr("&Tool Panels"));
436 submenu->addAction( mActions[Find] );
437 submenu->addAction( mActions[Replace] );
438 submenu->addAction( mActions[ShowCmdLine] );
439 submenu->addAction( mActions[ShowGoToLineTool] );
440 submenu->addSeparator();
441 submenu->addAction( mActions[CloseToolBox] );
442 menu->addSeparator();
443 menu->addAction( mEditors->action(MultiEditor::EnlargeFont) );
444 menu->addAction( mEditors->action(MultiEditor::ShrinkFont) );
445 menu->addAction( mEditors->action(MultiEditor::ResetFontSize) );
446 menu->addSeparator();
447 menu->addAction( mEditors->action(MultiEditor::ShowWhitespace) );
448 menu->addSeparator();
449 menu->addAction( mEditors->action(MultiEditor::NextDocument) );
450 menu->addAction( mEditors->action(MultiEditor::PreviousDocument) );
451 menu->addSeparator();
452 menu->addAction( mEditors->action(MultiEditor::SplitHorizontally) );
453 menu->addAction( mEditors->action(MultiEditor::SplitVertically) );
454 menu->addAction( mEditors->action(MultiEditor::RemoveCurrentSplit) );
455 menu->addAction( mEditors->action(MultiEditor::RemoveAllSplits) );
456 menu->addSeparator();
457 menu->addAction( mActions[ClearPostWindow] );
458 menu->addSeparator();
459 menu->addAction( mActions[ShowFullScreen] );
460 menu->addAction( mActions[LookupDefinition] );
461 menu->addAction( mActions[LookupDocumentation] );
463 menuBar()->addMenu(menu);
465 menu = new QMenu(tr("&Language"), this);
466 menu->addAction( mMain->scProcess()->action(SCProcess::StartSCLang) );
467 menu->addAction( mMain->scProcess()->action(SCProcess::StopSCLang) );
468 menu->addAction( mMain->scProcess()->action(SCProcess::RestartSCLang) );
469 menu->addAction( mMain->scProcess()->action(SCProcess::RecompileClassLibrary) );
470 menu->addSeparator();
471 menu->addAction( mEditors->action(MultiEditor::EvaluateCurrentDocument) );
472 menu->addAction( mEditors->action(MultiEditor::EvaluateRegion) );
473 menu->addAction( mEditors->action(MultiEditor::EvaluateLine) );
474 menu->addSeparator();
475 menu->addAction( mMain->scProcess()->action(ScIDE::SCProcess::RunMain) );
476 menu->addAction( mMain->scProcess()->action(ScIDE::SCProcess::StopMain) );
478 menuBar()->addMenu(menu);
480 menu = new QMenu(tr("&Settings"), this);
481 menu->addAction( mActions[ShowSettings] );
483 menuBar()->addMenu(menu);
485 menu = new QMenu(tr("&Help"), this);
486 menu->addAction( mActions[Help] );
487 menu->addAction( mActions[HelpForSelection] );
489 menuBar()->addMenu(menu);
492 void MainWindow::saveWindowState()
494 Settings::Manager *settings = Main::settings();
495 settings->beginGroup("IDE/mainWindow");
496 settings->setValue("geometry", this->saveGeometry().toBase64());
497 settings->setValue("state", this->saveState().toBase64());
498 settings->endGroup();
501 void MainWindow::restoreWindowState()
503 Settings::Manager *settings = Main::settings();
504 settings->beginGroup("IDE/mainWindow");
506 QByteArray geom = QByteArray::fromBase64( settings->value("geometry").value<QByteArray>() );
507 if (!geom.isEmpty())
508 restoreGeometry(geom);
509 else
510 setWindowState( windowState() & ~Qt::WindowFullScreen | Qt::WindowMaximized );
512 QByteArray state = QByteArray::fromBase64( settings->value("state").value<QByteArray>() );
513 if (!state.isEmpty())
514 restoreState(state);
516 settings->endGroup();
519 void MainWindow::newSession()
521 if (promptSaveDocs())
522 mMain->sessionManager()->newSession();
525 void MainWindow::saveCurrentSessionAs()
527 QString name = QInputDialog::getText( this,
528 "Save Current Session",
529 "Enter a name for the session:" );
531 if (name.isEmpty()) return;
533 mMain->sessionManager()->saveSessionAs(name);
535 updateSessionsMenu();
538 void MainWindow::onOpenSessionAction( QAction * action )
540 openSession(action->text());
543 void MainWindow::switchSession( Session *session )
545 if (session) {
546 session->beginGroup("mainWindow");
547 QByteArray geom = QByteArray::fromBase64( session->value("geometry").value<QByteArray>() );
548 QByteArray state = QByteArray::fromBase64( session->value("state").value<QByteArray>() );
549 session->endGroup();
551 // Workaround for Qt bug 4397:
552 setWindowState(Qt::WindowNoState);
554 if (!geom.isEmpty())
555 restoreGeometry(geom);
556 if (!state.isEmpty())
557 restoreState(state);
559 updateClockWidget(isFullScreen());
562 mEditors->switchSession(session);
564 updateWindowTitle();
567 void MainWindow::saveSession( Session *session )
569 session->beginGroup("mainWindow");
570 session->setValue("geometry", this->saveGeometry().toBase64());
571 session->setValue("state", this->saveState().toBase64());
572 session->endGroup();
574 mEditors->saveSession(session);
577 void MainWindow::openSessionsDialog()
579 QPointer<MainWindow> mainwin(this);
580 SessionsDialog dialog(mMain->sessionManager(), this);
581 dialog.exec();
582 if (mainwin)
583 mainwin->updateSessionsMenu();
586 QAction *MainWindow::action( ActionRole role )
588 Q_ASSERT( role < ActionCount );
589 return mActions[role];
592 bool MainWindow::quit()
594 if (!promptSaveDocs())
595 return false;
597 saveWindowState();
599 mMain->quit();
601 return true;
604 void MainWindow::onQuit()
606 quit();
609 void MainWindow::onCurrentDocumentChanged( Document * doc )
611 updateWindowTitle();
613 mActions[DocSave]->setEnabled(doc);
614 mActions[DocSaveAs]->setEnabled(doc);
615 mActions[DocClose]->setEnabled(doc);
617 CodeEditor *editor = mEditors->currentEditor();
618 mFindReplaceTool->setEditor( editor );
619 mGoToLineTool->setEditor( editor );
622 void MainWindow::onDocumentChangedExternally( Document *doc )
624 if (mDocDialog)
625 return;
627 mDocDialog = new DocumentsDialog(DocumentsDialog::ExternalChange, this);
628 mDocDialog->addDocument(doc);
629 connect(mDocDialog, SIGNAL(finished(int)), this, SLOT(onDocDialogFinished()));
630 mDocDialog->open();
633 void MainWindow::onDocDialogFinished()
635 mDocDialog->deleteLater();
636 mDocDialog = 0;
639 void MainWindow::updateRecentDocsMenu()
641 mRecentDocsMenu->clear();
643 const QStringList &recent = mMain->documentManager()->recents();
645 foreach( const QString & path, recent )
646 mRecentDocsMenu->addAction(path);
648 if (!recent.isEmpty()) {
649 mRecentDocsMenu->addSeparator();
650 mRecentDocsMenu->addAction(mActions[ClearRecentDocs]);
654 void MainWindow::onRecentDocAction( QAction *action )
656 mMain->documentManager()->open(action->text());
659 void MainWindow::onInterpreterStateChanged( QProcess::ProcessState state )
661 QString text;
662 QColor color;
664 switch(state) {
665 case QProcess::NotRunning:
666 text = "Inactive";
667 color = Qt::white;
668 break;
669 case QProcess::Starting:
670 text = "Booting";
671 color = QColor(255,255,0);
672 break;
673 case QProcess::Running:
674 text = "Active";
675 color = Qt::green;
676 break;
679 mLangStatus->setText(text);
680 mLangStatus->setTextColor(color);
684 void MainWindow::onServerStatusReply(int ugens, int synths, int groups, int synthDefs, float avgCPU, float peakCPU)
686 QString statusString =
687 QString("%1% %2% %3u %4s %5g %6d")
688 .arg(avgCPU, 5, 'f', 2)
689 .arg(peakCPU, 5, 'f', 2)
690 .arg(ugens, 4)
691 .arg(synths, 4)
692 .arg(groups, 4)
693 .arg(synthDefs, 4);
695 mServerStatus->setText(statusString);
698 void MainWindow::onServerRunningChanged(bool running, const QString &, int)
700 QAction *serverStatusAction = mActions[ToggleServerRunning];
702 mServerStatus->setTextColor( running ? Qt::green : Qt::white);
703 if (!running) {
704 onServerStatusReply(0, 0, 0, 0, 0, 0);
706 serverStatusAction->setText( tr("&Boot Server") );
707 serverStatusAction->setStatusTip(tr("Boot sound synthesis server"));
708 } else {
709 serverStatusAction->setText( tr("&Quit Server") );
710 serverStatusAction->setStatusTip(tr("Quit sound synthesis server"));
714 void MainWindow::closeEvent(QCloseEvent *event)
716 if(!quit()) event->ignore();
719 bool MainWindow::close( Document *doc )
721 if (doc->textDocument()->isModified())
723 QMessageBox::StandardButton ret;
724 ret = QMessageBox::warning(
725 mInstance,
726 tr("SuperCollider IDE"),
727 tr("There are unsaved changes in document '%1'.\n\n"
728 "Do you want to save it?").arg(doc->title()),
729 QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel,
730 QMessageBox::Save // the default
732 switch (ret)
734 case QMessageBox::Cancel:
735 return false;
736 case QMessageBox::Save:
737 if (!MainWindow::save(doc))
738 return false;
739 break;
743 Main::instance()->documentManager()->close(doc);
744 return true;
747 bool MainWindow::reload( Document *doc )
749 if (doc->filePath().isEmpty())
750 return false;
752 if (doc->textDocument()->isModified())
754 QMessageBox::StandardButton ret;
755 ret = QMessageBox::warning(
756 mInstance,
757 tr("SuperCollider IDE"),
758 tr("There are unsaved changes in document '%1'.\n\n"
759 "Do you want to reload it?").arg(doc->title()),
760 QMessageBox::Yes | QMessageBox::No,
761 QMessageBox::No // the default
763 if (ret == QMessageBox::No)
764 return false;
767 return Main::instance()->documentManager()->reload(doc);
770 bool MainWindow::save( Document *doc, bool forceChoose )
772 DocumentManager *mng = Main::instance()->documentManager();
773 if (forceChoose || doc->filePath().isEmpty()) {
774 QFileDialog dialog(mInstance);
775 dialog.setAcceptMode( QFileDialog::AcceptSave );
777 QStringList filters = (QStringList()
778 << "SuperCollider Document(*.scd)"
779 << "SuperCollider Class file(*.sc)"
780 << "SCDoc(*.schelp)"
781 << "All files(*)");
783 dialog.setNameFilters(filters);
784 dialog.setDefaultSuffix("scd");
786 if (dialog.exec() == QDialog::Accepted)
787 return mng->saveAs(doc, dialog.selectedFiles()[0]);
788 else
789 return false;
790 } else
791 return mng->save(doc);
794 void MainWindow::newDocument()
796 mMain->documentManager()->create();
799 void MainWindow::openDocument()
801 QFileDialog dialog (this, Qt::Dialog);
802 dialog.setModal(true);
803 dialog.setWindowModality(Qt::ApplicationModal);
805 dialog.setFileMode( QFileDialog::ExistingFiles );
807 CodeEditor * currentEditor = mEditors->currentEditor();
808 if (currentEditor) {
809 Document * currentDocument = currentEditor->document();
810 QFileInfo filePath (currentDocument->filePath());
811 dialog.setDirectory(filePath.dir());
814 QStringList filters;
815 filters
816 << "All files(*)"
817 << "SuperCollider(*.scd *.sc)"
818 << "SCDoc(*.schelp)";
819 dialog.setNameFilters(filters);
821 if (dialog.exec())
823 QStringList filenames = dialog.selectedFiles();
824 foreach(QString filename, filenames)
825 mMain->documentManager()->open(filename);
829 void MainWindow::saveDocument()
831 CodeEditor *editor = mEditors->currentEditor();
832 if(!editor) return;
834 Document *doc = editor->document();
835 Q_ASSERT(doc);
837 MainWindow::save(doc);
840 void MainWindow::saveDocumentAs()
842 CodeEditor *editor = mEditors->currentEditor();
843 if(!editor) return;
845 Document *doc = editor->document();
846 Q_ASSERT(doc);
848 MainWindow::save(doc, true);
851 void MainWindow::saveAllDocuments()
853 QList<Document*> docs = mMain->documentManager()->documents();
854 foreach (Document *doc, docs)
855 if (!MainWindow::save(doc))
856 return;
859 void MainWindow::reloadDocument()
861 CodeEditor *editor = mEditors->currentEditor();
862 if(!editor) return;
864 Q_ASSERT(editor->document());
865 MainWindow::reload(editor->document());
868 void MainWindow::closeDocument()
870 CodeEditor *editor = mEditors->currentEditor();
871 if(!editor) return;
873 Q_ASSERT(editor->document());
874 MainWindow::close( editor->document() );
877 void MainWindow::closeAllDocuments()
879 if (promptSaveDocs()) {
880 QList<Document*> docs = mMain->documentManager()->documents();
881 foreach (Document *doc, docs)
882 mMain->documentManager()->close(doc);
886 bool MainWindow::promptSaveDocs()
888 QList<Document*> docs = mMain->documentManager()->documents();
889 QList<Document*> unsavedDocs;
890 foreach(Document* doc, docs)
891 if(doc->textDocument()->isModified())
892 unsavedDocs.append(doc);
894 if (unsavedDocs.count())
896 DocumentsDialog dialog(unsavedDocs, DocumentsDialog::Quit, this);
898 if (!dialog.exec())
899 return false;
902 return true;
905 void MainWindow::updateWindowTitle()
907 Session *session = mMain->sessionManager()->currentSession();
908 CodeEditor *editor = mEditors->currentEditor();
909 Document *doc = editor ? editor->document() : 0;
911 QString title;
913 if (session) {
914 title.append(session->name());
915 if (doc) title.append(": ");
918 if (doc) {
919 if (!doc->filePath().isEmpty()) {
920 QFileInfo info = QFileInfo(doc->filePath());
921 QString pathString = info.dir().path();
923 QString homePath = QDir::homePath();
924 if (pathString.startsWith(homePath))
925 pathString.replace(0, homePath.size(), QString("~"));
927 QString titleString = QString("%1 (%2)").arg(info.fileName(), pathString);
929 title.append( titleString );
930 } else
931 title.append( "Untitled" );
934 if (!title.isEmpty())
935 title.append(" - ");
937 title.append("SuperCollider IDE");
939 setWindowTitle(title);
942 void MainWindow::toggleFullScreen()
944 if (isFullScreen()) {
945 setWindowState(windowState() & ~Qt::WindowFullScreen);
947 updateClockWidget(false);
948 } else {
949 setWindowState(windowState() | Qt::WindowFullScreen);
951 updateClockWidget(true);
955 void MainWindow::updateClockWidget(bool isFullScreen)
957 if (!isFullScreen) {
958 if (mClockLabel) {
959 delete mClockLabel;
960 mClockLabel = NULL;
962 } else {
963 if (mClockLabel == NULL) {
964 mClockLabel = new StatusClockLabel(this);
965 statusBar()->insertWidget(0, mClockLabel);
970 void MainWindow::openSession(const QString &sessionName)
972 if (promptSaveDocs())
973 mMain->sessionManager()->openSession( sessionName );
976 void MainWindow::openDefinition()
978 QWidget * focussedWidget = QApplication::focusWidget();
980 int indexOfMethod = focussedWidget->metaObject()->indexOfMethod("openDefinition()");
981 if (indexOfMethod != -1)
982 QMetaObject::invokeMethod( focussedWidget, "openDefinition", Qt::DirectConnection );
985 void MainWindow::lookupDefinition()
987 LookupDialog dialog(mEditors);
988 dialog.exec();
991 void MainWindow::lookupDocumentation()
993 PopupTextInput * dialog = new PopupTextInput(tr("Lookup Documentation For"), this);
995 bool success = dialog->exec();
996 if (success)
997 Main::openDocumentation(dialog->textValue());
999 delete dialog;
1002 void MainWindow::lookupReferences()
1004 QWidget * focussedWidget = QApplication::focusWidget();
1006 int indexOfMethod = focussedWidget->metaObject()->indexOfMethod("lookupReferences()");
1007 if (indexOfMethod != -1)
1008 QMetaObject::invokeMethod( focussedWidget, "lookupReferences", Qt::DirectConnection );
1012 void MainWindow::showMessage( QString const & string )
1014 mStatusBar->showMessage(string, 3000);
1017 void MainWindow::applySettings( Settings::Manager * settings )
1019 mEditors->applySettings(settings);
1020 mPostDock->mPostWindow->applySettings(settings);
1021 mCmdLine->applySettings(settings);
1025 void MainWindow::updateSessionsMenu()
1027 mSessionsMenu->clear();
1028 QStringList sessions = mMain->sessionManager()->availableSessions();
1029 foreach (const QString & session, sessions)
1030 mSessionsMenu->addAction( session );
1033 void MainWindow::showSwitchSessionDialog()
1035 SessionSwitchDialog * dialog = new SessionSwitchDialog(this);
1036 int result = dialog->exec();
1038 if (result == QDialog::Accepted)
1039 openSession(dialog->activeElement());
1041 delete dialog;
1044 void MainWindow::showCmdLine()
1046 mToolBox->setCurrentWidget( mCmdLine );
1047 mToolBox->show();
1049 mCmdLine->setFocus(Qt::OtherFocusReason);
1052 void MainWindow::showGoToLineTool()
1054 CodeEditor *editor = mEditors->currentEditor();
1055 mGoToLineTool->setValue( editor ? editor->textCursor().blockNumber() + 1 : 0 );
1057 mToolBox->setCurrentWidget( mGoToLineTool );
1058 mToolBox->show();
1060 mGoToLineTool->setFocus();
1063 void MainWindow::showFindTool()
1065 mFindReplaceTool->setMode( TextFindReplacePanel::Find );
1066 mFindReplaceTool->initiate();
1068 mToolBox->setCurrentWidget( mFindReplaceTool );
1069 mToolBox->show();
1071 mFindReplaceTool->setFocus(Qt::OtherFocusReason);
1074 void MainWindow::showReplaceTool()
1076 mFindReplaceTool->setMode( TextFindReplacePanel::Replace );
1077 mFindReplaceTool->initiate();
1079 mToolBox->setCurrentWidget( mFindReplaceTool );
1080 mToolBox->show();
1082 mFindReplaceTool->setFocus(Qt::OtherFocusReason);
1085 void MainWindow::hideToolBox()
1087 mToolBox->hide();
1088 CodeEditor *editor = mEditors->currentEditor();
1089 if (editor) {
1090 // This slot is mapped to Escape, so also clear highlighting
1091 // whenever invoked:
1092 editor->clearSearchHighlighting();
1093 if (!editor->hasFocus())
1094 editor->setFocus(Qt::OtherFocusReason);
1098 void MainWindow::showSettings()
1100 Settings::Dialog dialog(mMain->settings());
1101 dialog.resize(700,400);
1102 int result = dialog.exec();
1103 if( result == QDialog::Accepted )
1104 mMain->applySettings();
1107 void MainWindow::openDocumentation()
1109 QWidget * focussedWidget = QApplication::focusWidget();
1111 bool documentationOpened = false;
1113 int indexOfMethod = focussedWidget->metaObject()->indexOfMethod("openDocumentation()");
1114 if (indexOfMethod != -1)
1115 QMetaObject::invokeMethod( focussedWidget, "openDocumentation", Qt::DirectConnection,
1116 Q_RETURN_ARG(bool, documentationOpened) );
1118 if (!documentationOpened)
1119 openHelp();
1122 void MainWindow::openHelp()
1124 QString code = QString("Help.gui");
1125 Main::scProcess()->evaluateCode(code, true);
1128 void MainWindow::toggleServerRunning()
1130 ScServer *scServer = Main::scServer();
1132 if (scServer->isRunning())
1133 scServer->quit();
1134 else
1135 scServer->boot();
1138 //////////////////////////// StatusLabel /////////////////////////////////
1140 StatusLabel::StatusLabel(QWidget *parent) : QLabel(parent)
1142 setAutoFillBackground(true);
1143 setMargin(3);
1144 setAlignment(Qt::AlignCenter);
1145 setBackground(Qt::black);
1146 setTextColor(Qt::white);
1148 QFont font("Monospace");
1149 font.setStyleHint(QFont::Monospace);
1150 font.setBold(true);
1151 setFont(font);
1154 void StatusLabel::setBackground(const QBrush & brush)
1156 QPalette plt(palette());
1157 plt.setBrush(QPalette::Window, brush);
1158 setPalette(plt);
1161 void StatusLabel::setTextColor(const QColor & color)
1163 QPalette plt(palette());
1164 plt.setColor(QPalette::WindowText, color);
1165 setPalette(plt);
1168 //////////////////////////// StatusClockLabel ////////////////////////////
1170 StatusClockLabel::StatusClockLabel(QWidget * parent):
1171 StatusLabel(parent)
1173 setTextColor(Qt::green);
1174 mTimerId = startTimer(1000);
1175 updateTime();
1178 StatusClockLabel::~StatusClockLabel()
1180 killTimer(mTimerId);
1183 void StatusClockLabel::timerEvent(QTimerEvent *e)
1185 if (e->timerId() == mTimerId)
1186 updateTime();
1189 void StatusClockLabel::updateTime()
1191 setText(QTime::currentTime().toString());
1194 } // namespace ScIDE