ScriptingWindow.cpp 28.5 KB
Newer Older
1
2
3
4
//-------------------------------------------
// Includes
//-------------------------------------------
#include "ScriptingWindow.h"
5
#include "ApplicationWindow.h"
Roman Tolchenov's avatar
Roman Tolchenov committed
6
#include "MantidQtWidgets/Common/TSVSerialiser.h"
7
#include "MultiTabScriptInterpreter.h"
8
#include "ScriptFileInterpreter.h"
9
#include "ScriptingEnv.h"
Roman Tolchenov's avatar
Roman Tolchenov committed
10
#include <MantidQtWidgets/Common/pixmaps.h>
11

12
13
// Mantid
#include "MantidKernel/ConfigService.h"
Nick Draper's avatar
Nick Draper committed
14
#include "MantidKernel/Logger.h"
Roman Tolchenov's avatar
Roman Tolchenov committed
15
#include "MantidQtWidgets/Common/IProjectSerialisable.h"
16
17

// MantidQt
Roman Tolchenov's avatar
Roman Tolchenov committed
18
19
#include "MantidQtWidgets/Common/HelpWindow.h"
#include "MantidQtWidgets/Common/ScriptEditor.h"
20

21
// Qt
22
#include <QAction>
23
#include <QApplication>
24
#include <QCloseEvent>
25
#include <QDateTime>
26
#include <QFileInfo>
27
28
29
#include <QList>
#include <QMenu>
#include <QMenuBar>
30
#include <QMessageBox>
31
32
33
34
#include <QPrintDialog>
#include <QPrinter>
#include <QSettings>
#include <QTextEdit>
35
#include <QTextStream>
Nick Draper's avatar
Nick Draper committed
36
#include <QUrl>
37

38
using namespace Mantid;
39
using namespace MantidQt::API;
40

41
42
43
namespace {
/// static logger
Mantid::Kernel::Logger g_log("ScriptingWindow");
Gigg, Martyn Anthony's avatar
Gigg, Martyn Anthony committed
44
45
}

46
47
48
49
50
//-------------------------------------------
// Public member functions
//-------------------------------------------
/**
 * Constructor
51
52
53
 * @param env :: The scripting environment
 * @param parent :: The parent widget
 * @param flags :: Window flags passed to the base class
54
 */
55
56
57
ScriptingWindow::ScriptingWindow(ScriptingEnv *env, bool capturePrint,
                                 QWidget *parent, Qt::WindowFlags flags)
    : QMainWindow(parent, flags), m_acceptClose(false) {
58
  Q_UNUSED(capturePrint);
59
  setObjectName("MantidScriptWindow");
Nick Draper's avatar
Nick Draper committed
60
  setAcceptDrops(true);
61

62
  // Sub-widgets
63
  m_manager = new MultiTabScriptInterpreter(env, this);
64
  setCentralWidget(m_manager);
65
  setFocusProxy(m_manager);
66

67
68
  // Create menus and actions
  initMenus();
69
  readSettings();
70

71
  setWindowIcon(QIcon(":/MantidPlot_Icon_32offset.png"));
72
  setWindowTitle("MantidPlot: " + env->languageName() + " Window");
73
74
75

#ifdef Q_OS_MAC
  // Work around to ensure that floating windows remain on top of the main
76
77
  // application window, but below other applications on Mac.
  // Note: Qt::Tool cannot have both a max and min button on OSX
78
  flags |= Qt::Tool;
79
80
81
82
  flags |= Qt::Dialog;
  flags |= Qt::CustomizeWindowHint;
  flags |= Qt::WindowMinimizeButtonHint;
  flags |= Qt::WindowCloseButtonHint;
83
84
  setWindowFlags(flags);
#endif
85
86
87
88
89
}

/**
 * Destructor
 */
90
ScriptingWindow::~ScriptingWindow() { delete m_manager; }
91
92

/**
93
 * Is a script executing
94
95
 * @returns A flag indicating the current state
 */
96
bool ScriptingWindow::isExecuting() const { return m_manager->isExecuting(); }
97
98
99
100

/**
 * Save the settings on the window
 */
101
void ScriptingWindow::saveSettings() {
102
103
  QSettings settings;
  settings.beginGroup("/ScriptWindow");
104
105
  settings.setValue("/AlwaysOnTop", m_alwaysOnTop->isChecked());
  settings.setValue("/ProgressArrow", m_toggleProgress->isChecked());
106
  settings.setValue("/LastDirectoryVisited", m_manager->m_last_dir);
107
108
  settings.setValue("/RecentScripts", m_manager->recentScripts());
  settings.setValue("/ZoomLevel", m_manager->globalZoomLevel());
109
110
111
112
  settings.setValue("/ShowWhitespace", m_toggleWhitespace->isChecked());
  settings.setValue("/ReplaceTabs", m_manager->m_replaceTabs);
  settings.setValue("/TabWhitespaceCount", m_manager->m_tabWhitespaceCount);
  settings.setValue("/ScriptFontFamily", m_manager->m_fontFamily);
113
  settings.setValue("/CodeFolding", m_toggleFolding->isChecked());
114
  settings.setValue("/LineWrapping", m_toggleWrapping->isChecked());
115
  settings.setValue("/PreviousFiles", m_manager->fileNamesToQStringList());
116
  settings.endGroup();
117
}
118

119
120
121
/**
 * Read the settings on the window
 */
122
void ScriptingWindow::readSettings() {
123
124
125
  QSettings settings;
  settings.beginGroup("/ScriptWindow");
  QString lastdir = settings.value("LastDirectoryVisited", "").toString();
126
127
128
129
130
131
  // If nothing, set the last directory to the Mantid scripts directory (if
  // present)
  if (lastdir.isEmpty()) {
    lastdir = QString::fromStdString(
        Mantid::Kernel::ConfigService::Instance().getString(
            "pythonscripts.directory"));
132
133
134
135
  }
  m_manager->m_last_dir = lastdir;
  m_toggleProgress->setChecked(settings.value("ProgressArrow", true).toBool());
  m_manager->setRecentScripts(settings.value("/RecentScripts").toStringList());
136
  m_manager->m_globalZoomLevel = settings.value("ZoomLevel", 0).toInt();
137
  m_toggleFolding->setChecked(settings.value("CodeFolding", false).toBool());
138
  m_toggleWrapping->setChecked(settings.value("LineWrapping", false).toBool());
139
140
  m_toggleWhitespace->setChecked(
      settings.value("ShowWhitespace", false).toBool());
141
142

  m_manager->m_showWhitespace = m_toggleWhitespace->isChecked();
143
144
145
  m_manager->m_replaceTabs = settings.value("ReplaceTabs", true).toBool();
  m_manager->m_tabWhitespaceCount =
      settings.value("TabWhitespaceCount", 4).toInt();
146
  m_manager->m_fontFamily = settings.value("ScriptFontFamily", "").toString();
147
  openPreviousTabs(settings.value("/PreviousFiles", "").toStringList());
148

149
150
151
  settings.endGroup();
}

152
153
154
155
/**
 * Override the closeEvent
 * @param event :: A pointer to the event object
 */
156
void ScriptingWindow::closeEvent(QCloseEvent *event) {
157
  // We ideally don't want a close button but are force by some window managers.
158
159
160
  // Therefore if someone clicks close and MantidPlot is not quitting then we
  // will just hide
  if (!m_acceptClose) {
161
    emit hideMe();
162
    // this->hide();
163
164
165
    return;
  }

166
167
  emit closeMe();
  // This will ensure each is saved correctly
168
  m_manager->closeAllTabs();
169
170
171
172
173
174
175
  event->accept();
}

/**
 * Override the showEvent function
 * @param event :: A pointer to the event object
 */
176
177
void ScriptingWindow::showEvent(QShowEvent *event) {
  if (m_manager->count() == 0) {
178
179
180
    m_manager->newTab();
  }
  event->accept();
181
182
183
}

/**
184
185
 * Open a script directly. This is here for backwards compatability with the old
 * ScriptWindow
186
 * class
187
188
 * @param filename :: The file name
 * @param newtab :: Do we want a new tab
189
 */
190
void ScriptingWindow::open(const QString &filename, bool newtab) {
191
  m_manager->open(newtab, filename);
192
193
}

194
195
196
197
198
/**
 * Executes whatever is in the current tab. Primarily useful for automatically
 * running a script loaded with open
 * @param mode :: The execution type
 * */
199
void ScriptingWindow::executeCurrentTab(const Script::ExecutionMode mode) {
200
  m_manager->executeAll(mode);
201
202
203
}

//-------------------------------------------
204
// Private slot member functions
205
//-------------------------------------------
206
/// Populate file menu
207
void ScriptingWindow::populateFileMenu() {
208
209
210
211
212
213
  m_fileMenu->clear();
  const bool scriptsOpen(m_manager->count() > 0);

  m_fileMenu->addAction(m_newTab);
  m_fileMenu->addAction(m_openInNewTab);

214
  if (scriptsOpen) {
215
    m_fileMenu->addAction(m_openInCurTab);
216
    m_fileMenu->addSeparator();
217
218
219
220
221
    m_fileMenu->addAction(m_save);
    m_fileMenu->addAction(m_saveAs);
    m_fileMenu->addAction(m_print);
  }

222
  m_fileMenu->addSeparator();
223
224
225
  m_fileMenu->addMenu(m_recentScripts);
  m_recentScripts->setEnabled(m_manager->recentScripts().count() > 0);

226
  if (scriptsOpen) {
227
    m_fileMenu->addSeparator();
228
229
230
231
232
    m_fileMenu->addAction(m_closeTab);
  }
}

/// Ensure the list is up to date
233
void ScriptingWindow::populateRecentScriptsMenu() {
234
235
236
  m_recentScripts->clear();
  QStringList recentScripts = m_manager->recentScripts();
  QStringListIterator iter(recentScripts);
237
  while (iter.hasNext()) {
238
239
240
241
242
    m_recentScripts->addAction(iter.next());
  }
}

/// Populate edit menu
243
void ScriptingWindow::populateEditMenu() {
244
245
246
247
248
249
250
  m_editMenu->clear();
  m_editMenu->addAction(m_undo);
  m_editMenu->addAction(m_redo);
  m_editMenu->addAction(m_cut);
  m_editMenu->addAction(m_copy);
  m_editMenu->addAction(m_paste);

251
  m_editMenu->addSeparator();
252
253
254
  m_editMenu->addAction(m_comment);
  m_editMenu->addAction(m_uncomment);

255
  m_editMenu->addSeparator();
256
257
258
  m_editMenu->addAction(m_tabsToSpaces);
  m_editMenu->addAction(m_spacesToTabs);

259
  m_editMenu->addSeparator();
260
261
  m_editMenu->addAction(m_find);
}
262

263
/// Populate execute menu
264
void ScriptingWindow::populateExecMenu() {
265
266
267
268
  m_runMenu->clear();
  m_runMenu->addAction(m_execSelect);
  m_runMenu->addAction(m_execAll);
  m_runMenu->addSeparator();
269
270
  m_runMenu->addAction(m_abortCurrent);
  m_runMenu->addSeparator();
271
272
273
  m_runMenu->addAction(m_clearScriptVars);
  m_runMenu->addSeparator();

274
275
276
277
278
279
280
  m_execModeMenu->clear();
  m_execModeMenu->addAction(m_execParallel);
  m_execModeMenu->addAction(m_execSerial);
  m_runMenu->addMenu(m_execModeMenu);
}

/// Populate window menu
281
void ScriptingWindow::populateWindowMenu() {
282
283
284
285
286
287
  m_windowMenu->clear();
  const bool scriptsOpen(m_manager->count() > 0);

  m_windowMenu->addAction(m_alwaysOnTop);
  m_windowMenu->addAction(m_hide);

288
  if (scriptsOpen) {
289
    m_windowMenu->addSeparator();
290
291
    m_windowMenu->addAction(m_zoomIn);
    m_windowMenu->addAction(m_zoomOut);
292
    m_windowMenu->addAction(m_resetZoom);
293
    m_windowMenu->addAction(m_selectFont);
294

295
    m_windowMenu->addSeparator();
296
297
    m_windowMenu->addAction(m_toggleProgress);
    m_windowMenu->addAction(m_toggleFolding);
298
    m_windowMenu->addAction(m_toggleWrapping);
299
    m_windowMenu->addAction(m_toggleWhitespace);
300

301
    m_windowMenu->addSeparator();
302
    m_windowMenu->addAction(m_openConfigTabs);
303
304
305
  }
}

306
/// Populate help menu
307
void ScriptingWindow::populateHelpMenu() {
308
309
310
311
312
  m_helpMenu->clear();
  m_helpMenu->addAction(m_showHelp);
  m_helpMenu->addAction(m_showPythonHelp);
}

313
314
/**
 */
315
void ScriptingWindow::updateWindowFlags() {
316
  Qt::WindowFlags flags = Qt::Window;
317
  if (m_alwaysOnTop->isChecked()) {
318
319
    flags |= Qt::WindowStaysOnTopHint;
  }
320
321
#ifdef Q_OS_MAC
  // Work around to ensure that floating windows remain on top of the main
322
323
  // application window, but below other applications on Mac.
  // Note: Qt::Tool cannot have both a max and min button on OSX
324
  flags |= Qt::Tool;
325
326
327
328
  flags |= Qt::CustomizeWindowHint;
  flags |= Qt::WindowMinimizeButtonHint;
  flags |= Qt::WindowCloseButtonHint;
  setWindowFlags(flags);
329
#endif
330
  setWindowFlags(flags);
331
332
333
  // This is necessary due to the setWindowFlags function reparenting the window
  // and causing is
  // to hide itself
334
335
336
  show();
}

337
338
339
340
341
/**
 *  Update menus based on current tab states. Called when
 *  the number of tabs changes
 *  @param ntabs :: The number of tabs now open
 */
342
void ScriptingWindow::setMenuStates(int ntabs) {
343
344
345
  const bool tabsOpen(ntabs > 0);
  m_editMenu->setEnabled(tabsOpen);
  m_runMenu->setEnabled(tabsOpen);
346
347
}

348
/**
349
 * Set the state of the execution actions depending on the flag
350
 * @param off :: If the true the items are disabled, otherwise the are enabled
351
 */
352
void ScriptingWindow::setEditActionsDisabled(bool off) {
353
354
  auto actions = m_editMenu->actions();
  foreach (QAction *action, actions) {
355
    if (strcmp("Find", action->objectName().toAscii().constData()) != 0) {
356
      action->setDisabled(off);
357
358
    }
  }
359
360
361
362
}

/**
 * Set the state of the execution actions/menu depending on the flag
363
 * @param off :: If the true the items are disabled, otherwise the are enabled
364
 */
365
366
367
368
369
370
371
372
373
374
375
376
377
378
void ScriptingWindow::setExecutionActionsDisabled(bool off) {
  m_execSelect->setDisabled(off);
  m_execAll->setDisabled(off);
  m_execModeMenu->setDisabled(off);
  m_clearScriptVars->setDisabled(off);
  // Abort should be opposite
  setAbortActionsDisabled(!off);
}

/**
 * Set the state of the execution actions/menu depending on the flag
 * @param off :: If the true the items are disabled else they are enabled
 */
void ScriptingWindow::setAbortActionsDisabled(bool off) {
379
380
  if (!shouldEnableAbort())
    off = true;
381
  m_abortCurrent->setDisabled(off);
382
}
383

384
385
386
387
/**
 * Maps the QAction to an index in the recent scripts list
 * @param item A pointer to the action that triggered the slot
 */
388
389
void ScriptingWindow::openRecentScript(QAction *item) {
  const QList<QAction *> actions = m_recentScripts->actions();
390
391
392
393
394
  const int index = actions.indexOf(item);
  assert(index >= 0);
  m_manager->openRecentScript(index);
}

395
396
397
/**
 * Ask the manager to execute all code based on the currently selected mode
 */
398
void ScriptingWindow::executeAll() {
399
400
401
402
  m_manager->executeAll(this->getExecutionMode());
}

/**
403
404
 * Ask the manager to execute the current selection based on the currently
 * selected mode
405
 */
406
void ScriptingWindow::executeSelection() {
407
408
409
  m_manager->executeSelection(this->getExecutionMode());
}

410
411
412
/**
 * Ask the manager to abort the script execution for the current script.
 */
413
void ScriptingWindow::abortCurrent() { m_manager->abortCurrentScript(); }
414

415
416
/**
 */
417
void ScriptingWindow::clearScriptVariables() {
418
419
420
  m_manager->clearScriptVariables();
}

421
/**
422
423
 * Opens the Qt help windows for the scripting window.
 */
424
425
426
void ScriptingWindow::showHelp() {
  MantidQt::API::HelpWindow::showCustomInterface(NULL,
                                                 QString("ScriptingWindow"));
427
428
429
430
431
}

/**
 * Opens the Qt help windows for the Python API.
 */
432
433
434
void ScriptingWindow::showPythonHelp() {
  MantidQt::API::HelpWindow::showPage(
      NULL, QString("qthelp://org.mantidproject/doc/api/python/index.html"));
435
436
437
}

/**
438
439
440
441
442
443
  * Calls MultiTabScriptInterpreter to save the currently opened
  * script file names to a string.
  *
  * @param app :: the current application window instance
  * @return script file names in the matid project format
  */
444
445
446
447
std::string ScriptingWindow::saveToProject(ApplicationWindow *app) {
  (void)app; // suppress unused variable warnings
  return m_manager->saveToString().toStdString();
}
448

449
450
451
452
453
454
455
456
457
458
459
/**
 * Load script files from the project file
 *
 * @param lines :: raw lines from the project file
 * @param app :: the current application window instance
 * @param fileVersion :: the file version used when saved
 */
void ScriptingWindow::loadFromProject(const std::string &lines,
                                      ApplicationWindow *app,
                                      const int fileVersion) {
  Q_UNUSED(fileVersion);
Samuel Jackson's avatar
Samuel Jackson committed
460

461
  MantidQt::API::TSVSerialiser sTSV(lines);
462
  QStringList files;
Samuel Jackson's avatar
Samuel Jackson committed
463

464
465
  setWindowTitle("MantidPlot: " + app->scriptingEnv()->languageName() +
                 " Window");
466

467
  auto scriptNames = sTSV.values("ScriptNames");
Samuel Jackson's avatar
Samuel Jackson committed
468

469
470
471
  // Iterate, ignoring scriptNames[0] which is just "ScriptNames"
  for (size_t i = 1; i < scriptNames.size(); ++i)
    files.append(QString::fromStdString(scriptNames[i]));
Samuel Jackson's avatar
Samuel Jackson committed
472

473
474
  loadFromFileList(files);
}
Samuel Jackson's avatar
Samuel Jackson committed
475

476
477
478
479
480
481
482
483
/**
 * Load script files from a list of file names
 * @param files :: List of file names to oepn
 */
void ScriptingWindow::loadFromFileList(const QStringList &files) {
  for (auto file = files.begin(); file != files.end(); ++file) {
    if (file->isEmpty())
      continue;
484
    openUnique(*file);
485
486
  }
}
487
488

/**
489
 * Saves scripts file names to a string
490
491
 * @param value If true a future close event will be accepted otherwise it will
 * be ignored
492
 */
493
void ScriptingWindow::acceptCloseEvent(const bool value) {
494
495
496
  m_acceptClose = value;
}

497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
//-------------------------------------------
// Protected non-slot member functions
//-------------------------------------------
/**
 * Accept a custom event and in this case test if it is a ScriptingChangeEvent
 * @param event :: The custom event
 */
void ScriptingWindow::customEvent(QEvent *event) {
  if (!m_manager->isExecuting() && event->type() == SCRIPTING_CHANGE_EVENT) {
    ScriptingChangeEvent *sce = static_cast<ScriptingChangeEvent *>(event);
    setWindowTitle("MantidPlot: " + sce->scriptingEnv()->languageName() +
                   " Window");
  }
}

/**
 * Accept a drag enter event and selects whether to accept the action
 * @param de :: The drag enter event
 */
void ScriptingWindow::dragEnterEvent(QDragEnterEvent *de) {
  const QMimeData *mimeData = de->mimeData();
  if (mimeData->hasUrls()) {
    if (extractPyFiles(mimeData->urls()).size() > 0) {
      de->acceptProposedAction();
    }
  }
}

/**
 * Accept a drag move event and selects whether to accept the action
 * @param de :: The drag move event
 */
void ScriptingWindow::dragMoveEvent(QDragMoveEvent *de) {
  const QMimeData *mimeData = de->mimeData();
  if (mimeData->hasUrls()) {
    if (extractPyFiles(mimeData->urls()).size() > 0) {
      de->accept();
    }
  }
}

/**
 * Accept a drag drop event and process the data appropriately
 * @param de :: The drag drop event
 */
void ScriptingWindow::dropEvent(QDropEvent *de) {
  const QMimeData *mimeData = de->mimeData();
  if (mimeData->hasUrls()) {
    QStringList filenames = extractPyFiles(mimeData->urls());
    de->acceptProposedAction();

    for (int i = 0; i < filenames.size(); ++i) {
      m_manager->openInNewTab(filenames[i]);
    }
  }
}

554
555
556
557
//-------------------------------------------
// Private non-slot member functions
//-------------------------------------------

558
559
/**
 * Initialise the menus
560
 */
561
void ScriptingWindow::initMenus() {
562
563
564
  initActions();

  m_fileMenu = menuBar()->addMenu(tr("&File"));
565
566
#ifdef SCRIPTING_DIALOG
  m_scripting_lang = new QAction(tr("Scripting &language"), this);
567
568
  connect(m_scripting_lang, SIGNAL(triggered()), this,
          SIGNAL(chooseScriptingLanguage()));
569
#endif
570
571
572
  connect(m_fileMenu, SIGNAL(aboutToShow()), this, SLOT(populateFileMenu()));

  m_editMenu = menuBar()->addMenu(tr("&Edit"));
573
  connect(m_editMenu, SIGNAL(aboutToShow()), this, SLOT(populateEditMenu()));
574
575
  connect(m_manager, SIGNAL(executionStateChanged(bool)), this,
          SLOT(setEditActionsDisabled(bool)));
576
577
578

  m_runMenu = menuBar()->addMenu(tr("E&xecute"));
  connect(m_runMenu, SIGNAL(aboutToShow()), this, SLOT(populateExecMenu()));
579
580
  connect(m_manager, SIGNAL(executionStateChanged(bool)), this,
          SLOT(setExecutionActionsDisabled(bool)));
581
  m_execModeMenu = new QMenu("Mode", this);
582
583

  m_windowMenu = menuBar()->addMenu(tr("&Window"));
584
585
  connect(m_windowMenu, SIGNAL(aboutToShow()), this,
          SLOT(populateWindowMenu()));
586

587
588
589
  m_helpMenu = menuBar()->addMenu(tr("&Help"));
  connect(m_windowMenu, SIGNAL(aboutToShow()), this, SLOT(populateHelpMenu()));

590
591
  connect(m_manager, SIGNAL(tabCountChanged(int)), this,
          SLOT(setMenuStates(int)));
592

593
  // The menu items must be populated for the shortcuts to work
594
595
596
597
  populateFileMenu();
  populateEditMenu();
  populateExecMenu();
  populateWindowMenu();
598
  populateHelpMenu();
599
600
601
602
603
604
605
606
607
608
  connect(m_manager, SIGNAL(tabCountChanged(int)), this,
          SLOT(populateFileMenu()));
  connect(m_manager, SIGNAL(tabCountChanged(int)), this,
          SLOT(populateEditMenu()));
  connect(m_manager, SIGNAL(tabCountChanged(int)), this,
          SLOT(populateExecMenu()));
  connect(m_manager, SIGNAL(tabCountChanged(int)), this,
          SLOT(populateWindowMenu()));
  connect(m_manager, SIGNAL(tabCountChanged(int)), this,
          SLOT(populateHelpMenu()));
609
}
610

611
/**
612
 *  Create all actions
613
 */
614
void ScriptingWindow::initActions() {
615
616
617
618
  initFileMenuActions();
  initEditMenuActions();
  initExecMenuActions();
  initWindowMenuActions();
619
  initHelpMenuActions();
620
}
621

622
623
624
/**
 * Create the file actions
 */
625
void ScriptingWindow::initFileMenuActions() {
626
  m_newTab = new QAction(tr("&New Tab"), this);
627
  connect(m_newTab, SIGNAL(triggered()), m_manager, SLOT(newTab()));
628
629
630
  m_newTab->setShortcut(tr("Ctrl+N"));

  m_openInCurTab = new QAction(tr("&Open"), this);
631
632
  connect(m_openInCurTab, SIGNAL(triggered()), m_manager,
          SLOT(openInCurrentTab()));
633
634
635
  m_openInCurTab->setShortcut(tr("Ctrl+O"));

  m_openInNewTab = new QAction(tr("&Open in New Tab"), this);
636
  connect(m_openInNewTab, SIGNAL(triggered()), m_manager, SLOT(openInNewTab()));
637
638
639
  m_openInNewTab->setShortcut(tr("Ctrl+Shift+O"));

  m_save = new QAction(tr("&Save"), this);
640
  connect(m_save, SIGNAL(triggered()), m_manager, SLOT(saveToCurrentFile()));
641
642
643
  m_save->setShortcut(QKeySequence::Save);

  m_saveAs = new QAction(tr("&Save As"), this);
644
  connect(m_saveAs, SIGNAL(triggered()), m_manager, SLOT(saveAs()));
645
  m_saveAs->setShortcut(tr("Ctrl+Shift+S"));
646
647

  m_print = new QAction(tr("&Print script"), this);
648
  connect(m_print, SIGNAL(triggered()), m_manager, SLOT(print()));
649
650
651
  m_print->setShortcut(QKeySequence::Print);

  m_closeTab = new QAction(tr("&Close Tab"), this);
652
  connect(m_closeTab, SIGNAL(triggered()), m_manager, SLOT(closeCurrentTab()));
653
  m_closeTab->setShortcut(tr("Ctrl+W"));
654

655
656
657
658
659
  m_recentScripts = new QMenu(tr("&Recent Scripts"), this);
  connect(m_recentScripts, SIGNAL(aboutToShow()), this,
          SLOT(populateRecentScriptsMenu()));
  connect(m_recentScripts, SIGNAL(triggered(QAction *)), this,
          SLOT(openRecentScript(QAction *)));
660
}
661

662
/**
663
 * Create the edit menu action*/
664
void ScriptingWindow::initEditMenuActions() {
665
  m_undo = new QAction(tr("&Undo"), this);
666
  connect(m_undo, SIGNAL(triggered()), m_manager, SLOT(undo()));
667
668
  connect(m_manager, SIGNAL(undoAvailable(bool)), m_undo,
          SLOT(setEnabled(bool)));
669
670
671
  m_undo->setShortcut(QKeySequence::Undo);

  m_redo = new QAction(tr("&Redo"), this);
672
  connect(m_redo, SIGNAL(triggered()), m_manager, SLOT(redo()));
673
674
  connect(m_manager, SIGNAL(redoAvailable(bool)), m_redo,
          SLOT(setEnabled(bool)));
675
676
677
  m_redo->setShortcut(QKeySequence::Redo);

  m_cut = new QAction(tr("C&ut"), this);
678
  connect(m_cut, SIGNAL(triggered()), m_manager, SLOT(cut()));
679
680
681
  m_cut->setShortcut(QKeySequence::Cut);

  m_copy = new QAction(tr("&Copy"), this);
682
  connect(m_copy, SIGNAL(triggered()), m_manager, SLOT(copy()));
683
684
685
  m_copy->setShortcut(QKeySequence::Copy);

  m_paste = new QAction(tr("&Paste"), this);
686
  connect(m_paste, SIGNAL(triggered()), m_manager, SLOT(paste()));
687
  m_paste->setShortcut(QKeySequence::Paste);
688

689
690
691
692
693
694
695
  m_comment = new QAction(tr("Co&mment"), this);
  connect(m_comment, SIGNAL(triggered()), m_manager, SLOT(comment()));
  m_comment->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_M));

  m_uncomment = new QAction(tr("Uncomment"), this);
  connect(m_uncomment, SIGNAL(triggered()), m_manager, SLOT(uncomment()));
  m_uncomment->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_M));
696

697
698
699
700
701
  m_tabsToSpaces = new QAction(tr("Tabs to Spaces"), this);
  connect(m_tabsToSpaces, SIGNAL(triggered()), m_manager, SLOT(tabsToSpaces()));

  m_spacesToTabs = new QAction(tr("Spaces to Tabs"), this);
  connect(m_spacesToTabs, SIGNAL(triggered()), m_manager, SLOT(spacesToTabs()));
702
703

  m_find = new QAction(tr("&Find/Replace"), this);
704
  connect(m_find, SIGNAL(triggered()), m_manager,
705
          SLOT(showFindReplaceDialog()));
706
707
708
709
710
  m_find->setShortcut(QKeySequence::Find);
}

/**
 * Create the execute menu actions
711
 */
712
void ScriptingWindow::initExecMenuActions() {
713
  m_execSelect = new QAction(tr("E&xecute Selection"), this);
714
  connect(m_execSelect, SIGNAL(triggered()), this, SLOT(executeSelection()));
715

716
717
718
719
720
  QList<QKeySequence> shortcuts;
  shortcuts << Qt::CTRL + Qt::Key_Return << Qt::CTRL + Qt::Key_Enter;
  m_execSelect->setShortcuts(shortcuts);

  m_execAll = new QAction(tr("Execute &All"), this);
721
  connect(m_execAll, SIGNAL(triggered()), this, SLOT(executeAll()));
722
  shortcuts.clear();
723
724
  shortcuts << Qt::CTRL + Qt::SHIFT + Qt::Key_Return
            << Qt::CTRL + Qt::SHIFT + Qt::Key_Enter;
725
  m_execAll->setShortcuts(shortcuts);
726

727
728
  m_abortCurrent = new QAction(tr("A&bort"), this);
  connect(m_abortCurrent, SIGNAL(triggered()), this, SLOT(abortCurrent()));
729
730
731
  shortcuts.clear();
  shortcuts << Qt::CTRL + Qt::Key_D;
  m_abortCurrent->setShortcuts(shortcuts);
732
  setAbortActionsDisabled(false);
733

734
  m_clearScriptVars = new QAction(tr("&Clear Variables"), this);
735
736
737
738
  connect(m_clearScriptVars, SIGNAL(triggered()), this,
          SLOT(clearScriptVariables()));
  m_clearScriptVars->setToolTip(
      "Clear all variable definitions in this script");
739

740
741
742
743
744
745
746
747
748
  m_execParallel = new QAction("Asynchronous", this);
  m_execParallel->setCheckable(true);
  m_execSerial = new QAction("Serialised", this);
  m_execSerial->setCheckable(true);

  m_execModeGroup = new QActionGroup(this);
  m_execModeGroup->addAction(m_execParallel);
  m_execModeGroup->addAction(m_execSerial);
  m_execParallel->setChecked(true);
749
}
750
751

/**
752
 * Create the window menu actions
753
 */
754
void ScriptingWindow::initWindowMenuActions() {
755
756
  m_alwaysOnTop = new QAction(tr("Always on &Top"), this);
  m_alwaysOnTop->setCheckable(true);
757
758
  connect(m_alwaysOnTop, SIGNAL(toggled(bool)), this,
          SLOT(updateWindowFlags()));
759
760
761
762
763
764
765

  m_hide = new QAction(tr("&Hide"), this);
#ifdef __APPLE__
  m_hide->setShortcut(tr("Ctrl+3")); // F3 is used by the window manager on Mac
#else
  m_hide->setShortcut(tr("F3"));
#endif
766
767
  // Note that we channel the hide through the parent so that we can save the
  // geometry state
768
  connect(m_hide, SIGNAL(triggered()), this, SIGNAL(hideMe()));
769

770
  m_zoomIn = new QAction(("&Increase font size"), this);
771
772
773
774
  // Setting two shortcuts makes it work for both the plus on the keypad and one
  // above an =
  // Despite the Qt docs advertising the use of QKeySequence::ZoomIn as the
  // solution to this,
775
  // it doesn't seem to work for me
776
777
  m_zoomIn->setShortcut(Qt::SHIFT + Qt::CTRL + Qt::Key_Equal);
  m_zoomIn->setShortcut(Qt::CTRL + Qt::Key_Plus);
778
  connect(m_zoomIn, SIGNAL(triggered()), m_manager, SLOT(zoomIn()));
779
  connect(m_zoomIn, SIGNAL(triggered()), m_manager, SLOT(trackZoomIn()));
780

781
  m_zoomOut = new QAction(("&Decrease font size"), this);
782
  m_zoomOut->setShortcut(QKeySequence::ZoomOut);
783
  connect(m_zoomOut, SIGNAL(triggered()), m_manager, SLOT(zoomOut()));
784
  connect(m_zoomOut, SIGNAL(triggered()), m_manager, SLOT(trackZoomOut()));
785

786
787
788
  m_resetZoom = new QAction(("&Reset font size"), this);
  connect(m_resetZoom, SIGNAL(triggered()), m_manager, SLOT(resetZoom()));

789
790
791
792
  // Show font selection dialog
  m_selectFont = new QAction(tr("Select Font"), this);
  connect(m_selectFont, SIGNAL(triggered()), m_manager, SLOT(showSelectFont()));

793
  // Toggle the progress arrow
794
  m_toggleProgress = new QAction(tr("&Progress Reporting"), this);
795
  m_toggleProgress->setCheckable(true);
796
797
  connect(m_toggleProgress, SIGNAL(toggled(bool)), m_manager,
          SLOT(toggleProgressReporting(bool)));
798
799
800
801

  // Toggle code folding
  m_toggleFolding = new QAction(tr("Code &Folding"), this);
  m_toggleFolding->setCheckable(true);
802
803
  connect(m_toggleFolding, SIGNAL(toggled(bool)), m_manager,
          SLOT(toggleCodeFolding(bool)));
804

805
806
807
808
809
  m_toggleWrapping = new QAction(tr("Line &Wrapping"), this);
  m_toggleWrapping->setCheckable(true);
  connect(m_toggleWrapping, SIGNAL(toggled(bool)), m_manager,
          SLOT(toggleLineWrapping(bool)));

810
811
  // Toggle the whitespace arrow
  m_toggleWhitespace = new QAction(tr("&Show Whitespace"), this);
812
  m_toggleWhitespace->setCheckable(true);
813
814
  connect(m_toggleWhitespace, SIGNAL(toggled(bool)), m_manager,
          SLOT(toggleWhitespace(bool)));
815
816
817

  // Open Config Tabs dialog
  m_openConfigTabs = new QAction(tr("Configure Tabs"), this);
818
819
  connect(m_openConfigTabs, SIGNAL(triggered()), m_manager,
          SLOT(openConfigTabs()));
820
}
821

822
823
824
/**
 * Create the help menu actions
 */
825
void ScriptingWindow::initHelpMenuActions() {
826
827
828
829
830
831
832
833
834
  // Show Qt help window
  m_showHelp = new QAction(tr("Scripting Window Help"), this);
  connect(m_showHelp, SIGNAL(triggered()), this, SLOT(showHelp()));

  // Show Qt help window for Python API
  m_showPythonHelp = new QAction(tr("Python API Help"), this);
  connect(m_showPythonHelp, SIGNAL(triggered()), this, SLOT(showPythonHelp()));
}

835
836
837
838
839
/// Should we enable abort functionality
bool ScriptingWindow::shouldEnableAbort() const {
  return m_manager->scriptingEnv()->supportsAbortRequests();
}

840
/**
841
842
843
 * Opens a script providing a copy is not already open. On exit the
 * active tab will be the one containing the given script.
 * @param filename The name of the newTab to open
844
 */
845
void ScriptingWindow::openUnique(QString filename) {
846
  auto openFiles = m_manager->fileNamesToQStringList();
847
848
  // The list of open files contains absolute paths so make sure we have one
  // here
849
  filename = QFileInfo(filename).absoluteFilePath();
850
851
852
853
854
855
  auto position = openFiles.indexOf(filename);
  if (position < 0) {
    m_manager->newTab(openFiles.size(), filename);
  } else {
    // make it the current tab
    m_manager->setCurrentIndex(position);
856
857
858
  }
}

859
/**
860
861
 * Opens a set of files in new tabs
 * @param tabsToOpen A list of filenames to open in new tabs
862
 */
863
864
void ScriptingWindow::openPreviousTabs(const QStringList &tabsToOpen) {
  const int totalFiles = tabsToOpen.size();
865
866
  QStringList files = QStringList();
  // Check files can be opened
867
868
869
  for (int i = 0; i < totalFiles; i++) {
    if (FILE *file = fopen(tabsToOpen[i].toStdString().c_str(), "r")) {
      fclose(file);
870
      files.append(tabsToOpen[i]);
871
872
    }
  }
873
874
875
876
877
878

  // Remove duplicates
  files.removeDuplicates();

  // opens files providing there are more than 0
  const int validTotal = files.size();
879
  if (validTotal == 0) {
880
881
    m_manager->newTab();
  } else {
882
    for (int i = 0; i < validTotal; i++) {
883
      m_manager->newTab(i, files[i]);
Nick Draper's avatar
Nick Draper committed
884
885
886
    }
  }
}
887

Nick Draper's avatar
Nick Draper committed
888
/**
889
 * Returns the current execution mode set in the menu
Nick Draper's avatar
Nick Draper committed
890
 */
891
892
893
894
895
Script::ExecutionMode ScriptingWindow::getExecutionMode() const {
  if (m_execParallel->isChecked())
    return Script::Asynchronous;
  else
    return Script::Serialised;
Nick Draper's avatar
Nick Draper committed
896
}
897

898
QStringList ScriptingWindow::extractPyFiles(const QList<QUrl> &urlList) const {
Nick Draper's avatar
Nick Draper committed
899
  QStringList filenames;
900
  for (int i = 0; i < urlList.size(); ++i) {
Nick Draper's avatar
Nick Draper committed
901
    QString fName = urlList[i].toLocalFile();
902
    if (fName.size() > 0) {
Nick Draper's avatar
Nick Draper committed
903
      QFileInfo fi(fName);
904

905
      if (fi.suffix().toUpper() == "PY") {
Nick Draper's avatar
Nick Draper committed
906
907
908
909
910
        filenames.append(fName);
      }
    }
  }
  return filenames;
911
}