1 /*******************************************************************
2
3 Part of the Fritzing project - http://fritzing.org
4 Copyright (c) 2007-2015 Fachhochschule Potsdam - http://fh-potsdam.de
5
6 Fritzing 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 3 of the License, or
9 (at your option) any later version.
10
11 Fritzing 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.
15
16 You should have received a copy of the GNU General Public License
17 along with Fritzing. If not, see <http://www.gnu.org/licenses/>.
18
19 ********************************************************************
20
21 $Revision: 6904 $:
22 $Author: irascibl@gmail.com $:
23 $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $
24
25 ********************************************************************/
26
27 /////////////////////////////////////////////
28 //
29 // TODO
30 //
31 // integrate dirty
32 // remove old program window
33 // enable all buttons, and give error messages (i.e. where is IDE)
34
35
36 #include "programwindow.h"
37 #include "highlighter.h"
38 #include "syntaxer.h"
39 #include "programtab.h"
40 #include "platformarduino.h"
41 #include "platformpicaxe.h"
42
43 #include "../debugdialog.h"
44 #include "../waitpushundostack.h"
45 #include "../utils/folderutils.h"
46
47 #include <QFileInfoList>
48 #include <QFileInfo>
49 #include <QRegExp>
50 #include <QSettings>
51 #include <QFontMetrics>
52 #include <QTextStream>
53 #include <QLayout>
54 #include <QMenu>
55 #include <QMenuBar>
56 #include <QApplication>
57 #include <QKeyEvent>
58 #include <QCloseEvent>
59 #include <QPrinter>
60 #include <QPrintDialog>
61 #include <QtSerialPort/qserialportinfo.h>
62 #include <QtSerialPort/qserialport.h>
63
64 ///////////////////////////////////////////////
65
PTabWidget(QWidget * parent)66 PTabWidget::PTabWidget(QWidget * parent) : QTabWidget(parent) {
67 m_lastTabIndex = -1;
68
69 connect(tabBar(), SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int)));
70 }
71
tabBar()72 QTabBar * PTabWidget::tabBar() {
73 return QTabWidget::tabBar();
74 }
75
tabChanged(int index)76 void PTabWidget::tabChanged(int index) {
77 // Hide the close button on the old tab
78 if (m_lastTabIndex >= 0) {
79 QAbstractButton *tabButton = qobject_cast<QAbstractButton *>(tabBar()->tabButton(m_lastTabIndex, QTabBar::LeftSide));
80 if (!tabButton) {
81 tabButton = qobject_cast<QAbstractButton *>(tabBar()->tabButton(m_lastTabIndex, QTabBar::RightSide));
82 }
83
84 if (tabButton) {
85 tabButton->hide();
86 }
87 }
88
89 m_lastTabIndex = index;
90
91 // Show the close button on the new tab
92 if (m_lastTabIndex >= 0) {
93 QAbstractButton *tabButton = qobject_cast<QAbstractButton *>(tabBar()->tabButton(m_lastTabIndex, QTabBar::LeftSide));
94 if (!tabButton) {
95 tabButton = qobject_cast<QAbstractButton *>(tabBar()->tabButton(m_lastTabIndex, QTabBar::RightSide));
96 }
97 if (tabButton) {
98 tabButton->show();
99 }
100 }
101 }
102
103 ///////////////////////////////////////////////
104
105 static int UntitledIndex = 1;
106 QList<Platform *> ProgramWindow::m_platforms;
107 QString ProgramWindow::NoBoardName;
108
ProgramWindow(QWidget * parent)109 ProgramWindow::ProgramWindow(QWidget *parent)
110 : FritzingWindow("", untitledFileCount(), "", parent)
111 {
112 QFile styleSheet(":/resources/styles/programwindow.qss");
113
114 this->setObjectName("programmingWindow");
115 if (!styleSheet.open(QIODevice::ReadOnly)) {
116 qWarning("Unable to open :/resources/styles/programwindow.qss");
117 } else {
118 QString ss = styleSheet.readAll();
119 #ifdef Q_OS_MAC
120 int paneLoc = 4;
121 int tabBarLoc = 0;
122 #else
123 int paneLoc = -1;
124 int tabBarLoc = 5;
125 #endif
126 ss = ss.arg(paneLoc).arg(tabBarLoc);
127 this->setStyleSheet(ss);
128 }
129
130 if (m_platforms.count() == 0) {
131 initPlatforms();
132 }
133
134 if (NoBoardName.isEmpty()) {
135 NoBoardName = tr("No boards available");
136 }
137
138 m_savingProgramTab = NULL;
139 UntitledIndex--; // incremented by FritzingWindow
140 ProgramWindow::setTitle(); // set to something weird by FritzingWindow
141 }
142
~ProgramWindow()143 ProgramWindow::~ProgramWindow()
144 {
145 }
146
setup()147 void ProgramWindow::setup()
148 {
149 if (parentWidget() == NULL) {
150 resize(500,700);
151 setAttribute(Qt::WA_DeleteOnClose, true);
152 }
153
154 QFrame * mainFrame = new QFrame(this);
155
156 QFrame * headerFrame = createHeader();
157 QFrame * centerFrame = createCenter();
158
159 layout()->setMargin(0);
160 layout()->setSpacing(0);
161
162 QGridLayout *layout = new QGridLayout(mainFrame);
163 layout->setMargin(0);
164 layout->setSpacing(0);
165 layout->addWidget(headerFrame,0,0);
166 layout->addWidget(centerFrame,1,0);
167
168 setCentralWidget(mainFrame);
169
170 setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
171
172 QSettings settings;
173 if (!settings.value("programwindow/state").isNull()) {
174 restoreState(settings.value("programwindow/state").toByteArray());
175 }
176 if (!settings.value("programwindow/geometry").isNull()) {
177 restoreGeometry(settings.value("programwindow/geometry").toByteArray());
178 }
179
180 installEventFilter(this);
181 }
182
initMenus(QMenuBar * menubar)183 void ProgramWindow::initMenus(QMenuBar * menubar) {
184 QAction *currentAction;
185
186 m_editMenu = menubar->addMenu(tr("&Edit"));
187
188 m_undoAction = new QAction(tr("Undo"), this);
189 m_undoAction->setShortcuts(QKeySequence::Undo);
190 m_undoAction->setEnabled(false);
191 connect(m_undoAction, SIGNAL(triggered()), this, SLOT(undo()));
192 m_editMenu->addAction(m_undoAction);
193
194 m_redoAction = new QAction(tr("Redo"), this);
195 m_redoAction->setShortcuts(QKeySequence::Redo);
196 m_redoAction->setEnabled(false);
197 connect(m_redoAction, SIGNAL(triggered()), this, SLOT(redo()));
198 m_editMenu->addAction(m_redoAction);
199
200 m_editMenu->addSeparator();
201
202 m_cutAction = new QAction(tr("&Cut"), this);
203 m_cutAction->setShortcut(QKeySequence::Cut);
204 m_cutAction->setStatusTip(tr("Cut selection"));
205 m_cutAction->setEnabled(false);
206 connect(m_cutAction, SIGNAL(triggered()), this, SLOT(cut()));
207 m_editMenu->addAction(m_cutAction);
208
209 m_copyAction = new QAction(tr("&Copy"), this);
210 m_copyAction->setShortcut(QKeySequence::Copy);
211 m_copyAction->setStatusTip(tr("Copy selection"));
212 m_copyAction->setEnabled(false);
213 connect(m_copyAction, SIGNAL(triggered()), this, SLOT(copy()));
214 m_editMenu->addAction(m_copyAction);
215
216 m_pasteAction = new QAction(tr("&Paste"), this);
217 m_pasteAction->setShortcut(QKeySequence::Paste);
218 m_pasteAction->setStatusTip(tr("Paste clipboard contents"));
219 // TODO: Check clipboard status and disable appropriately here
220 connect(m_pasteAction, SIGNAL(triggered()), this, SLOT(paste()));
221 m_editMenu->addAction(m_pasteAction);
222
223 m_editMenu->addSeparator();
224
225 m_selectAction = new QAction(tr("&Select All"), this);
226 m_selectAction->setShortcut(QKeySequence::SelectAll);
227 m_selectAction->setStatusTip(tr("Select all text"));
228 connect(m_selectAction, SIGNAL(triggered()), this, SLOT(selectAll()));
229 m_editMenu->addAction(m_selectAction);
230
231 m_editMenu->addSeparator();
232
233 m_preferencesAction = new QAction(tr("&Preferences..."), this);
234 m_preferencesAction->setStatusTip(tr("Show the application's about box"));
235 connect(m_preferencesAction, SIGNAL(triggered()), QApplication::instance(), SLOT(preferences()));
236 m_editMenu->addAction(m_preferencesAction);
237
238 m_programMenu = menubar->addMenu(tr("&Code"));
239
240 m_newAction = new QAction(tr("&New Tab"), this);
241 m_newAction->setShortcut(QKeySequence::AddTab);
242 m_newAction->setStatusTip(tr("Create a new program tab"));
243 connect(m_newAction, SIGNAL(triggered()), this, SLOT(addTab()));
244 m_programMenu->addAction(m_newAction);
245
246 m_openAction = new QAction(tr("&Import Code..."), this);
247 m_openAction->setShortcut(tr("Alt+Ctrl+I"));
248 m_openAction->setStatusTip(tr("Import a program from a file"));
249 connect(m_openAction, SIGNAL(triggered()), this, SLOT(loadProgramFile()));
250 m_programMenu->addAction(m_openAction);
251
252 m_saveAction = new QAction(tr("&Save Tab"), this);
253 m_saveAction->setShortcut(tr("Alt+Ctrl+S"));
254 m_saveAction->setStatusTip(tr("Save the current program tab"));
255 connect(m_saveAction, SIGNAL(triggered()), this, SLOT(saveCurrentTab()));
256 m_programMenu->addAction(m_saveAction);
257
258 currentAction = new QAction(tr("&Rename Tab"), this);
259 currentAction->setShortcut(tr("Alt+Ctrl+R"));
260 currentAction->setStatusTip(tr("Rename the current program tab"));
261 connect(currentAction, SIGNAL(triggered()), this, SLOT(rename()));
262 m_programMenu->addAction(currentAction);
263
264 currentAction = new QAction(tr("Close Tab"), this);
265 currentAction->setShortcut(tr("Alt+Ctrl+W"));
266 currentAction->setStatusTip(tr("Remove the current program tab from the sketch"));
267 connect(currentAction, SIGNAL(triggered()), this, SLOT(closeCurrentTab()));
268 m_programMenu->addAction(currentAction);
269
270 m_programMenu->addSeparator();
271
272 m_platformMenu = new QMenu(tr("Platform"), this);
273 m_programMenu->addMenu(m_platformMenu);
274 QSettings settings;
275 QString currentPlatform = settings.value("programwindow/platform", "").toString();
276 QList<Platform *> platforms = getAvailablePlatforms();
277 m_platformActionGroup = new QActionGroup(this);
278 foreach (Platform * platform, platforms) {
279 currentAction = new QAction(platform->getName(), this);
280 currentAction->setCheckable(true);
281 m_platformActions.insert(platform, currentAction);
282 m_platformActionGroup->addAction(currentAction);
283 m_platformMenu->addAction(currentAction);
284 if (!currentPlatform.isEmpty()) {
285 if (platform->getName().compare(currentPlatform) == 0) {
286 currentAction->setChecked(true);
287 }
288 }
289 }
290 connect(m_platformMenu, SIGNAL(triggered(QAction*)), this, SLOT(setPlatform(QAction*)));
291
292 m_boardMenu = new QMenu(tr("Board"), this);
293 m_programMenu->addMenu(m_boardMenu);
294 m_boardActionGroup = new QActionGroup(this);
295 updateBoards();
296 connect(m_boardMenu, SIGNAL(triggered(QAction*)), this, SLOT(setBoard(QAction*)));
297
298 m_serialPortMenu = new QMenu(tr("Port"), this);
299 m_programMenu->addMenu(m_serialPortMenu);
300 m_serialPortActionGroup = new QActionGroup(this);
301 updateSerialPorts();
302 connect(m_serialPortMenu, SIGNAL(triggered(QAction*)), this, SLOT(setPort(QAction*)));
303 connect(m_serialPortMenu, SIGNAL(aboutToShow()), this, SLOT(updateSerialPorts()), Qt::DirectConnection);
304
305 m_programMenu->addSeparator();
306
307 m_monitorAction = new QAction(tr("Serial Monitor"), this);
308 m_monitorAction->setShortcut(tr("Ctrl+M"));
309 m_monitorAction->setStatusTip(tr("Monitor the serial port communication"));
310 m_monitorAction->setEnabled(false);
311 connect(m_monitorAction, SIGNAL(triggered()), this, SLOT(serialMonitor()));
312 m_programMenu->addAction(m_monitorAction);
313
314 m_programAction = new QAction(tr("Upload"), this);
315 m_programAction->setShortcut(tr("Ctrl+U"));
316 m_programAction->setStatusTip(tr("Upload the current program onto a microcontroller"));
317 m_programAction->setEnabled(false);
318 connect(m_programAction, SIGNAL(triggered()), this, SLOT(sendProgram()));
319 m_programMenu->addAction(m_programAction);
320
321 m_viewMenu = menubar->addMenu(tr("&View"));
322 foreach (QAction * action, m_viewMenuActions) {
323 m_viewMenu->addAction(action);
324 }
325
326 addTab(); // the initial ProgramTab must be created after all actions are set up
327 }
328
showMenus(bool show)329 void ProgramWindow::showMenus(bool show) {
330 if (m_editMenu) {
331 m_editMenu->menuAction()->setVisible(show);
332 m_editMenu->setEnabled(show);
333 m_undoAction->setEnabled(show);
334 m_redoAction->setEnabled(show);
335 m_cutAction->setEnabled(show);
336 m_copyAction->setEnabled(show);
337 m_pasteAction->setEnabled(show);
338 m_selectAction->setEnabled(show);
339 }
340 if (m_programMenu) {
341 m_programMenu->menuAction()->setVisible(show);
342 m_programMenu->setEnabled(show);
343 }
344 if (m_viewMenu) {
345 m_viewMenu->menuAction()->setVisible(show);
346 m_viewMenu->setEnabled(show);
347 }
348 }
349
createViewMenuActions(QList<QAction * > & actions)350 void ProgramWindow::createViewMenuActions(QList<QAction *> & actions) {
351 m_viewMenuActions = actions;
352 }
353
linkFiles(const QList<LinkedFile * > & linkedFiles,const QString & alternativePath)354 void ProgramWindow::linkFiles(const QList<LinkedFile *> & linkedFiles, const QString & alternativePath) {
355 if (linkedFiles.isEmpty()) return;
356
357 bool firstTime = true;
358 foreach (LinkedFile * linkedFile, linkedFiles) {
359 ProgramTab * programTab = NULL;
360 if (firstTime) {
361 firstTime = false;
362 programTab = indexWidget(0);
363 }
364 else {
365 programTab = addTab();
366 }
367 QDir dir(alternativePath);
368 QFileInfo fileInfo(linkedFile->linkedFilename);
369 programTab->loadProgramFile(linkedFile->linkedFilename, dir.absoluteFilePath(fileInfo.fileName()), false);
370 if ((linkedFile->fileFlags & LinkedFile::InBundleFlag) && ((linkedFile->fileFlags & LinkedFile::ReadOnlyFlag) == 0)) {
371 if (linkedFile->fileFlags & LinkedFile::SameMachineFlag) {
372 programTab->appendToConsole(tr("File '%1' was restored from the .fzz file; the local copy was not found.").arg(fileInfo.fileName()));
373 }
374 else {
375 programTab->appendToConsole(tr("File '%1' was restored from the .fzz file; save a local copy to work with an external editor.").arg(fileInfo.fileName()));
376 }
377 }
378 if (hasPlatform(linkedFile->platform)) {
379 programTab->setPlatform(linkedFile->platform, false);
380 }
381 else {
382 linkedFile->platform.clear();
383 }
384 }
385 }
386
createHeader()387 QFrame * ProgramWindow::createHeader() {
388 QFrame * headerFrame = new QFrame();
389 headerFrame->setSizePolicy(QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed));
390 headerFrame->setObjectName("header");
391
392 return headerFrame;
393 }
394
createCenter()395 QFrame * ProgramWindow::createCenter() {
396
397 QFrame * centerFrame = new QFrame(this);
398 centerFrame->setObjectName("center");
399
400 m_tabWidget = new PTabWidget(centerFrame);
401 m_tabWidget->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
402 m_tabWidget->setMovable(true);
403 m_tabWidget->setTabsClosable(true);
404 m_tabWidget->setUsesScrollButtons(false);
405 m_tabWidget->setElideMode(Qt::ElideLeft);
406 connect(m_tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int)));
407
408 //addTab();
409
410 QGridLayout *tabLayout = new QGridLayout(m_tabWidget);
411 tabLayout->setMargin(0);
412 tabLayout->setSpacing(0);
413
414 QGridLayout *mainLayout = new QGridLayout(centerFrame);
415 mainLayout->setMargin(0);
416 mainLayout->setSpacing(0);
417 mainLayout->addWidget(m_tabWidget,0,0,1,1);
418
419 return centerFrame;
420 }
421
cleanUp()422 void ProgramWindow::cleanUp() {
423 }
424
425 /**
426 * eventFilter here is used to catch the keyboard shortcuts that trigger the close event
427 * for ProgramWindow. If there are more than one tab widget the standard close shortcut
428 * should only close the current tab instead of closing the whole window. Every other
429 * case is ignored and handled by closeEvent() like normal.
430 */
eventFilter(QObject * object,QEvent * event)431 bool ProgramWindow::eventFilter(QObject * object, QEvent * event) {
432 if (object == this && event->type() == QEvent::ShortcutOverride) {
433 QKeyEvent *keyEvent = dynamic_cast<QKeyEvent*>(event);
434 if(keyEvent && keyEvent->matches(QKeySequence::Close) && m_tabWidget->count() > 1 ) {
435 return true;
436 }
437 }
438 return QMainWindow::eventFilter(object, event);
439 }
440
441 /**
442 * Reimplement closeEvent to save any modified documents before closing.
443 */
closeEvent(QCloseEvent * event)444 void ProgramWindow::closeEvent(QCloseEvent *event) {
445 bool discard;
446 if(beforeClosing(true, discard)) {
447 cleanUp();
448 QMainWindow::closeEvent(event);
449 emit closed();
450 } else {
451 event->ignore();
452 }
453
454 QSettings settings;
455 settings.setValue("programwindow/state",saveState());
456 settings.setValue("programwindow/geometry",saveGeometry());
457 }
458
untitledFileName()459 const QString ProgramWindow::untitledFileName() {
460 return "Untitled";
461 }
462
fileExtension()463 const QString ProgramWindow::fileExtension() {
464 return "";
465 }
466
defaultSaveFolder()467 const QString ProgramWindow::defaultSaveFolder() {
468 return FolderUtils::openSaveFolder();
469 }
470
event(QEvent * e)471 bool ProgramWindow::event(QEvent * e) {
472 switch (e->type()) {
473 case QEvent::WindowActivate:
474 emit changeActivationSignal(true, this);
475 break;
476 case QEvent::WindowDeactivate:
477 emit changeActivationSignal(false, this);
478 break;
479 default:
480 break;
481 }
482 return FritzingWindow::event(e);
483 }
484
untitledFileCount()485 int & ProgramWindow::untitledFileCount() {
486 return UntitledIndex;
487 }
488
setTitle()489 void ProgramWindow::setTitle() {
490 setWindowTitle(tr("Code Window"));
491 }
492
setTitle(const QString & filename)493 void ProgramWindow::setTitle(const QString & filename) {
494 setWindowTitle(tr("Code Window - %1").arg(filename));
495 }
496
497 /**
498 * Create and open a new tab within the PTabWidget child.
499 * This function handled connecting all the appropriate signals
500 * and setting an appropriate filename.
501 */
addTab()502 ProgramTab * ProgramWindow::addTab() {
503 QString name = (UntitledIndex == 1) ? untitledFileName() : tr("%1%2").arg(untitledFileName()).arg(UntitledIndex);
504 ProgramTab * programTab = new ProgramTab(name, m_tabWidget);
505 connect(programTab, SIGNAL(wantToSave(int)), this, SLOT(tabSave(int)));
506 connect(programTab, SIGNAL(wantToSaveAs(int)), this, SLOT(tabSaveAs(int)));
507 connect(programTab, SIGNAL(wantToRename(int)), this, SLOT(tabRename(int)));
508 connect(programTab, SIGNAL(wantToDelete(int, bool)), this, SLOT(tabDelete(int, bool)), Qt::DirectConnection);
509 connect(programTab,
510 SIGNAL(programWindowUpdateRequest(bool, bool, bool, bool, bool, bool, Platform *, const QString &, const QString &, const QString &)),
511 this,
512 SLOT(updateMenu(bool, bool, bool, bool, bool, bool, Platform *, const QString &, const QString &, const QString &)));
513 int ix = m_tabWidget->addTab(programTab, name);
514 m_tabWidget->setCurrentIndex(ix);
515 programTab->initMenus();
516 UntitledIndex++;
517
518 return programTab;
519 }
520
521 /**
522 * A function for closing the current displayed tab.
523 * I'm using a ProgramWindow method instead of calling deleteTab()
524 * directly as I believe it's more apropos.
525 */
closeCurrentTab()526 void ProgramWindow::closeCurrentTab() {
527 closeTab(m_tabWidget->currentIndex());
528 }
529
closeTab(int index)530 void ProgramWindow::closeTab(int index) {
531 ProgramTab * pTab = indexWidget(index);
532 if (pTab) {
533 emit linkToProgramFile(pTab->filename(), NULL, false, true);
534 pTab->deleteTab();
535 }
536 }
537
538 /**
539 * This slot is for updating the tab-dependent menu items.
540 * - program
541 * - undo/redo
542 * - cut/copy
543 */
updateMenu(bool programEnable,bool undoEnable,bool redoEnable,bool cutEnable,bool copyEnable,bool pasteEnable,Platform * platform,const QString & port,const QString & board,const QString & filename)544 void ProgramWindow::updateMenu(bool programEnable, bool undoEnable, bool redoEnable, bool cutEnable, bool copyEnable, bool pasteEnable,
545 Platform* platform, const QString & port, const QString & board, const QString & filename)
546 {
547 ProgramTab * programTab = currentWidget();
548 m_saveAction->setEnabled(programTab->isModified());
549 m_monitorAction->setEnabled(!port.isEmpty());
550 m_programAction->setEnabled(programEnable);
551 m_undoAction->setEnabled(undoEnable);
552 m_redoAction->setEnabled(redoEnable);
553 m_cutAction->setEnabled(cutEnable);
554 m_copyAction->setEnabled(copyEnable);
555 m_pasteAction->setEnabled(pasteEnable);
556 QAction *lang = m_platformActions.value(platform);
557 if (lang) {
558 lang->setChecked(true);
559 }
560 QAction *portAction = m_portActions.value(port);
561 if (portAction) {
562 portAction->setChecked(true);
563 }
564 QAction *boardAction = m_boardActions.value(board);
565 if (boardAction) {
566 boardAction->setChecked(true);
567 }
568
569 setTitle(filename);
570 }
571
setPlatform(QAction * action)572 void ProgramWindow::setPlatform(QAction* action) {
573 currentWidget()->setPlatform(action->text());
574 }
575
setPort(QAction * action)576 void ProgramWindow::setPort(QAction* action) {
577 currentWidget()->setPort(action->text());
578 }
579
setBoard(QAction * action)580 void ProgramWindow::setBoard(QAction* action) {
581 currentWidget()->setBoard(action->text());
582 }
583
beforeClosing(bool showCancel,bool & discard)584 bool ProgramWindow::beforeClosing(bool showCancel, bool & discard) {
585 discard = false;
586 for (int i = 0; i < m_tabWidget->count(); i++) {
587 if (!beforeClosingTab(i, showCancel)) {
588 return false;
589 }
590 }
591
592 return true;
593 }
594
beforeClosingTab(int index,bool showCancel)595 bool ProgramWindow::beforeClosingTab(int index, bool showCancel)
596 {
597 ProgramTab * programTab = indexWidget(index);
598 if (programTab == NULL) return true;
599
600 if (!programTab->isModified()) return true;
601
602 QMessageBox::StandardButton reply = beforeClosingMessage(programTab->filename(), showCancel);
603 if (reply == QMessageBox::Save) {
604 return prepSave(programTab, false);
605 }
606
607 if (reply == QMessageBox::Discard) {
608 return true;
609 }
610
611 return false;
612 }
613
print()614 void ProgramWindow::print() {
615 #ifndef QT_NO_PRINTER
616 QPrinter printer(QPrinter::HighResolution);
617 QPrintDialog printDialog(&printer, this);
618 if (printDialog.exec() == QDialog::Accepted) {
619 currentWidget()->print(printer);
620 }
621 #endif
622 }
623
624
625 // overrides MainWindow::saveAsAux
saveAsAux(const QString & fileName)626 bool ProgramWindow::saveAsAux(const QString & fileName) {
627 if (!m_savingProgramTab) return false;
628
629 bool result = m_savingProgramTab->save(fileName);
630 m_savingProgramTab = NULL;
631 return result;
632 }
633
tabDelete(int index,bool deleteFile)634 void ProgramWindow::tabDelete(int index, bool deleteFile) {
635 ProgramTab * programTab = indexWidget(index);
636 QString fname = programTab->filename();
637 m_tabWidget->removeTab(index);
638 if (m_tabWidget->count() == 0) {
639 addTab();
640 }
641
642 if (deleteFile) {
643 QFile file(fname);
644 file.remove();
645 }
646 }
647
saveAll()648 void ProgramWindow::saveAll() {
649 for (int i= 0; i < m_tabWidget->count(); i++)
650 tabSave(i);
651 }
652
saveCurrentTab()653 void ProgramWindow::saveCurrentTab() {
654 tabSave(m_tabWidget->currentIndex());
655 }
656
tabSave(int index)657 void ProgramWindow::tabSave(int index) {
658 ProgramTab * programTab =indexWidget(index);
659 if (programTab == NULL) return;
660
661 prepSave(programTab, false);
662 }
663
tabSaveAs(int index)664 void ProgramWindow::tabSaveAs(int index) {
665 ProgramTab * programTab = indexWidget(index);
666 if (programTab == NULL) return;
667
668 prepSave(programTab, true);
669 }
670
tabRename(int index)671 void ProgramWindow::tabRename(int index) {
672 ProgramTab * programTab = indexWidget(index);
673 if (programTab == NULL) return;
674
675 QString oldFileName = programTab->filename();
676 if (prepSave(programTab, true)) {
677 if (programTab->filename() != oldFileName) {
678 QFile oldFile(oldFileName);
679 if (oldFile.exists()) {
680 oldFile.remove();
681 emit linkToProgramFile(oldFileName, NULL, false, true);
682 }
683 }
684 }
685 }
686
duplicateTab()687 void ProgramWindow::duplicateTab() {
688 ProgramTab * oldTab = currentWidget();
689 if (oldTab == NULL) return;
690
691 ProgramTab * newTab = addTab();
692
693 newTab->setText(oldTab->text());
694 }
695
tabBeforeClosing(int index,bool & ok)696 void ProgramWindow::tabBeforeClosing(int index, bool & ok) {
697 ok = beforeClosingTab(index, true);
698 }
699
prepSave(ProgramTab * programTab,bool saveAsFlag)700 bool ProgramWindow::prepSave(ProgramTab * programTab, bool saveAsFlag)
701 {
702 m_savingProgramTab = programTab; // need this for the saveAsAux call
703 if (!programTab->isModified()) return false;
704
705 bool result = (saveAsFlag)
706 ? saveAs(programTab->filename(), programTab->readOnly())
707 : save(programTab->filename(), programTab->readOnly());
708
709 if (result) {
710 programTab->setClean();
711 emit linkToProgramFile(programTab->filename(), programTab->platform(), true, true);
712 }
713 return result;
714 }
715
initPlatforms()716 void ProgramWindow::initPlatforms() {
717 QDir dir(FolderUtils::getApplicationSubFolderPath("translations"));
718 Highlighter::loadStyles(dir.absolutePath().append("/syntax/styles.xml"));
719
720 m_platforms << new PlatformArduino() << new PlatformPicaxe();
721 }
722
getAvailablePlatforms()723 QList<Platform *> ProgramWindow::getAvailablePlatforms() {
724 return m_platforms;
725 }
726
getPlatformByName(const QString & platformName)727 Platform * ProgramWindow::getPlatformByName(const QString & platformName) {
728 foreach (Platform * platform, getAvailablePlatforms()) {
729 if (platform->getName().compare(platformName, Qt::CaseInsensitive) == 0)
730 return platform;
731 }
732 return NULL;
733 }
734
hasPlatform(const QString & platformName)735 bool ProgramWindow::hasPlatform(const QString & platformName) {
736 return getPlatformByName(platformName) != NULL;
737 }
738
getBoards()739 const QMap<QString, QString> ProgramWindow::getBoards() {
740 if (currentWidget() && currentWidget()->platform())
741 return currentWidget()->platform()->getBoards();
742
743 QMap<QString, QString> boards;
744 boards.insert(NoBoardName, NoBoardName);
745 return boards;
746 }
747
addBoard(const QString & name,const QString & definition)748 QAction * ProgramWindow::addBoard(const QString & name, const QString & definition)
749 {
750 QAction * currentAction = new QAction(name, this);
751 currentAction->setCheckable(true);
752 currentAction->setData(definition);
753 m_boardActions.insert(name, currentAction);
754 m_boardMenu->addAction(currentAction);
755 m_boardActionGroup->addAction(currentAction);
756 return currentAction;
757 }
758
updateBoards()759 void ProgramWindow::updateBoards() {
760 QMap<QString, QString> boards = getBoards();
761
762 m_boardActions.clear();
763 foreach (QAction * action, m_boardActionGroup->actions())
764 m_boardActionGroup->removeAction(action);
765 m_boardMenu->clear();
766
767 QMapIterator<QString, QString> i(boards);
768 while (i.hasNext()) {
769 i.next();
770 addBoard(i.key(), i.value());
771 }
772 }
773
loadProgramFile()774 void ProgramWindow::loadProgramFile() {
775 DebugDialog::debug("loading program file");
776 currentWidget()->loadProgramFile();
777
778 }
779
loadProgramFileNew()780 void ProgramWindow::loadProgramFileNew() {
781 ProgramTab * programTab = addTab();
782 if (programTab) {
783 if (!programTab->loadProgramFile()) {
784 delete programTab;
785 }
786 }
787 }
788
rename()789 void ProgramWindow::rename() {
790 currentWidget()->rename();
791 }
792
undo()793 void ProgramWindow::undo() {
794 currentWidget()->undo();
795 }
796
redo()797 void ProgramWindow::redo() {
798 currentWidget()->redo();
799 }
800
cut()801 void ProgramWindow::cut() {
802 currentWidget()->cut();
803 }
804
copy()805 void ProgramWindow::copy() {
806 currentWidget()->copy();
807 }
808
paste()809 void ProgramWindow::paste() {
810 currentWidget()->paste();
811 }
812
selectAll()813 void ProgramWindow::selectAll() {
814 currentWidget()->selectAll();
815 }
816
serialMonitor()817 void ProgramWindow::serialMonitor() {
818 currentWidget()->serialMonitor();
819 }
820
sendProgram()821 void ProgramWindow::sendProgram() {
822 currentWidget()->sendProgram();
823 }
824
currentWidget()825 ProgramTab * ProgramWindow::currentWidget() {
826 return qobject_cast<ProgramTab *>(m_tabWidget->currentWidget());
827 }
828
indexWidget(int index)829 ProgramTab * ProgramWindow::indexWidget(int index) {
830 return qobject_cast<ProgramTab *>(m_tabWidget->widget(index));
831 }
832
alreadyHasProgram(const QString & filename)833 bool ProgramWindow::alreadyHasProgram(const QString & filename) {
834 DebugDialog::debug("already has program");
835 for (int i = 0; i < m_tabWidget->count(); i++) {
836 ProgramTab * tab = indexWidget(i);
837 if (tab->filename() == filename) {
838 m_tabWidget->setCurrentIndex(i);
839 return true;
840 }
841 }
842
843 return false;
844 }
845
getExtensionString()846 QString ProgramWindow::getExtensionString() {
847 ProgramTab * pt = currentWidget();
848 if (pt == NULL) return "";
849
850 return pt->extensionString();
851 }
852
getExtensions()853 QStringList ProgramWindow::getExtensions() {
854 ProgramTab * pt = currentWidget();
855 if (pt == NULL) return QStringList();
856
857 return pt->extensions();
858 }
859
getSerialPorts()860 QList<QSerialPortInfo> ProgramWindow::getSerialPorts() {
861 QList<QSerialPortInfo> ports;
862 ports = QSerialPortInfo::availablePorts();
863
864 /*
865 // on the pc, handy for testing the UI when there are no serial ports
866 ports.removeOne("COM0");
867 ports.removeOne("COM1");
868 ports.removeOne("COM2");
869 ports.removeOne("COM3");
870 */
871
872 return ports;
873 }
874
updateSerialPorts()875 void ProgramWindow::updateSerialPorts() {
876 QList<QSerialPortInfo> ports = getSerialPorts();
877
878 m_portActions.clear();
879 foreach (QAction * action, m_serialPortActionGroup->actions())
880 m_serialPortActionGroup->removeAction(action);
881 m_serialPortMenu->clear();
882
883 foreach (QSerialPortInfo port, ports) {
884 addPort(port);
885 }
886 }
887
addPort(QSerialPortInfo port)888 QAction * ProgramWindow::addPort(QSerialPortInfo port)
889 {
890 QAction * currentAction = new QAction(port.portName(), this);
891 currentAction->setCheckable(true);
892 currentAction->setData(port.systemLocation());
893 m_portActions.insert(port.portName(), currentAction);
894 m_serialPortMenu->addAction(currentAction);
895 m_serialPortActionGroup->addAction(currentAction);
896 return currentAction;
897 }
898
hasPort(const QString & portName)899 bool ProgramWindow::hasPort(const QString & portName) {
900 foreach (QSerialPortInfo port, getSerialPorts()) {
901 if (port.portName().compare(portName) == 0)
902 return true;
903 }
904 return false;
905 }
906
updateLink(const QString & filename,Platform * platform,bool addlink,bool strong)907 void ProgramWindow::updateLink(const QString & filename, Platform * platform, bool addlink, bool strong)
908 {
909 DebugDialog::debug("updating link");
910 emit linkToProgramFile(filename, platform, addlink, strong);
911 }
912
portProcessFinished(int exitCode,QProcess::ExitStatus exitStatus)913 void ProgramWindow::portProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) {
914 DebugDialog::debug(QString("process finished %1 %2").arg(exitCode).arg(exitStatus));
915
916 // parse the text and update the combo box
917
918 sender()->deleteLater();
919 }
920
portProcessReadyRead()921 void ProgramWindow::portProcessReadyRead() {
922 m_ports.clear();
923
924 QByteArray byteArray = qobject_cast<QProcess *>(sender())->readAllStandardOutput();
925 QTextStream textStream(byteArray, QIODevice::ReadOnly);
926 while (true) {
927 QString line = textStream.readLine();
928 if (line.isNull()) break;
929
930 if (!line.contains("tty")) continue;
931 if (!line.contains("serial", Qt::CaseInsensitive)) continue;
932
933 QStringList candidates = line.split(" ");
934 foreach (QString candidate, candidates) {
935 if (candidate.contains("tty")) {
936 m_ports.append(candidate);
937 break;
938 }
939 }
940 }
941 }
942