scide: MainWindow - introduce session switch dialog
[supercollider.git] / editors / sc-ide / widgets / main_window.cpp
blobac269ccb45b0192e7f932f110debbff75d801eb0
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 // Settings
338 mActions[ShowSettings] = act = new QAction(tr("&Configure IDE..."), this);
339 act->setStatusTip(tr("Show configuration dialog"));
340 connect(act, SIGNAL(triggered()), this, SLOT(showSettings()));
343 // Help
344 mActions[Help] = act = new QAction(
345 QIcon::fromTheme("system-help"), tr("Open Help Browser"), this);
346 act->setStatusTip(tr("Open help."));
347 connect(act, SIGNAL(triggered()), this, SLOT(openHelp()));
349 mActions[HelpForSelection] = act = new QAction(
350 QIcon::fromTheme("system-help"), tr("&Help for Selection"), this);
351 act->setShortcut(tr("Ctrl+D", "Help for selection"));
352 act->setStatusTip(tr("Find help for selected text"));
353 connect(act, SIGNAL(triggered()), this, SLOT(openDocumentation()));
355 // Server
356 mActions[ToggleServerRunning] = act = new QAction(this);
357 connect(act, SIGNAL(triggered()), this, SLOT(toggleServerRunning()));
359 settings->endGroup(); // IDE/shortcuts;
361 // Add actions to settings
362 for (int i = 0; i < ActionCount; ++i)
363 settings->addAction( mActions[i] );
366 void MainWindow::createMenus()
368 QMenu *menu;
369 QMenu *submenu;
371 menu = new QMenu(tr("&File"), this);
372 menu->addAction( mActions[DocNew] );
373 menu->addAction( mActions[DocOpen] );
374 mRecentDocsMenu = menu->addMenu(tr("Open Recent", "Open a recent document"));
375 connect(mRecentDocsMenu, SIGNAL(triggered(QAction*)),
376 this, SLOT(onRecentDocAction(QAction*)));
377 menu->addAction( mActions[DocSave] );
378 menu->addAction( mActions[DocSaveAs] );
379 menu->addAction( mActions[DocSaveAll] );
380 menu->addSeparator();
381 menu->addAction( mActions[DocReload] );
382 menu->addSeparator();
383 menu->addAction( mActions[DocClose] );
384 menu->addAction( mActions[DocCloseAll] );
385 menu->addSeparator();
386 menu->addAction( mActions[Quit] );
388 menuBar()->addMenu(menu);
390 menu = new QMenu(tr("&Session"), this);
391 menu->addAction( mActions[NewSession] );
392 menu->addAction( mActions[SaveSessionAs] );
393 submenu = menu->addMenu(tr("&Open Session"));
394 connect(submenu, SIGNAL(triggered(QAction*)),
395 this, SLOT(onOpenSessionAction(QAction*)));
396 mSessionsMenu = submenu;
397 updateSessionsMenu();
398 menu->addSeparator();
399 menu->addAction( mActions[ManageSessions] );
400 menu->addAction( mActions[OpenSessionSwitchDialog] );
402 menuBar()->addMenu(menu);
404 menu = new QMenu(tr("&Edit"), this);
405 menu->addAction( mEditors->action(MultiEditor::Undo) );
406 menu->addAction( mEditors->action(MultiEditor::Redo) );
407 menu->addSeparator();
408 menu->addAction( mEditors->action(MultiEditor::Cut) );
409 menu->addAction( mEditors->action(MultiEditor::Copy) );
410 menu->addAction( mEditors->action(MultiEditor::Paste) );
411 menu->addSeparator();
412 menu->addAction( mActions[Find] );
413 menu->addAction( mActions[Replace] );
414 menu->addSeparator();
415 menu->addAction( mEditors->action(MultiEditor::IndentLineOrRegion) );
416 menu->addAction( mEditors->action(MultiEditor::ToggleComment) );
417 menu->addAction( mEditors->action(MultiEditor::ToggleOverwriteMode) );
418 menu->addAction( mEditors->action(MultiEditor::SelectRegion) );
419 menu->addSeparator();
420 menu->addAction( mActions[OpenDefinition] );
422 menuBar()->addMenu(menu);
424 menu = new QMenu(tr("&View"), this);
425 submenu = new QMenu(tr("&Docks"), this);
426 submenu->addAction( mPostDock->toggleViewAction() );
427 submenu->addAction( mDocListDock->toggleViewAction() );
428 menu->addMenu(submenu);
429 menu->addSeparator();
430 submenu = menu->addMenu(tr("&Tool Panels"));
431 submenu->addAction( mActions[Find] );
432 submenu->addAction( mActions[Replace] );
433 submenu->addAction( mActions[ShowCmdLine] );
434 submenu->addAction( mActions[ShowGoToLineTool] );
435 submenu->addSeparator();
436 submenu->addAction( mActions[CloseToolBox] );
437 menu->addSeparator();
438 menu->addAction( mEditors->action(MultiEditor::EnlargeFont) );
439 menu->addAction( mEditors->action(MultiEditor::ShrinkFont) );
440 menu->addAction( mEditors->action(MultiEditor::ResetFontSize) );
441 menu->addSeparator();
442 menu->addAction( mEditors->action(MultiEditor::ShowWhitespace) );
443 menu->addSeparator();
444 menu->addAction( mEditors->action(MultiEditor::NextDocument) );
445 menu->addAction( mEditors->action(MultiEditor::PreviousDocument) );
446 menu->addSeparator();
447 menu->addAction( mEditors->action(MultiEditor::SplitHorizontally) );
448 menu->addAction( mEditors->action(MultiEditor::SplitVertically) );
449 menu->addAction( mEditors->action(MultiEditor::RemoveCurrentSplit) );
450 menu->addAction( mEditors->action(MultiEditor::RemoveAllSplits) );
451 menu->addSeparator();
452 menu->addAction( mActions[ClearPostWindow] );
453 menu->addSeparator();
454 menu->addAction( mActions[ShowFullScreen] );
455 menu->addAction( mActions[LookupDefinition] );
456 menu->addAction( mActions[LookupDocumentation] );
458 menuBar()->addMenu(menu);
460 menu = new QMenu(tr("&Language"), this);
461 menu->addAction( mMain->scProcess()->action(SCProcess::StartSCLang) );
462 menu->addAction( mMain->scProcess()->action(SCProcess::StopSCLang) );
463 menu->addAction( mMain->scProcess()->action(SCProcess::RestartSCLang) );
464 menu->addAction( mMain->scProcess()->action(SCProcess::RecompileClassLibrary) );
465 menu->addSeparator();
466 menu->addAction( mEditors->action(MultiEditor::EvaluateCurrentDocument) );
467 menu->addAction( mEditors->action(MultiEditor::EvaluateRegion) );
468 menu->addAction( mEditors->action(MultiEditor::EvaluateLine) );
469 menu->addSeparator();
470 menu->addAction( mMain->scProcess()->action(ScIDE::SCProcess::RunMain) );
471 menu->addAction( mMain->scProcess()->action(ScIDE::SCProcess::StopMain) );
473 menuBar()->addMenu(menu);
475 menu = new QMenu(tr("&Settings"), this);
476 menu->addAction( mActions[ShowSettings] );
478 menuBar()->addMenu(menu);
480 menu = new QMenu(tr("&Help"), this);
481 menu->addAction( mActions[Help] );
482 menu->addAction( mActions[HelpForSelection] );
484 menuBar()->addMenu(menu);
487 void MainWindow::saveWindowState()
489 Settings::Manager *settings = Main::settings();
490 settings->beginGroup("IDE/mainWindow");
491 settings->setValue("geometry", this->saveGeometry().toBase64());
492 settings->setValue("state", this->saveState().toBase64());
493 settings->endGroup();
496 void MainWindow::restoreWindowState()
498 Settings::Manager *settings = Main::settings();
499 settings->beginGroup("IDE/mainWindow");
501 QByteArray geom = QByteArray::fromBase64( settings->value("geometry").value<QByteArray>() );
502 if (!geom.isEmpty())
503 restoreGeometry(geom);
504 else
505 setWindowState( windowState() & ~Qt::WindowFullScreen | Qt::WindowMaximized );
507 QByteArray state = QByteArray::fromBase64( settings->value("state").value<QByteArray>() );
508 if (!state.isEmpty())
509 restoreState(state);
511 settings->endGroup();
514 void MainWindow::newSession()
516 if (promptSaveDocs())
517 mMain->sessionManager()->newSession();
520 void MainWindow::saveCurrentSessionAs()
522 QString name = QInputDialog::getText( this,
523 "Save Current Session",
524 "Enter a name for the session:" );
526 if (name.isEmpty()) return;
528 mMain->sessionManager()->saveSessionAs(name);
530 updateSessionsMenu();
533 void MainWindow::onOpenSessionAction( QAction * action )
535 openSession(action->text());
538 void MainWindow::switchSession( Session *session )
540 if (session) {
541 session->beginGroup("mainWindow");
542 QByteArray geom = QByteArray::fromBase64( session->value("geometry").value<QByteArray>() );
543 QByteArray state = QByteArray::fromBase64( session->value("state").value<QByteArray>() );
544 session->endGroup();
546 // Workaround for Qt bug 4397:
547 setWindowState(Qt::WindowNoState);
549 if (!geom.isEmpty())
550 restoreGeometry(geom);
551 if (!state.isEmpty())
552 restoreState(state);
554 updateClockWidget(isFullScreen());
557 mEditors->switchSession(session);
559 updateWindowTitle();
562 void MainWindow::saveSession( Session *session )
564 session->beginGroup("mainWindow");
565 session->setValue("geometry", this->saveGeometry().toBase64());
566 session->setValue("state", this->saveState().toBase64());
567 session->endGroup();
569 mEditors->saveSession(session);
572 void MainWindow::openSessionsDialog()
574 QPointer<MainWindow> mainwin(this);
575 SessionsDialog dialog(mMain->sessionManager(), this);
576 dialog.exec();
577 if (mainwin)
578 mainwin->updateSessionsMenu();
581 QAction *MainWindow::action( ActionRole role )
583 Q_ASSERT( role < ActionCount );
584 return mActions[role];
587 bool MainWindow::quit()
589 if (!promptSaveDocs())
590 return false;
592 saveWindowState();
594 mMain->quit();
596 return true;
599 void MainWindow::onQuit()
601 quit();
604 void MainWindow::onCurrentDocumentChanged( Document * doc )
606 updateWindowTitle();
608 mActions[DocSave]->setEnabled(doc);
609 mActions[DocSaveAs]->setEnabled(doc);
610 mActions[DocClose]->setEnabled(doc);
612 CodeEditor *editor = mEditors->currentEditor();
613 mFindReplaceTool->setEditor( editor );
614 mGoToLineTool->setEditor( editor );
617 void MainWindow::onDocumentChangedExternally( Document *doc )
619 if (mDocDialog)
620 return;
622 mDocDialog = new DocumentsDialog(DocumentsDialog::ExternalChange, this);
623 mDocDialog->addDocument(doc);
624 connect(mDocDialog, SIGNAL(finished(int)), this, SLOT(onDocDialogFinished()));
625 mDocDialog->open();
628 void MainWindow::onDocDialogFinished()
630 mDocDialog->deleteLater();
631 mDocDialog = 0;
634 void MainWindow::updateRecentDocsMenu()
636 mRecentDocsMenu->clear();
638 const QStringList &recent = mMain->documentManager()->recents();
640 foreach( const QString & path, recent )
641 mRecentDocsMenu->addAction(path);
643 if (!recent.isEmpty()) {
644 mRecentDocsMenu->addSeparator();
645 mRecentDocsMenu->addAction(mActions[ClearRecentDocs]);
649 void MainWindow::onRecentDocAction( QAction *action )
651 mMain->documentManager()->open(action->text());
654 void MainWindow::onInterpreterStateChanged( QProcess::ProcessState state )
656 QString text;
657 QColor color;
659 switch(state) {
660 case QProcess::NotRunning:
661 text = "Inactive";
662 color = Qt::white;
663 break;
664 case QProcess::Starting:
665 text = "Booting";
666 color = QColor(255,255,0);
667 break;
668 case QProcess::Running:
669 text = "Active";
670 color = Qt::green;
671 break;
674 mLangStatus->setText(text);
675 mLangStatus->setTextColor(color);
679 void MainWindow::onServerStatusReply(int ugens, int synths, int groups, int synthDefs, float avgCPU, float peakCPU)
681 QString statusString =
682 QString("%1% %2% %3u %4s %5g %6d")
683 .arg(avgCPU, 5, 'f', 2)
684 .arg(peakCPU, 5, 'f', 2)
685 .arg(ugens, 4)
686 .arg(synths, 4)
687 .arg(groups, 4)
688 .arg(synthDefs, 4);
690 mServerStatus->setText(statusString);
693 void MainWindow::onServerRunningChanged(bool running, const QString &, int)
695 QAction *serverStatusAction = mActions[ToggleServerRunning];
697 mServerStatus->setTextColor( running ? Qt::green : Qt::white);
698 if (!running) {
699 onServerStatusReply(0, 0, 0, 0, 0, 0);
701 serverStatusAction->setText( tr("&Boot Server") );
702 serverStatusAction->setStatusTip(tr("Boot sound synthesis server"));
703 } else {
704 serverStatusAction->setText( tr("&Quit Server") );
705 serverStatusAction->setStatusTip(tr("Quit sound synthesis server"));
709 void MainWindow::closeEvent(QCloseEvent *event)
711 if(!quit()) event->ignore();
714 bool MainWindow::close( Document *doc )
716 if (doc->textDocument()->isModified())
718 QMessageBox::StandardButton ret;
719 ret = QMessageBox::warning(
720 mInstance,
721 tr("SuperCollider IDE"),
722 tr("There are unsaved changes in document '%1'.\n\n"
723 "Do you want to save it?").arg(doc->title()),
724 QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel,
725 QMessageBox::Save // the default
727 switch (ret)
729 case QMessageBox::Cancel:
730 return false;
731 case QMessageBox::Save:
732 if (!MainWindow::save(doc))
733 return false;
734 break;
738 Main::instance()->documentManager()->close(doc);
739 return true;
742 bool MainWindow::reload( Document *doc )
744 if (doc->filePath().isEmpty())
745 return false;
747 if (doc->textDocument()->isModified())
749 QMessageBox::StandardButton ret;
750 ret = QMessageBox::warning(
751 mInstance,
752 tr("SuperCollider IDE"),
753 tr("There are unsaved changes in document '%1'.\n\n"
754 "Do you want to reload it?").arg(doc->title()),
755 QMessageBox::Yes | QMessageBox::No,
756 QMessageBox::No // the default
758 if (ret == QMessageBox::No)
759 return false;
762 return Main::instance()->documentManager()->reload(doc);
765 bool MainWindow::save( Document *doc, bool forceChoose )
767 DocumentManager *mng = Main::instance()->documentManager();
768 if (forceChoose || doc->filePath().isEmpty()) {
769 QFileDialog dialog(mInstance);
770 dialog.setAcceptMode( QFileDialog::AcceptSave );
772 QStringList filters = (QStringList()
773 << "SuperCollider Document(*.scd)"
774 << "SuperCollider Class file(*.sc)"
775 << "SCDoc(*.schelp)"
776 << "All files(*)");
778 dialog.setNameFilters(filters);
779 dialog.setDefaultSuffix("scd");
781 if (dialog.exec() == QDialog::Accepted)
782 return mng->saveAs(doc, dialog.selectedFiles()[0]);
783 else
784 return false;
785 } else
786 return mng->save(doc);
789 void MainWindow::newDocument()
791 mMain->documentManager()->create();
794 void MainWindow::openDocument()
796 QFileDialog dialog (this, Qt::Dialog);
797 dialog.setModal(true);
798 dialog.setWindowModality(Qt::ApplicationModal);
800 dialog.setFileMode( QFileDialog::ExistingFiles );
802 CodeEditor * currentEditor = mEditors->currentEditor();
803 if (currentEditor) {
804 Document * currentDocument = currentEditor->document();
805 QFileInfo filePath (currentDocument->filePath());
806 dialog.setDirectory(filePath.dir());
809 QStringList filters;
810 filters
811 << "All files(*)"
812 << "SuperCollider(*.scd *.sc)"
813 << "SCDoc(*.schelp)";
814 dialog.setNameFilters(filters);
816 if (dialog.exec())
818 QStringList filenames = dialog.selectedFiles();
819 foreach(QString filename, filenames)
820 mMain->documentManager()->open(filename);
824 void MainWindow::saveDocument()
826 CodeEditor *editor = mEditors->currentEditor();
827 if(!editor) return;
829 Document *doc = editor->document();
830 Q_ASSERT(doc);
832 MainWindow::save(doc);
835 void MainWindow::saveDocumentAs()
837 CodeEditor *editor = mEditors->currentEditor();
838 if(!editor) return;
840 Document *doc = editor->document();
841 Q_ASSERT(doc);
843 MainWindow::save(doc, true);
846 void MainWindow::saveAllDocuments()
848 QList<Document*> docs = mMain->documentManager()->documents();
849 foreach (Document *doc, docs)
850 if (!MainWindow::save(doc))
851 return;
854 void MainWindow::reloadDocument()
856 CodeEditor *editor = mEditors->currentEditor();
857 if(!editor) return;
859 Q_ASSERT(editor->document());
860 MainWindow::reload(editor->document());
863 void MainWindow::closeDocument()
865 CodeEditor *editor = mEditors->currentEditor();
866 if(!editor) return;
868 Q_ASSERT(editor->document());
869 MainWindow::close( editor->document() );
872 void MainWindow::closeAllDocuments()
874 if (promptSaveDocs()) {
875 QList<Document*> docs = mMain->documentManager()->documents();
876 foreach (Document *doc, docs)
877 mMain->documentManager()->close(doc);
881 bool MainWindow::promptSaveDocs()
883 QList<Document*> docs = mMain->documentManager()->documents();
884 QList<Document*> unsavedDocs;
885 foreach(Document* doc, docs)
886 if(doc->textDocument()->isModified())
887 unsavedDocs.append(doc);
889 if (unsavedDocs.count())
891 DocumentsDialog dialog(unsavedDocs, DocumentsDialog::Quit, this);
893 if (!dialog.exec())
894 return false;
897 return true;
900 void MainWindow::updateWindowTitle()
902 Session *session = mMain->sessionManager()->currentSession();
903 CodeEditor *editor = mEditors->currentEditor();
904 Document *doc = editor ? editor->document() : 0;
906 QString title;
908 if (session) {
909 title.append(session->name());
910 if (doc) title.append(": ");
913 if (doc) {
914 if (!doc->filePath().isEmpty()) {
915 QFileInfo info = QFileInfo(doc->filePath());
916 QString pathString = info.dir().path();
918 QString homePath = QDir::homePath();
919 if (pathString.startsWith(homePath))
920 pathString.replace(0, homePath.size(), QString("~"));
922 QString titleString = QString("%1 (%2)").arg(info.fileName(), pathString);
924 title.append( titleString );
925 } else
926 title.append( "Untitled" );
929 if (!title.isEmpty())
930 title.append(" - ");
932 title.append("SuperCollider IDE");
934 setWindowTitle(title);
937 void MainWindow::toggleFullScreen()
939 if (isFullScreen()) {
940 setWindowState(windowState() & ~Qt::WindowFullScreen);
942 updateClockWidget(false);
943 } else {
944 setWindowState(windowState() | Qt::WindowFullScreen);
946 updateClockWidget(true);
950 void MainWindow::updateClockWidget(bool isFullScreen)
952 if (!isFullScreen) {
953 if (mClockLabel) {
954 delete mClockLabel;
955 mClockLabel = NULL;
957 } else {
958 if (mClockLabel == NULL) {
959 mClockLabel = new StatusClockLabel(this);
960 statusBar()->insertWidget(0, mClockLabel);
965 void MainWindow::openSession(const QString &sessionName)
967 if (promptSaveDocs())
968 mMain->sessionManager()->openSession( sessionName );
971 void MainWindow::openDefinition()
973 QWidget * focussedWidget = QApplication::focusWidget();
975 int indexOfMethod = focussedWidget->metaObject()->indexOfMethod("openDefinition()");
976 if (indexOfMethod != -1)
977 QMetaObject::invokeMethod( focussedWidget, "openDefinition", Qt::DirectConnection );
980 void MainWindow::lookupDefinition()
982 LookupDialog dialog(mEditors);
983 dialog.exec();
986 void MainWindow::lookupDocumentation()
988 PopupTextInput * dialog = new PopupTextInput(tr("Lookup Documentation For"), this);
990 bool success = dialog->exec();
991 if (success)
992 Main::openDocumentation(dialog->textValue());
994 delete dialog;
997 void MainWindow::showMessage( QString const & string )
999 mStatusBar->showMessage(string, 3000);
1002 void MainWindow::applySettings( Settings::Manager * settings )
1004 mEditors->applySettings(settings);
1005 mPostDock->mPostWindow->applySettings(settings);
1006 mCmdLine->applySettings(settings);
1010 void MainWindow::updateSessionsMenu()
1012 mSessionsMenu->clear();
1013 QStringList sessions = mMain->sessionManager()->availableSessions();
1014 foreach (const QString & session, sessions)
1015 mSessionsMenu->addAction( session );
1018 void MainWindow::showSwitchSessionDialog()
1020 SessionSwitchDialog * dialog = new SessionSwitchDialog(this);
1021 int result = dialog->exec();
1023 if (result == QDialog::Accepted)
1024 openSession(dialog->activeElement());
1026 delete dialog;
1029 void MainWindow::showCmdLine()
1031 mToolBox->setCurrentWidget( mCmdLine );
1032 mToolBox->show();
1034 mCmdLine->setFocus(Qt::OtherFocusReason);
1037 void MainWindow::showGoToLineTool()
1039 CodeEditor *editor = mEditors->currentEditor();
1040 mGoToLineTool->setValue( editor ? editor->textCursor().blockNumber() + 1 : 0 );
1042 mToolBox->setCurrentWidget( mGoToLineTool );
1043 mToolBox->show();
1045 mGoToLineTool->setFocus();
1048 void MainWindow::showFindTool()
1050 mFindReplaceTool->setMode( TextFindReplacePanel::Find );
1051 mFindReplaceTool->initiate();
1053 mToolBox->setCurrentWidget( mFindReplaceTool );
1054 mToolBox->show();
1056 mFindReplaceTool->setFocus(Qt::OtherFocusReason);
1059 void MainWindow::showReplaceTool()
1061 mFindReplaceTool->setMode( TextFindReplacePanel::Replace );
1062 mFindReplaceTool->initiate();
1064 mToolBox->setCurrentWidget( mFindReplaceTool );
1065 mToolBox->show();
1067 mFindReplaceTool->setFocus(Qt::OtherFocusReason);
1070 void MainWindow::hideToolBox()
1072 mToolBox->hide();
1073 CodeEditor *editor = mEditors->currentEditor();
1074 if (editor) {
1075 // This slot is mapped to Escape, so also clear highlighting
1076 // whenever invoked:
1077 editor->clearSearchHighlighting();
1078 if (!editor->hasFocus())
1079 editor->setFocus(Qt::OtherFocusReason);
1083 void MainWindow::showSettings()
1085 Settings::Dialog dialog(mMain->settings());
1086 dialog.resize(700,400);
1087 int result = dialog.exec();
1088 if( result == QDialog::Accepted )
1089 mMain->applySettings();
1092 void MainWindow::openDocumentation()
1094 QWidget * focussedWidget = QApplication::focusWidget();
1096 bool documentationOpened = false;
1098 int indexOfMethod = focussedWidget->metaObject()->indexOfMethod("openDocumentation()");
1099 if (indexOfMethod != -1)
1100 QMetaObject::invokeMethod( focussedWidget, "openDocumentation", Qt::DirectConnection,
1101 Q_RETURN_ARG(bool, documentationOpened) );
1103 if (!documentationOpened)
1104 openHelp();
1107 void MainWindow::openHelp()
1109 QString code = QString("Help.gui");
1110 Main::scProcess()->evaluateCode(code, true);
1113 void MainWindow::toggleServerRunning()
1115 ScServer *scServer = Main::scServer();
1117 if (scServer->isRunning())
1118 scServer->quit();
1119 else
1120 scServer->boot();
1123 //////////////////////////// StatusLabel /////////////////////////////////
1125 StatusLabel::StatusLabel(QWidget *parent) : QLabel(parent)
1127 setAutoFillBackground(true);
1128 setMargin(3);
1129 setAlignment(Qt::AlignCenter);
1130 setBackground(Qt::black);
1131 setTextColor(Qt::white);
1133 QFont font("Monospace");
1134 font.setStyleHint(QFont::Monospace);
1135 font.setBold(true);
1136 setFont(font);
1139 void StatusLabel::setBackground(const QBrush & brush)
1141 QPalette plt(palette());
1142 plt.setBrush(QPalette::Window, brush);
1143 setPalette(plt);
1146 void StatusLabel::setTextColor(const QColor & color)
1148 QPalette plt(palette());
1149 plt.setColor(QPalette::WindowText, color);
1150 setPalette(plt);
1153 //////////////////////////// StatusClockLabel ////////////////////////////
1155 StatusClockLabel::StatusClockLabel(QWidget * parent):
1156 StatusLabel(parent)
1158 setTextColor(Qt::green);
1159 mTimerId = startTimer(1000);
1160 updateTime();
1163 StatusClockLabel::~StatusClockLabel()
1165 killTimer(mTimerId);
1168 void StatusClockLabel::timerEvent(QTimerEvent *e)
1170 if (e->timerId() == mTimerId)
1171 updateTime();
1174 void StatusClockLabel::updateTime()
1176 setText(QTime::currentTime().toString());
1179 } // namespace ScIDE