scide: improve menu bar
[supercollider.git] / editors / sc-ide / widgets / main_window.cpp
blob2c26c63e523b20d67ab4fcc649c59a6e6b2112c8
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/sc_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>
54 #include <QUrl>
56 namespace ScIDE {
58 MainWindow * MainWindow::mInstance = 0;
60 MainWindow::MainWindow(Main * main) :
61 mMain(main),
62 mClockLabel(0),
63 mDocDialog(0)
65 Q_ASSERT(!mInstance);
66 mInstance = this;
68 setAcceptDrops(true);
70 setCorner( Qt::BottomLeftCorner, Qt::LeftDockWidgetArea );
72 // Construct status bar:
74 mLangStatus = new StatusLabel();
75 mLangStatus->setText("Inactive");
76 mServerStatus = new StatusLabel();
77 onServerStatusReply(0, 0, 0, 0, 0, 0);
79 mStatusBar = statusBar();
80 mStatusBar->addPermanentWidget( new QLabel("Interpreter:") );
81 mStatusBar->addPermanentWidget( mLangStatus );
82 mStatusBar->addPermanentWidget( new QLabel("Server:") );
83 mStatusBar->addPermanentWidget( mServerStatus );
85 // Code editor
86 mEditors = new MultiEditor(main);
88 // Tools
90 mCmdLine = new CmdLine("Command Line:");
91 connect(mCmdLine, SIGNAL(invoked(QString, bool)),
92 main->scProcess(), SLOT(evaluateCode(QString, bool)));
94 mFindReplaceTool = new TextFindReplacePanel;
96 mGoToLineTool = new GoToLineTool();
97 connect(mGoToLineTool, SIGNAL(activated(int)), this, SLOT(hideToolBox()));
99 mToolBox = new ToolBox;
100 mToolBox->addWidget(mCmdLine);
101 mToolBox->addWidget(mFindReplaceTool);
102 mToolBox->addWidget(mGoToLineTool);
103 mToolBox->hide();
105 // Docks
106 mDocListDock = new DocumentsDock(main->documentManager(), this);
107 mDocListDock->setObjectName("documents-dock");
108 addDockWidget(Qt::RightDockWidgetArea, mDocListDock);
109 mDocListDock->hide();
112 mPostDock = new PostDock(this);
113 mPostDock->setObjectName("post-dock");
114 addDockWidget(Qt::LeftDockWidgetArea, mPostDock);
116 // Layout
117 QVBoxLayout *center_box = new QVBoxLayout;
118 center_box->setContentsMargins(0,0,0,0);
119 center_box->setSpacing(0);
120 center_box->addWidget(mEditors);
121 center_box->addWidget(mToolBox);
123 QWidget *central = new QWidget;
124 central->setLayout(center_box);
125 setCentralWidget(central);
127 // Session management
128 connect(main->sessionManager(), SIGNAL(saveSessionRequest(Session*)),
129 this, SLOT(saveSession(Session*)));
130 connect(main->sessionManager(), SIGNAL(switchSessionRequest(Session*)),
131 this, SLOT(switchSession(Session*)));
132 connect(main->sessionManager(), SIGNAL(currentSessionNameChanged()),
133 this, SLOT(updateWindowTitle()));
134 // A system for easy evaluation of pre-defined code:
135 connect(&mCodeEvalMapper, SIGNAL(mapped(QString)),
136 this, SIGNAL(evaluateCode(QString)));
137 connect(this, SIGNAL(evaluateCode(QString,bool)),
138 main->scProcess(), SLOT(evaluateCode(QString,bool)));
139 // Interpreter: post output
140 connect(main->scProcess(), SIGNAL( scPost(QString) ),
141 mPostDock->mPostWindow, SLOT( post(QString) ) );
142 // Interpreter: monitor running state
143 connect(main->scProcess(), SIGNAL( stateChanged(QProcess::ProcessState) ),
144 this, SLOT( onInterpreterStateChanged(QProcess::ProcessState) ) );
145 // Interpreter: forward status messages
146 connect(main->scProcess(), SIGNAL(statusMessage(const QString&)),
147 this, SLOT(showStatusMessage(const QString&)));
149 // Document list interaction
150 connect(mDocListDock->list(), SIGNAL(clicked(Document*)),
151 mEditors, SLOT(setCurrent(Document*)));
152 connect(mEditors, SIGNAL(currentDocumentChanged(Document*)),
153 mDocListDock->list(), SLOT(setCurrent(Document*)),
154 Qt::QueuedConnection);
156 // Update actions on document change
157 connect(mEditors, SIGNAL(currentDocumentChanged(Document*)),
158 this, SLOT(onCurrentDocumentChanged(Document*)));
159 // Document management
160 DocumentManager *docMng = main->documentManager();
161 connect(docMng, SIGNAL(changedExternally(Document*)),
162 this, SLOT(onDocumentChangedExternally(Document*)));
163 connect(docMng, SIGNAL(recentsChanged()),
164 this, SLOT(updateRecentDocsMenu()));
166 connect(main, SIGNAL(applySettingsRequest(Settings::Manager*)),
167 this, SLOT(applySettings(Settings::Manager*)));
169 // ToolBox
170 connect(mToolBox->closeButton(), SIGNAL(clicked()), this, SLOT(hideToolBox()));
172 connect(main->scResponder(), SIGNAL(serverRunningChanged(bool,QString,int)), this, SLOT(onServerRunningChanged(bool,QString,int)));
173 connect(main->scServer(), SIGNAL(updateServerStatus(int,int,int,int,float,float)), this, SLOT(onServerStatusReply(int,int,int,int,float,float)));
175 createActions();
176 createMenus();
178 // Must be called after createAtions(), because it accesses an action:
179 onServerRunningChanged(false, "", 0);
181 mServerStatus->addAction( action(ServerToggleRunning) );
182 mServerStatus->setContextMenuPolicy(Qt::ActionsContextMenu);
184 // Initialize recent documents menu
185 updateRecentDocsMenu();
187 QIcon icon;
188 icon.addFile(":/icons/sc-cube-128");
189 icon.addFile(":/icons/sc-cube-48");
190 icon.addFile(":/icons/sc-cube-32");
191 icon.addFile(":/icons/sc-cube-16");
192 QApplication::setWindowIcon(icon);
194 updateWindowTitle();
197 void MainWindow::createActions()
199 Settings::Manager *settings = mMain->settings();
200 settings->beginGroup("IDE/shortcuts");
202 QAction *act;
204 // File
205 mActions[Quit] = act = new QAction(
206 QIcon::fromTheme("application-exit"), tr("&Quit..."), this);
207 act->setShortcut(tr("Ctrl+Q", "Quit application"));
208 act->setStatusTip(tr("Quit SuperCollider IDE"));
209 QObject::connect( act, SIGNAL(triggered()), this, SLOT(onQuit()) );
211 mActions[DocNew] = act = new QAction(
212 QIcon::fromTheme("document-new"), tr("&New"), this);
213 act->setShortcut(tr("Ctrl+N", "New document"));
214 act->setStatusTip(tr("Create a new document"));
215 connect(act, SIGNAL(triggered()), this, SLOT(newDocument()));
217 mActions[DocOpen] = act = new QAction(
218 QIcon::fromTheme("document-open"), tr("&Open..."), this);
219 act->setShortcut(tr("Ctrl+O", "Open document"));
220 act->setStatusTip(tr("Open an existing file"));
221 connect(act, SIGNAL(triggered()), this, SLOT(openDocument()));
223 mActions[DocSave] = act = new QAction(
224 QIcon::fromTheme("document-save"), tr("&Save"), this);
225 act->setShortcut(tr("Ctrl+S", "Save document"));
226 act->setStatusTip(tr("Save the current document"));
227 connect(act, SIGNAL(triggered()), this, SLOT(saveDocument()));
229 mActions[DocSaveAs] = act = new QAction(
230 QIcon::fromTheme("document-save-as"), tr("Save &As..."), this);
231 act->setShortcut(tr("Ctrl+Shift+S", "Save &As..."));
232 act->setStatusTip(tr("Save the current document into a different file"));
233 connect(act, SIGNAL(triggered()), this, SLOT(saveDocumentAs()));
235 mActions[DocSaveAll] = act = new QAction(
236 QIcon::fromTheme("document-save"), tr("Save All..."), this);
237 act->setShortcut(tr("Ctrl+Alt+S", "Save all documents"));
238 act->setStatusTip(tr("Save all open documents"));
239 connect(act, SIGNAL(triggered()), this, SLOT(saveAllDocuments()));
241 mActions[DocClose] = act = new QAction(
242 QIcon::fromTheme("window-close"), tr("&Close"), this);
243 act->setShortcut(tr("Ctrl+W", "Close document"));
244 act->setStatusTip(tr("Close the current document"));
245 connect(act, SIGNAL(triggered()), this, SLOT(closeDocument()));
247 mActions[DocCloseAll] = act = new QAction(
248 QIcon::fromTheme("window-close"), tr("Close All..."), this);
249 act->setShortcut(tr("Ctrl+Shift+W", "Close all documents"));
250 act->setStatusTip(tr("Close all documents"));
251 connect(act, SIGNAL(triggered()), this, SLOT(closeAllDocuments()));
253 mActions[DocReload] = act = new QAction(
254 QIcon::fromTheme("view-refresh"), tr("&Reload"), this);
255 act->setShortcut(tr("F5", "Reload document"));
256 act->setStatusTip(tr("Reload the current document"));
257 connect(act, SIGNAL(triggered()), this, SLOT(reloadDocument()));
259 mActions[ClearRecentDocs] = act = new QAction(tr("Clear", "Clear recent documents"), this);
260 connect(act, SIGNAL(triggered()),
261 Main::instance()->documentManager(), SLOT(clearRecents()));
263 // Sessions
264 mActions[NewSession] = act = new QAction(
265 QIcon::fromTheme("document-new"), tr("&New Session"), this);
266 act->setStatusTip(tr("Open a new session"));
267 connect(act, SIGNAL(triggered()), this, SLOT(newSession()));
269 mActions[SaveSessionAs] = act = new QAction(
270 QIcon::fromTheme("document-save-as"), tr("Save Session &As..."), this);
271 act->setStatusTip(tr("Save the current session with a different name"));
272 connect(act, SIGNAL(triggered()), this, SLOT(saveCurrentSessionAs()));
274 mActions[ManageSessions] = act = new QAction(
275 tr("&Manage Sessions..."), this);
276 connect(act, SIGNAL(triggered()), this, SLOT(openSessionsDialog()));
278 mActions[OpenSessionSwitchDialog] = act = new QAction(
279 tr("&Switch Session..."), this);
280 connect(act, SIGNAL(triggered()), this, SLOT(showSwitchSessionDialog()));
281 act->setShortcut(tr("Ctrl+Shift+Q", "Switch Session"));
283 // Edit
284 mActions[Find] = act = new QAction(
285 QIcon::fromTheme("edit-find"), tr("&Find..."), this);
286 act->setShortcut(tr("Ctrl+F", "Find"));
287 act->setStatusTip(tr("Find text in document"));
288 connect(act, SIGNAL(triggered()), this, SLOT(showFindTool()));
290 mActions[Replace] = act = new QAction(
291 QIcon::fromTheme("edit-replace"), tr("&Replace..."), this);
292 act->setShortcut(tr("Ctrl+R", "Replace"));
293 act->setStatusTip(tr("Find and replace text in document"));
294 connect(act, SIGNAL(triggered()), this, SLOT(showReplaceTool()));
296 // View
297 mActions[ShowCmdLine] = act = new QAction(tr("&Command Line"), this);
298 act->setStatusTip(tr("Command line for quick code evaluation"));
299 act->setShortcut(tr("Ctrl+E", "Show command line"));
300 connect(act, SIGNAL(triggered()), this, SLOT(showCmdLine()));
302 mActions[ShowGoToLineTool] = act = new QAction(tr("&Go To Line"), this);
303 act->setStatusTip(tr("Tool to jump to a line by number"));
304 act->setShortcut(tr("Ctrl+G", "Show go-to-line tool"));
305 connect(act, SIGNAL(triggered()), this, SLOT(showGoToLineTool()));
307 mActions[CloseToolBox] = act = new QAction(
308 QIcon::fromTheme("window-close"), tr("&Close Tool Panel"), this);
309 act->setStatusTip(tr("Close any open tool panel"));
310 act->setShortcut(tr("Esc", "Close tool box"));
311 connect(act, SIGNAL(triggered()), this, SLOT(hideToolBox()));
313 mActions[ShowFullScreen] = act = new QAction(tr("&Full Screen"), this);
314 act->setCheckable(false);
315 act->setShortcut(tr("Ctrl+Shift+F", "Show ScIDE in Full Screen"));
316 connect(act, SIGNAL(triggered()), this, SLOT(toggleFullScreen()));
318 mActions[ClearPostWindow] = act = new QAction(
319 QIcon::fromTheme("window-clearpostwindow"), tr("Clear Post Window"), this);
320 act->setStatusTip(tr("Clear Post Window"));
321 act->setShortcut(tr("Ctrl+Shift+C", "Clear Post Window"));
322 connect(act, SIGNAL(triggered()), mPostDock->mPostWindow, SLOT(clear()));
324 mActions[FocusPostWindow] = act = new QAction( tr("Focus Post Window"), this);
325 act->setStatusTip(tr("Focus Post Window"));
326 act->setShortcut(tr("Ctrl+L", "Focus Post Window"));
327 connect(act, SIGNAL(triggered()), mPostDock->mPostWindow, SLOT(setFocus()));
329 mActions[LookupReferences] = act = new QAction(
330 QIcon::fromTheme("window-lookupreferences"), tr("Look Up References"), this);
331 act->setShortcut(tr("Ctrl+Shift+U", "Look Up References"));
332 connect(act, SIGNAL(triggered()), this, SLOT(lookupReferences()));
334 mActions[LookupDefinition] = act = new QAction(
335 QIcon::fromTheme("window-lookupdefinition"), tr("Look Up Definition"), this);
336 act->setShortcut(tr("Ctrl+Shift+I", "Look Up Definition"));
337 connect(act, SIGNAL(triggered()), this, SLOT(lookupDefinition()));
339 mActions[LookupDocumentation] = act = new QAction(
340 QIcon::fromTheme("window-lookupDocumentation"), tr("Look Up Documentation"), this);
341 act->setShortcut(tr("Ctrl+Shift+D", "Look Up Documentation"));
342 connect(act, SIGNAL(triggered()), this, SLOT(lookupDocumentation()));
344 // Language
345 mActions[OpenDefinition] = act = new QAction(tr("Open Class/Method Definition"), this);
346 act->setShortcut(tr("Ctrl+I", "Open definition of selected class or method"));
347 connect(act, SIGNAL(triggered(bool)), this, SLOT(openDefinition()));
349 mActions[FindReferences] = act = new QAction(tr("Find References"), this);
350 act->setShortcut(tr("Ctrl+U", "Find References"));
351 connect(act, SIGNAL(triggered(bool)), this, SLOT(findReferences()));
353 // Settings
354 mActions[ShowSettings] = act = new QAction(tr("&Configure IDE..."), this);
355 act->setStatusTip(tr("Show configuration dialog"));
356 connect(act, SIGNAL(triggered()), this, SLOT(showSettings()));
359 // Help
360 mActions[Help] = act = new QAction(
361 QIcon::fromTheme("system-help"), tr("Open Help Browser"), this);
362 act->setStatusTip(tr("Open help."));
363 connect(act, SIGNAL(triggered()), this, SLOT(openHelp()));
365 mActions[HelpForSelection] = act = new QAction(
366 QIcon::fromTheme("system-help"), tr("&Help for Selection"), this);
367 act->setShortcut(tr("Ctrl+D", "Help for selection"));
368 act->setStatusTip(tr("Find help for selected text"));
369 connect(act, SIGNAL(triggered()), this, SLOT(openDocumentation()));
371 // Server
372 mActions[ServerToggleRunning] = act = new QAction(tr("Boot or quit server"), this);
373 act->setShortcut(tr("Ctrl+B", "Boot or quit default server"));
374 connect(act, SIGNAL(triggered()), this, SLOT(serverToggleRunning()));
376 mActions[ServerReboot] = act = new QAction(tr("Reboot server"), this);
377 act->setShortcut(tr("Ctrl+Shift+B", "Reboot default server"));
378 connect(act, SIGNAL(triggered()), this, SLOT(serverReboot()));
380 mActions[ServerShowMeters] = act = new QAction(tr("Show server meter"), this);
381 act->setShortcut(tr("Ctrl+M", "Show server meter"));
382 connect(act, SIGNAL(triggered()), this, SLOT(serverShowMeters()));
384 mActions[ServerDumpNodeTree] = act = new QAction(tr("Dump node tree"), this);
385 act->setShortcut(tr("Ctrl+T", "Dump node tree"));
386 connect(act, SIGNAL(triggered()), this, SLOT(serverDumpNodeTree()));
388 mActions[ServerDumpNodeTreeWithControls] = act = new QAction(tr("Dump node tree with controls"), this);
389 act->setShortcut(tr("Ctrl+Shift+T", "Dump node tree with controls"));
390 connect(act, SIGNAL(triggered()), this, SLOT(serverDumpNodeTreeWithControls()));
392 settings->endGroup(); // IDE/shortcuts;
395 // Add actions to settings
396 for (int i = 0; i < ActionCount; ++i)
397 settings->addAction( mActions[i] );
400 void MainWindow::createMenus()
402 QMenu *menu;
403 QMenu *submenu;
405 menu = new QMenu(tr("&File"), this);
406 menu->addAction( mActions[DocNew] );
407 menu->addAction( mActions[DocOpen] );
408 mRecentDocsMenu = menu->addMenu(tr("Open Recent", "Open a recent document"));
409 connect(mRecentDocsMenu, SIGNAL(triggered(QAction*)),
410 this, SLOT(onRecentDocAction(QAction*)));
411 menu->addAction( mActions[DocSave] );
412 menu->addAction( mActions[DocSaveAs] );
413 menu->addAction( mActions[DocSaveAll] );
414 menu->addSeparator();
415 menu->addAction( mActions[DocReload] );
416 menu->addSeparator();
417 menu->addAction( mActions[DocClose] );
418 menu->addAction( mActions[DocCloseAll] );
419 menu->addSeparator();
420 menu->addAction( mActions[Quit] );
422 menuBar()->addMenu(menu);
424 menu = new QMenu(tr("&Session"), this);
425 menu->addAction( mActions[NewSession] );
426 menu->addAction( mActions[SaveSessionAs] );
427 submenu = menu->addMenu(tr("&Open Session"));
428 connect(submenu, SIGNAL(triggered(QAction*)),
429 this, SLOT(onOpenSessionAction(QAction*)));
430 mSessionsMenu = submenu;
431 updateSessionsMenu();
432 menu->addSeparator();
433 menu->addAction( mActions[ManageSessions] );
434 menu->addAction( mActions[OpenSessionSwitchDialog] );
436 menuBar()->addMenu(menu);
438 menu = new QMenu(tr("&Edit"), this);
439 menu->addAction( mEditors->action(MultiEditor::Undo) );
440 menu->addAction( mEditors->action(MultiEditor::Redo) );
441 menu->addSeparator();
442 menu->addAction( mEditors->action(MultiEditor::Cut) );
443 menu->addAction( mEditors->action(MultiEditor::Copy) );
444 menu->addAction( mEditors->action(MultiEditor::Paste) );
445 menu->addSeparator();
446 menu->addAction( mActions[Find] );
447 menu->addAction( mActions[Replace] );
448 menu->addSeparator();
449 menu->addAction( mEditors->action(MultiEditor::IndentLineOrRegion) );
450 menu->addAction( mEditors->action(MultiEditor::ToggleComment) );
451 menu->addAction( mEditors->action(MultiEditor::ToggleOverwriteMode) );
452 menu->addAction( mEditors->action(MultiEditor::SelectRegion) );
454 menuBar()->addMenu(menu);
456 menu = new QMenu(tr("&View"), this);
457 submenu = new QMenu(tr("&Docks"), this);
458 submenu->addAction( mPostDock->toggleViewAction() );
459 submenu->addAction( mDocListDock->toggleViewAction() );
460 menu->addMenu(submenu);
461 menu->addSeparator();
462 submenu = menu->addMenu(tr("&Tool Panels"));
463 submenu->addAction( mActions[Find] );
464 submenu->addAction( mActions[Replace] );
465 submenu->addAction( mActions[ShowCmdLine] );
466 submenu->addAction( mActions[ShowGoToLineTool] );
467 submenu->addSeparator();
468 submenu->addAction( mActions[CloseToolBox] );
469 menu->addSeparator();
470 menu->addAction( mEditors->action(MultiEditor::EnlargeFont) );
471 menu->addAction( mEditors->action(MultiEditor::ShrinkFont) );
472 menu->addAction( mEditors->action(MultiEditor::ResetFontSize) );
473 menu->addSeparator();
474 menu->addAction( mEditors->action(MultiEditor::ShowWhitespace) );
475 menu->addSeparator();
476 menu->addAction( mEditors->action(MultiEditor::NextDocument) );
477 menu->addAction( mEditors->action(MultiEditor::PreviousDocument) );
478 menu->addSeparator();
479 menu->addAction( mEditors->action(MultiEditor::SplitHorizontally) );
480 menu->addAction( mEditors->action(MultiEditor::SplitVertically) );
481 menu->addAction( mEditors->action(MultiEditor::RemoveCurrentSplit) );
482 menu->addAction( mEditors->action(MultiEditor::RemoveAllSplits) );
483 menu->addSeparator();
484 menu->addAction( mActions[FocusPostWindow] );
485 menu->addAction( mActions[ClearPostWindow] );
486 menu->addSeparator();
487 menu->addAction( mActions[ShowFullScreen] );
488 menu->addSeparator();
489 menu->addAction( mActions[OpenDefinition] );
490 menu->addAction( mActions[LookupDefinition] );
491 menu->addAction( mActions[FindReferences] );
492 menu->addAction( mActions[LookupReferences] );
494 menuBar()->addMenu(menu);
496 menu = new QMenu(tr("&Language"), this);
497 menu->addAction( mMain->scProcess()->action(SCProcess::StartSCLang) );
498 menu->addAction( mMain->scProcess()->action(SCProcess::StopSCLang) );
499 menu->addAction( mMain->scProcess()->action(SCProcess::RestartSCLang) );
500 menu->addAction( mMain->scProcess()->action(SCProcess::RecompileClassLibrary) );
501 menu->addSeparator();
502 menu->addAction( mActions[ServerToggleRunning] );
503 menu->addAction( mActions[ServerReboot] );
504 menu->addAction( mActions[ServerShowMeters] );
505 menu->addAction( mActions[ServerDumpNodeTree] );
506 menu->addAction( mActions[ServerDumpNodeTreeWithControls] );
507 menu->addSeparator();
508 menu->addAction( mEditors->action(MultiEditor::EvaluateCurrentDocument) );
509 menu->addAction( mEditors->action(MultiEditor::EvaluateRegion) );
510 menu->addAction( mEditors->action(MultiEditor::EvaluateLine) );
511 menu->addSeparator();
512 menu->addAction( mMain->scProcess()->action(ScIDE::SCProcess::RunMain) );
513 menu->addAction( mMain->scProcess()->action(ScIDE::SCProcess::StopMain) );
515 menuBar()->addMenu(menu);
517 menu = new QMenu(tr("&Settings"), this);
518 menu->addAction( mActions[ShowSettings] );
520 menuBar()->addMenu(menu);
522 menu = new QMenu(tr("&Help"), this);
523 menu->addAction( mActions[Help] );
524 menu->addAction( mActions[HelpForSelection] );
525 menu->addAction( mActions[LookupDocumentation] );
527 menuBar()->addMenu(menu);
530 void MainWindow::saveWindowState()
532 Settings::Manager *settings = Main::settings();
533 settings->beginGroup("IDE/mainWindow");
534 settings->setValue("geometry", this->saveGeometry().toBase64());
535 settings->setValue("state", this->saveState().toBase64());
536 settings->endGroup();
539 void MainWindow::restoreWindowState()
541 Settings::Manager *settings = Main::settings();
542 settings->beginGroup("IDE/mainWindow");
544 QByteArray geom = QByteArray::fromBase64( settings->value("geometry").value<QByteArray>() );
545 if (!geom.isEmpty())
546 restoreGeometry(geom);
547 else
548 setWindowState( windowState() & ~Qt::WindowFullScreen | Qt::WindowMaximized );
550 QByteArray state = QByteArray::fromBase64( settings->value("state").value<QByteArray>() );
551 if (!state.isEmpty())
552 restoreState(state);
554 settings->endGroup();
557 void MainWindow::focusCodeEditor()
559 if (mEditors->currentEditor())
560 mEditors->currentEditor()->setFocus();
561 else
562 mEditors->setFocus();
565 void MainWindow::newSession()
567 if (promptSaveDocs())
568 mMain->sessionManager()->newSession();
571 void MainWindow::saveCurrentSessionAs()
573 QString name = QInputDialog::getText( this,
574 "Save Current Session",
575 "Enter a name for the session:" );
577 if (name.isEmpty()) return;
579 mMain->sessionManager()->saveSessionAs(name);
581 updateSessionsMenu();
584 void MainWindow::onOpenSessionAction( QAction * action )
586 openSession(action->text());
589 void MainWindow::switchSession( Session *session )
591 if (session) {
592 session->beginGroup("mainWindow");
593 QByteArray geom = QByteArray::fromBase64( session->value("geometry").value<QByteArray>() );
594 QByteArray state = QByteArray::fromBase64( session->value("state").value<QByteArray>() );
595 session->endGroup();
597 // Workaround for Qt bug 4397:
598 setWindowState(Qt::WindowNoState);
600 if (!geom.isEmpty())
601 restoreGeometry(geom);
602 if (!state.isEmpty())
603 restoreState(state);
605 updateClockWidget(isFullScreen());
608 mEditors->switchSession(session);
610 updateWindowTitle();
613 void MainWindow::saveSession( Session *session )
615 session->beginGroup("mainWindow");
616 session->setValue("geometry", this->saveGeometry().toBase64());
617 session->setValue("state", this->saveState().toBase64());
618 session->endGroup();
620 mEditors->saveSession(session);
623 void MainWindow::openSessionsDialog()
625 QPointer<MainWindow> mainwin(this);
626 SessionsDialog dialog(mMain->sessionManager(), this);
627 dialog.exec();
628 if (mainwin)
629 mainwin->updateSessionsMenu();
632 QAction *MainWindow::action( ActionRole role )
634 Q_ASSERT( role < ActionCount );
635 return mActions[role];
638 bool MainWindow::quit()
640 if (!promptSaveDocs())
641 return false;
643 saveWindowState();
645 mMain->quit();
647 return true;
650 void MainWindow::onQuit()
652 quit();
655 void MainWindow::onCurrentDocumentChanged( Document * doc )
657 updateWindowTitle();
659 mActions[DocSave]->setEnabled(doc);
660 mActions[DocSaveAs]->setEnabled(doc);
661 mActions[DocClose]->setEnabled(doc);
663 ScCodeEditor *editor = mEditors->currentEditor();
664 mFindReplaceTool->setEditor( editor );
665 mGoToLineTool->setEditor( editor );
668 void MainWindow::onDocumentChangedExternally( Document *doc )
670 if (mDocDialog)
671 return;
673 mDocDialog = new DocumentsDialog(DocumentsDialog::ExternalChange, this);
674 mDocDialog->addDocument(doc);
675 connect(mDocDialog, SIGNAL(finished(int)), this, SLOT(onDocDialogFinished()));
676 mDocDialog->open();
679 void MainWindow::onDocDialogFinished()
681 mDocDialog->deleteLater();
682 mDocDialog = 0;
685 void MainWindow::updateRecentDocsMenu()
687 mRecentDocsMenu->clear();
689 const QStringList &recent = mMain->documentManager()->recents();
691 foreach( const QString & path, recent )
692 mRecentDocsMenu->addAction(path);
694 if (!recent.isEmpty()) {
695 mRecentDocsMenu->addSeparator();
696 mRecentDocsMenu->addAction(mActions[ClearRecentDocs]);
700 void MainWindow::onRecentDocAction( QAction *action )
702 mMain->documentManager()->open(action->text());
705 void MainWindow::onInterpreterStateChanged( QProcess::ProcessState state )
707 QString text;
708 QColor color;
710 switch(state) {
711 case QProcess::NotRunning:
712 text = "Inactive";
713 color = Qt::white;
714 break;
715 case QProcess::Starting:
716 text = "Booting";
717 color = QColor(255,255,0);
718 break;
719 case QProcess::Running:
720 text = "Active";
721 color = Qt::green;
722 break;
725 mLangStatus->setText(text);
726 mLangStatus->setTextColor(color);
730 void MainWindow::onServerStatusReply(int ugens, int synths, int groups, int synthDefs, float avgCPU, float peakCPU)
732 QString statusString =
733 QString("%1% %2% %3u %4s %5g %6d")
734 .arg(avgCPU, 5, 'f', 2)
735 .arg(peakCPU, 5, 'f', 2)
736 .arg(ugens, 4)
737 .arg(synths, 4)
738 .arg(groups, 4)
739 .arg(synthDefs, 4);
741 mServerStatus->setText(statusString);
744 void MainWindow::onServerRunningChanged(bool running, const QString &, int)
746 QAction *serverStatusAction = mActions[ServerToggleRunning];
748 mServerStatus->setTextColor( running ? Qt::green : Qt::white);
749 if (!running) {
750 onServerStatusReply(0, 0, 0, 0, 0, 0);
752 serverStatusAction->setText( tr("&Boot Server") );
753 serverStatusAction->setStatusTip(tr("Boot sound synthesis server"));
754 } else {
755 serverStatusAction->setText( tr("&Quit Server") );
756 serverStatusAction->setStatusTip(tr("Quit sound synthesis server"));
760 void MainWindow::closeEvent(QCloseEvent *event)
762 if(!quit()) event->ignore();
765 bool MainWindow::close( Document *doc )
767 if (doc->textDocument()->isModified())
769 QMessageBox::StandardButton ret;
770 ret = QMessageBox::warning(
771 mInstance,
772 tr("SuperCollider IDE"),
773 tr("There are unsaved changes in document '%1'.\n\n"
774 "Do you want to save it?").arg(doc->title()),
775 QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel,
776 QMessageBox::Save // the default
779 switch (ret) {
780 case QMessageBox::Cancel:
781 return false;
782 case QMessageBox::Save:
783 if (!MainWindow::save(doc))
784 return false;
785 break;
786 default:;
790 Main::instance()->documentManager()->close(doc);
791 return true;
794 bool MainWindow::reload( Document *doc )
796 if (doc->filePath().isEmpty())
797 return false;
799 if (doc->textDocument()->isModified())
801 QMessageBox::StandardButton ret;
802 ret = QMessageBox::warning(
803 mInstance,
804 tr("SuperCollider IDE"),
805 tr("There are unsaved changes in document '%1'.\n\n"
806 "Do you want to reload it?").arg(doc->title()),
807 QMessageBox::Yes | QMessageBox::No,
808 QMessageBox::No // the default
810 if (ret == QMessageBox::No)
811 return false;
814 return Main::instance()->documentManager()->reload(doc);
817 bool MainWindow::save( Document *doc, bool forceChoose )
819 DocumentManager *mng = Main::instance()->documentManager();
820 if (forceChoose || doc->filePath().isEmpty()) {
821 QFileDialog dialog(mInstance);
822 dialog.setAcceptMode( QFileDialog::AcceptSave );
824 QStringList filters = (QStringList()
825 << "SuperCollider Document(*.scd)"
826 << "SuperCollider Class file(*.sc)"
827 << "SCDoc(*.schelp)"
828 << "All files(*)");
830 dialog.setNameFilters(filters);
831 dialog.setDefaultSuffix("scd");
833 if (dialog.exec() == QDialog::Accepted)
834 return mng->saveAs(doc, dialog.selectedFiles()[0]);
835 else
836 return false;
837 } else
838 return mng->save(doc);
841 void MainWindow::newDocument()
843 mMain->documentManager()->create();
846 void MainWindow::openDocument()
848 QFileDialog dialog (this, Qt::Dialog);
849 dialog.setModal(true);
850 dialog.setWindowModality(Qt::ApplicationModal);
852 dialog.setFileMode( QFileDialog::ExistingFiles );
854 ScCodeEditor * currentEditor = mEditors->currentEditor();
855 if (currentEditor) {
856 Document * currentDocument = currentEditor->document();
857 QFileInfo filePath (currentDocument->filePath());
858 dialog.setDirectory(filePath.dir());
861 QStringList filters;
862 filters
863 << "All files(*)"
864 << "SuperCollider(*.scd *.sc)"
865 << "SCDoc(*.schelp)";
866 dialog.setNameFilters(filters);
868 if (dialog.exec())
870 QStringList filenames = dialog.selectedFiles();
871 foreach(QString filename, filenames)
872 mMain->documentManager()->open(filename);
876 void MainWindow::saveDocument()
878 ScCodeEditor *editor = mEditors->currentEditor();
879 if(!editor) return;
881 Document *doc = editor->document();
882 Q_ASSERT(doc);
884 MainWindow::save(doc);
887 void MainWindow::saveDocumentAs()
889 ScCodeEditor *editor = mEditors->currentEditor();
890 if(!editor) return;
892 Document *doc = editor->document();
893 Q_ASSERT(doc);
895 MainWindow::save(doc, true);
898 void MainWindow::saveAllDocuments()
900 QList<Document*> docs = mMain->documentManager()->documents();
901 foreach (Document *doc, docs)
902 if (!MainWindow::save(doc))
903 return;
906 void MainWindow::reloadDocument()
908 ScCodeEditor *editor = mEditors->currentEditor();
909 if(!editor) return;
911 Q_ASSERT(editor->document());
912 MainWindow::reload(editor->document());
915 void MainWindow::closeDocument()
917 ScCodeEditor *editor = mEditors->currentEditor();
918 if(!editor) return;
920 Q_ASSERT(editor->document());
921 MainWindow::close( editor->document() );
924 void MainWindow::closeAllDocuments()
926 if (promptSaveDocs()) {
927 QList<Document*> docs = mMain->documentManager()->documents();
928 foreach (Document *doc, docs)
929 mMain->documentManager()->close(doc);
933 bool MainWindow::promptSaveDocs()
935 QList<Document*> docs = mMain->documentManager()->documents();
936 QList<Document*> unsavedDocs;
937 foreach(Document* doc, docs)
938 if(doc->textDocument()->isModified())
939 unsavedDocs.append(doc);
941 if (unsavedDocs.count())
943 DocumentsDialog dialog(unsavedDocs, DocumentsDialog::Quit, this);
945 if (!dialog.exec())
946 return false;
949 return true;
952 void MainWindow::updateWindowTitle()
954 Session *session = mMain->sessionManager()->currentSession();
955 ScCodeEditor *editor = mEditors->currentEditor();
956 Document *doc = editor ? editor->document() : 0;
958 QString title;
960 if (session) {
961 title.append(session->name());
962 if (doc) title.append(": ");
965 if (doc) {
966 if (!doc->filePath().isEmpty()) {
967 QFileInfo info = QFileInfo(doc->filePath());
968 QString pathString = info.dir().path();
970 QString homePath = QDir::homePath();
971 if (pathString.startsWith(homePath))
972 pathString.replace(0, homePath.size(), QString("~"));
974 QString titleString = QString("%1 (%2)").arg(info.fileName(), pathString);
976 title.append( titleString );
977 } else
978 title.append( "Untitled" );
981 if (!title.isEmpty())
982 title.append(" - ");
984 title.append("SuperCollider IDE");
986 setWindowTitle(title);
989 void MainWindow::toggleFullScreen()
991 if (isFullScreen()) {
992 setWindowState(windowState() & ~Qt::WindowFullScreen);
994 updateClockWidget(false);
995 } else {
996 setWindowState(windowState() | Qt::WindowFullScreen);
998 updateClockWidget(true);
1002 void MainWindow::updateClockWidget(bool isFullScreen)
1004 if (!isFullScreen) {
1005 if (mClockLabel) {
1006 delete mClockLabel;
1007 mClockLabel = NULL;
1009 } else {
1010 if (mClockLabel == NULL) {
1011 mClockLabel = new StatusClockLabel(this);
1012 statusBar()->insertWidget(0, mClockLabel);
1017 void MainWindow::openSession(const QString &sessionName)
1019 if (promptSaveDocs())
1020 mMain->sessionManager()->openSession( sessionName );
1023 void MainWindow::openDefinition()
1025 QWidget * focussedWidget = QApplication::focusWidget();
1027 int indexOfMethod = focussedWidget->metaObject()->indexOfMethod("openDefinition()");
1028 if (indexOfMethod != -1)
1029 QMetaObject::invokeMethod( focussedWidget, "openDefinition", Qt::DirectConnection );
1032 void MainWindow::lookupDefinition()
1034 LookupDialog dialog(mEditors);
1035 dialog.exec();
1038 void MainWindow::lookupDocumentation()
1040 PopupTextInput * dialog = new PopupTextInput(tr("Look up Documentation For"), this);
1042 bool success = dialog->exec();
1043 if (success)
1044 Main::openDocumentation(dialog->textValue());
1046 delete dialog;
1049 void MainWindow::findReferences()
1051 QWidget * focussedWidget = QApplication::focusWidget();
1053 int indexOfMethod = focussedWidget->metaObject()->indexOfMethod("findReferences()");
1054 if (indexOfMethod != -1)
1055 QMetaObject::invokeMethod( focussedWidget, "findReferences", Qt::DirectConnection );
1058 void MainWindow::lookupReferences()
1060 ReferencesDialog dialog(parentWidget());
1061 dialog.exec();
1065 void MainWindow::showStatusMessage( QString const & string )
1067 mStatusBar->showMessage(string, 3000);
1070 void MainWindow::applySettings( Settings::Manager * settings )
1072 mEditors->applySettings(settings);
1073 mPostDock->mPostWindow->applySettings(settings);
1074 mCmdLine->applySettings(settings);
1078 void MainWindow::updateSessionsMenu()
1080 mSessionsMenu->clear();
1081 QStringList sessions = mMain->sessionManager()->availableSessions();
1082 foreach (const QString & session, sessions)
1083 mSessionsMenu->addAction( session );
1086 void MainWindow::showSwitchSessionDialog()
1088 SessionSwitchDialog * dialog = new SessionSwitchDialog(this);
1089 int result = dialog->exec();
1091 if (result == QDialog::Accepted)
1092 openSession(dialog->activeElement());
1094 delete dialog;
1097 void MainWindow::showCmdLine()
1099 mToolBox->setCurrentWidget( mCmdLine );
1100 mToolBox->show();
1102 mCmdLine->setFocus(Qt::OtherFocusReason);
1105 void MainWindow::showGoToLineTool()
1107 ScCodeEditor *editor = mEditors->currentEditor();
1108 mGoToLineTool->setValue( editor ? editor->textCursor().blockNumber() + 1 : 0 );
1110 mToolBox->setCurrentWidget( mGoToLineTool );
1111 mToolBox->show();
1113 mGoToLineTool->setFocus();
1116 void MainWindow::showFindTool()
1118 mFindReplaceTool->setMode( TextFindReplacePanel::Find );
1119 mFindReplaceTool->initiate();
1121 mToolBox->setCurrentWidget( mFindReplaceTool );
1122 mToolBox->show();
1124 mFindReplaceTool->setFocus(Qt::OtherFocusReason);
1127 void MainWindow::showReplaceTool()
1129 mFindReplaceTool->setMode( TextFindReplacePanel::Replace );
1130 mFindReplaceTool->initiate();
1132 mToolBox->setCurrentWidget( mFindReplaceTool );
1133 mToolBox->show();
1135 mFindReplaceTool->setFocus(Qt::OtherFocusReason);
1138 void MainWindow::hideToolBox()
1140 mToolBox->hide();
1141 ScCodeEditor *editor = mEditors->currentEditor();
1142 if (editor) {
1143 // This slot is mapped to Escape, so also clear highlighting
1144 // whenever invoked:
1145 editor->clearSearchHighlighting();
1146 if (!editor->hasFocus())
1147 editor->setFocus(Qt::OtherFocusReason);
1151 void MainWindow::showSettings()
1153 Settings::Dialog dialog(mMain->settings());
1154 dialog.resize(700,400);
1155 int result = dialog.exec();
1156 if( result == QDialog::Accepted )
1157 mMain->applySettings();
1160 void MainWindow::openDocumentation()
1162 QWidget * focussedWidget = QApplication::focusWidget();
1164 bool documentationOpened = false;
1166 int indexOfMethod = focussedWidget->metaObject()->indexOfMethod("openDocumentation()");
1167 if (indexOfMethod != -1)
1168 QMetaObject::invokeMethod( focussedWidget, "openDocumentation", Qt::DirectConnection,
1169 Q_RETURN_ARG(bool, documentationOpened) );
1171 if (!documentationOpened)
1172 openHelp();
1175 void MainWindow::openHelp()
1177 QString code = QString("Help.gui");
1178 Main::scProcess()->evaluateCode(code, true);
1181 void MainWindow::serverToggleRunning()
1183 ScServer *scServer = Main::scServer();
1185 if (scServer->isRunning())
1186 scServer->quit();
1187 else
1188 scServer->boot();
1191 void MainWindow::serverReboot()
1193 ScServer *scServer = Main::scServer();
1194 scServer->reboot();
1197 void MainWindow::serverShowMeters()
1199 Main::evaluateCode("ScIDE.defaultServer.meter", true);
1202 void MainWindow::serverDumpNodeTree()
1204 ScServer *scServer = Main::scServer();
1205 scServer->queryAllNodes(false);
1208 void MainWindow::serverDumpNodeTreeWithControls()
1210 ScServer *scServer = Main::scServer();
1211 scServer->queryAllNodes(true);
1214 void MainWindow::dragEnterEvent( QDragEnterEvent * event )
1216 if (event->mimeData()->hasUrls()) {
1217 foreach (QUrl url, event->mimeData()->urls()) {
1218 if (url.scheme() == QString("file")) { // LATER: use isLocalFile
1219 // LATER: check mime type ?
1220 event->acceptProposedAction();
1221 return;
1227 void MainWindow::dropEvent( QDropEvent * event )
1229 const QMimeData * data = event->mimeData();
1230 if (data->hasUrls()) {
1231 foreach (QUrl url, data->urls()) {
1232 if (url.scheme() == QString("file")) // LATER: use isLocalFile
1233 Main::documentManager()->open(url.toLocalFile());
1238 //////////////////////////// StatusLabel /////////////////////////////////
1240 StatusLabel::StatusLabel(QWidget *parent) : QLabel(parent)
1242 setAutoFillBackground(true);
1243 setMargin(3);
1244 setAlignment(Qt::AlignCenter);
1245 setBackground(Qt::black);
1246 setTextColor(Qt::white);
1248 QFont font("Monospace");
1249 font.setStyleHint(QFont::Monospace);
1250 font.setBold(true);
1251 setFont(font);
1254 void StatusLabel::setBackground(const QBrush & brush)
1256 QPalette plt(palette());
1257 plt.setBrush(QPalette::Window, brush);
1258 setPalette(plt);
1261 void StatusLabel::setTextColor(const QColor & color)
1263 QPalette plt(palette());
1264 plt.setColor(QPalette::WindowText, color);
1265 setPalette(plt);
1268 //////////////////////////// StatusClockLabel ////////////////////////////
1270 StatusClockLabel::StatusClockLabel(QWidget * parent):
1271 StatusLabel(parent)
1273 setTextColor(Qt::green);
1274 mTimerId = startTimer(1000);
1275 updateTime();
1278 StatusClockLabel::~StatusClockLabel()
1280 killTimer(mTimerId);
1283 void StatusClockLabel::timerEvent(QTimerEvent *e)
1285 if (e->timerId() == mTimerId)
1286 updateTime();
1289 void StatusClockLabel::updateTime()
1291 setText(QTime::currentTime().toString());
1294 } // namespace ScIDE