OSDN Git Service

[denncoCreator] fixed a crash bug.
[dennco/denncoCreator.git] / Source / mainwindow.cpp
1 //  Copyright (c) 2012 Dennco Project
2 //
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, either version 3 of the License, or
6 // (at your option) any later version.
7 //
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
16 //
17 //  Created by tkawata on Sep-30, 2012.
18 //
19 #include "TKLog.h"
20
21 #include "mainwindow.h"
22 #include "ui_mainwindow.h"
23
24 #include "dccreator.h"
25 #include "dccontent.h"
26 #include "dcglvisualizerwidget.h"
27 #include "dctreeviewwidget.h"
28 #include "dccellcodescripttreeviewwidget.h"
29 #include "TKConsole.h"
30 #include "utils/dcresources.h"
31 #include "utils/dcskeltoncreatorutil.h"
32 #include "dialog/dcinputnewcontentdirdialog.h"
33 #include "dialog/dcmanagecellcodedialog.h"
34 #include "dialog/dccodeeditorexternaleditorsettingdialog.h"
35 #include "dccellscriptseditorwindow.h"
36
37 #include <QCloseEvent>
38 #include <QFileDialog>
39 #include <QMessageBox>
40 #include <QSettings>
41 #include <QLocalSocket>
42
43 #if defined(Q_WS_WIN)
44     const QString DENNCO_ENGINE = "QtDennco.exe";
45 #elif defined(Q_WS_MAC)
46     const QString DENNCO_ENGINE = "QtDennco.app";
47 #elif defined(Q_OS_UNIX)
48     const QString DENNCO_ENGINE = "./QtDennco";
49 #endif
50
51 class SleeperThread : public QThread
52 {
53 public:
54     static void msleep(unsigned long msecs)
55     {
56         QThread::msleep(msecs);
57     }
58 };
59
60 MainWindow::MainWindow(QWidget *parent) :
61     QMainWindow(parent),
62     ui(new Ui::MainWindow)
63 {
64     ui->setupUi(this);
65
66     DCResources::initResources();
67
68     createActions();
69     createMenus();
70     createToolBars();
71     createStatusBar();
72
73     readSettings();
74
75     TKLog::setDestination(new TKConsole);
76
77     d_creator = new DCCreator(this);
78     d_visualizerWidget = new DCGLVisualizerWidget(d_creator, this) ;
79     setCentralWidget(d_visualizerWidget);
80
81     d_treeViewWidget = new DCTreeViewWidget(this, d_creator);
82     ui->treeViewDock->layout()->addWidget(d_treeViewWidget);
83
84     d_cellCodeScriptTreeViewWidget = new DCCellCodeScriptTreeViewWidget(this, d_creator);
85     ui->cellCodeScriptTreeViewDock->layout()->addWidget(d_cellCodeScriptTreeViewWidget);
86
87     DCCellScriptsEditorWindow::construct(d_creator);
88
89     setCurrentContent("");
90     setUnifiedTitleAndToolBarOnMac(true);
91
92     tabifyDockWidget(ui->dock2,ui->dock1);
93 }
94
95 MainWindow::~MainWindow()
96 {
97     delete ui;
98     if (d_creator)
99         delete d_creator;
100 }
101
102
103 void MainWindow::closeEvent(QCloseEvent *event)
104 {
105     if (maybeSave()) {
106         writeSettings();
107         event->accept();
108     } else {
109         event->ignore();
110     }
111 }
112
113 void MainWindow::newFile()
114 {
115     QString bdir = QDir::home().absolutePath();
116     if (d_contentOpenHistory.length()>0)
117     {
118         QDir d(d_contentOpenHistory.at(0));
119         d.cdUp();
120         bdir = d.absolutePath();
121     }
122     DCInputNewContentDirDialog dialog(bdir);
123     dialog.exec();
124
125     QString dirName = dialog.getResult();
126     if (dirName.length() > 0 && DCSkeltonCreatorUtil::createNewContent(dirName))
127     {
128         loadContent(dirName);
129     }
130 }
131
132 void MainWindow::open()
133 {
134     if (maybeSave())
135     {
136         QString dir = QDir::home().absolutePath();
137         if (d_contentOpenHistory.length()>0)
138         {
139             QDir d(d_contentOpenHistory.at(0));
140             d.cdUp();
141             dir = d.absolutePath();
142         }
143         QString dirName = QFileDialog::getExistingDirectory(this, tr("Open a content"),dir);
144         if (!dirName.isEmpty())
145             loadContent(dirName);
146     }
147 }
148
149 bool MainWindow::openRecent(int idx)
150 {
151     if (maybeSave())
152     {
153         if (d_contentOpenHistory.length() > idx)
154         {
155             loadContent(QString(d_contentOpenHistory.at(idx)));
156             return true;
157         }
158     }
159     return false;
160 }
161
162 void MainWindow::openRecent1()
163 {
164     openRecent(0);
165 }
166
167 void MainWindow::openRecent2()
168 {
169     openRecent(1);
170 }
171
172 void MainWindow::openRecent3()
173 {
174     openRecent(2);
175 }
176
177 void MainWindow::openRecent4()
178 {
179     openRecent(3);
180 }
181
182 void MainWindow::openRecent5()
183 {
184     openRecent(4);
185 }
186
187 bool MainWindow::save(bool showMessage)
188 {
189     if (!d_creator)
190         return false;
191
192     return d_creator->saveAll(showMessage);
193 }
194
195 bool MainWindow::save()
196 {
197     return save(true);
198 }
199
200 bool MainWindow::saveAs()
201 {
202     //TODO
203     return false;
204 }
205
206 void MainWindow::doExternalEditorSetting()
207 {
208     DCCodeEditorExternalEditorSettingDialog dialog(this);
209     dialog.exec();
210 }
211
212 void MainWindow::about()
213 {
214    QMessageBox::about(this, tr("About Application"),
215             tr("TODO: exampalin\81@about <b>dennco creator</b>  "
216                "TODO: exampalin\81@about <b>dennco creator</b> "
217                "TODO: exampalin\81@about <b>dennco creator</b>"));
218 }
219
220 void MainWindow::documentWasModified()
221 //! [15] //! [16]
222 {
223 //    setWindowModified(textEdit->document()->isModified());
224 }
225 //! [16]
226
227 void MainWindow::createActions()
228 {
229     newAct = new QAction(QIcon(":/images/new.png"), tr("&New"), this);
230     newAct->setShortcuts(QKeySequence::New);
231     newAct->setStatusTip(tr("Create a new file"));
232     connect(newAct, SIGNAL(triggered()), this, SLOT(newFile()));
233
234     openAct = new QAction(QIcon(":/images/open.png"), tr("&Open..."), this);
235     openAct->setShortcuts(QKeySequence::Open);
236     openAct->setStatusTip(tr("Open an existing file"));
237     connect(openAct, SIGNAL(triggered()), this, SLOT(open()));
238
239     QAction *openRecentAct = new QAction(this);
240     connect(openRecentAct, SIGNAL(triggered()), this, SLOT(openRecent1()));
241     openRecentActs.append(openRecentAct);
242
243     openRecentAct = new QAction(this);
244     connect(openRecentAct, SIGNAL(triggered()), this, SLOT(openRecent2()));
245     openRecentActs.append(openRecentAct);
246
247     openRecentAct = new QAction(this);
248     connect(openRecentAct, SIGNAL(triggered()), this, SLOT(openRecent3()));
249     openRecentActs.append(openRecentAct);
250
251     openRecentAct = new QAction(this);
252     connect(openRecentAct, SIGNAL(triggered()), this, SLOT(openRecent4()));
253     openRecentActs.append(openRecentAct);
254
255     openRecentAct = new QAction(this);
256     connect(openRecentAct, SIGNAL(triggered()), this, SLOT(openRecent5()));
257     openRecentActs.append(openRecentAct);
258
259     saveAct = new QAction(QIcon(":/images/save.png"), tr("&Save"), this);
260     saveAct->setShortcuts(QKeySequence::Save);
261     saveAct->setStatusTip(tr("Save the document to disk"));
262     connect(saveAct, SIGNAL(triggered()), this, SLOT(save()));
263
264     saveAsAct = new QAction(tr("Save &As..."), this);
265     saveAsAct->setShortcuts(QKeySequence::SaveAs);
266     saveAsAct->setStatusTip(tr("Save the document under a new name"));
267     connect(saveAsAct, SIGNAL(triggered()), this, SLOT(saveAs()));
268
269     exitAct = new QAction(tr("E&xit"), this);
270     exitAct->setShortcuts(QKeySequence::Quit);
271     exitAct->setStatusTip(tr("Exit the application"));
272     connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
273
274     manageCellCodeAct = new QAction(tr("Manage Cell Code..."), this);
275     connect(manageCellCodeAct, SIGNAL(triggered()), this, SLOT(manageCellCode()));
276
277     cutAct = new QAction(QIcon(":/images/cut.png"), tr("Cu&t"), this);
278     cutAct->setShortcuts(QKeySequence::Cut);
279     cutAct->setStatusTip(tr("Cut the current selection's contents to the "
280                             "clipboard"));
281 //    connect(cutAct, SIGNAL(triggered()), textEdit, SLOT(cut()));
282
283     copyAct = new QAction(QIcon(":/images/copy.png"), tr("&Copy"), this);
284     copyAct->setShortcuts(QKeySequence::Copy);
285     copyAct->setStatusTip(tr("Copy the current selection's contents to the "
286                              "clipboard"));
287 //    connect(copyAct, SIGNAL(triggered()), textEdit, SLOT(copy()));
288
289     pasteAct = new QAction(QIcon(":/images/paste.png"), tr("&Paste"), this);
290     pasteAct->setShortcuts(QKeySequence::Paste);
291     pasteAct->setStatusTip(tr("Paste the clipboard's contents into the current "
292                               "selection"));
293
294     //    connect(pasteAct, SIGNAL(triggered()), textEdit, SLOT(paste()));
295
296     playAct = new QAction(QIcon(":/images/playbutton.png"), tr("Play"), this);
297     playAct->setStatusTip(tr("Play the editing content"));
298
299     connect(playAct, SIGNAL(triggered()), this, SLOT(playContent()));
300
301     externalEditorSettingAct = new QAction(tr("&External editor..."), this);
302     externalEditorSettingAct->setStatusTip(tr("Setup properties for external code editor."));
303     connect(externalEditorSettingAct, SIGNAL(triggered()), this, SLOT(doExternalEditorSetting()));
304
305     aboutAct = new QAction(tr("&About"), this);
306     aboutAct->setStatusTip(tr("Show the application's About box"));
307     connect(aboutAct, SIGNAL(triggered()), this, SLOT(about()));
308
309     cutAct->setEnabled(false);
310     copyAct->setEnabled(false);
311     playAct->setEnabled(false);
312 //    connect(textEdit, SIGNAL(copyAvailable(bool)),
313 //            cutAct, SLOT(setEnabled(bool)));
314 //    connect(textEdit, SIGNAL(copyAvailable(bool)),
315 //            copyAct, SLOT(setEnabled(bool)));
316 }
317
318 void MainWindow::createMenus()
319 {
320     fileMenu = menuBar()->addMenu(tr("&File"));
321     fileMenu->addAction(newAct);
322     fileMenu->addAction(openAct);
323     openRecentMenu = fileMenu->addMenu(tr("Opened recently"));
324     fileMenu->addAction(saveAct);
325     fileMenu->addAction(saveAsAct);
326     fileMenu->addSeparator();
327     fileMenu->addAction(exitAct);
328
329     editMenu = menuBar()->addMenu(tr("&Edit"));
330     editMenu->addAction(manageCellCodeAct);
331     editMenu->addSeparator();
332     editMenu->addAction(cutAct);
333     editMenu->addAction(copyAct);
334     editMenu->addAction(pasteAct);
335
336     menuBar()->addSeparator();
337
338     editMenu = menuBar()->addMenu(tr("&Play"));
339     editMenu->addAction(playAct);
340
341     menuBar()->addSeparator();
342
343     settingMenu = menuBar()->addMenu(tr("&Setting"));
344     settingMenu->addAction(externalEditorSettingAct);
345     helpMenu = menuBar()->addMenu(tr("&Help"));
346     helpMenu->addAction(aboutAct);
347 }
348
349 void MainWindow::createOpenRecentMenu()
350 {
351     openRecentMenu->clear();
352     for (int i = 0; i < d_contentOpenHistory.length() && i < openRecentActs.length(); i++)
353     {
354         openRecentActs.at(i)->setText(d_contentOpenHistory.at(i));
355         openRecentMenu->addAction(openRecentActs.at(i));
356     }
357 }
358
359 void MainWindow::createToolBars()
360 {
361     fileToolBar = addToolBar(tr("File"));
362     fileToolBar->addAction(newAct);
363     fileToolBar->addAction(openAct);
364     fileToolBar->addAction(saveAct);
365
366     editToolBar = addToolBar(tr("Edit"));
367     editToolBar->addAction(cutAct);
368     editToolBar->addAction(copyAct);
369     editToolBar->addAction(pasteAct);
370
371     playToolBar = addToolBar(tr("Play"));
372     playToolBar->addAction(playAct);
373 }
374
375 void MainWindow::createStatusBar()
376 //! [32] //! [33]
377 {
378     statusBar()->showMessage(tr("Ready"));
379 }
380 //! [33]
381
382 void MainWindow::readSettings()
383 {
384     QSettings settings("dennco project", "dennco creator");
385     QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
386     QSize size = settings.value("size", QSize(400, 400)).toSize();
387     d_contentOpenHistory = settings.value("contents", QStringList()).toStringList();
388
389     resize(size);
390     move(pos);
391     createOpenRecentMenu();
392 }
393
394 void MainWindow::writeSettings()
395 {
396     QSettings settings("dennco project", "dennco creator");
397     settings.setValue("pos", pos());
398     settings.setValue("size", size());
399     settings.setValue("contents", d_contentOpenHistory);
400 }
401
402 //static
403 QByteArray MainWindow::readSettingsForCellScriptsEditorGeometory()
404 {
405     QSettings settings("dennco project", "dennco creator");
406     return settings.value("segeometory").toByteArray();
407 }
408
409 //static
410 void MainWindow::writeSettingsForCellScriptsEditorGeometory(const QByteArray &value)
411 {
412     QSettings settings("dennco project", "dennco creator");
413     settings.setValue("segeometory", value);
414 }
415
416 //static
417 QString MainWindow::readSettingsForExternalScriptEditorPath()
418 {
419     QSettings settings("dennco project", "dennco creator");
420     return settings.value("external editor path").toString();
421 }
422
423 //static
424 QString MainWindow::readSettingsForExternalScriptEditorParameters()
425 {
426     QSettings settings("dennco project", "dennco creator");
427     return settings.value("external editor param").toString();
428 }
429
430 //static
431 void MainWindow::writeSettingsForExternalScriptEditorPath(const QString &arg)
432 {
433     QSettings settings("dennco project", "dennco creator");
434     settings.setValue("external editor path", arg);
435 }
436
437 //static
438 void MainWindow::writeSettingsForExternalScriptEditorParameters(const QString &arg)
439 {
440     QSettings settings("dennco project", "dennco creator");
441     settings.setValue("external editor param", arg);
442 }
443
444 bool MainWindow::maybeSave()
445 {
446     if (d_creator->getCurrentContent())
447     {
448         if (!DCCellScriptsEditorWindow::closeEditor())
449             return false;
450
451         if (d_creator->getCurrentContent()->getIsModified())
452         {
453             QMessageBox::StandardButton ret;
454             ret = QMessageBox::warning(this, tr("Application"),
455                 tr("The content has been modified.\n"
456                 "Do you want to save your changes?"),
457                 QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
458             if (ret == QMessageBox::Save)
459                 return save(false);
460             else if (ret == QMessageBox::Cancel)
461                 return false;
462         }
463     }
464     return true;
465 }
466
467 void MainWindow::loadContent(const QString &contentDirectory)
468 {
469     if (!d_creator)
470         return;
471
472     if (d_creator->loadContent(contentDirectory))
473     {
474         playAct->setEnabled(true);
475     }
476     else
477     {
478         playAct->setEnabled(false);
479     }
480
481     QString absContentDirectory = QDir().absoluteFilePath(contentDirectory);
482     int idx = d_contentOpenHistory.indexOf(absContentDirectory);
483     if (idx != -1)
484     {
485         d_contentOpenHistory.removeAt(idx);
486     }
487     d_contentOpenHistory.prepend(absContentDirectory);
488     while (d_contentOpenHistory.length()>5)
489     {
490         d_contentOpenHistory.removeLast();
491     }
492     setCurrentContent(contentDirectory);
493     createOpenRecentMenu();
494
495     QDir dir(contentDirectory);
496     QString title = dir.dirName();
497     title.append(" - dennco creator");
498     setWindowTitle(title);
499 }
500
501 void MainWindow::setCurrentContent(const QString &contentDirectory)
502 {
503     curContent = contentDirectory;
504     setWindowModified(false);
505
506     QString shownName = curContent;
507     if (curContent.isEmpty())
508         shownName = "untitled.txt";
509     setWindowFilePath(shownName);
510 }
511
512 QString MainWindow::strippedName(const QString &fullFileName)
513 {
514     return QFileInfo(fullFileName).fileName();
515 }
516
517 void MainWindow::playContent()
518 {
519     if (!d_creator || !d_creator->getCurrentContent())
520         return;
521
522     maybeSave();
523
524     if (d_player.state() != QProcess::NotRunning)
525     {
526         QLocalSocket *socket = new QLocalSocket(this);
527         connect(socket, SIGNAL(disconnected()), socket,SLOT(deleteLater()));
528
529         for (int retry = 0; retry < 20; retry++)
530         {
531             socket->connectToServer(d_IPCServerName);
532             if (socket->waitForConnected())
533             {
534                 break;
535             }
536             SleeperThread::msleep(500);
537         }
538         QString requestData = "close,";
539         socket->write(requestData.toLocal8Bit());
540         socket->flush();
541         socket->waitForBytesWritten();
542         socket->close();
543
544         if (!d_player.waitForFinished())
545             d_player.kill();
546     }
547
548     d_IPCServerName = "denncoCreator_";
549     QTextStream s(&d_IPCServerName);
550     s.setIntegerBase(16);
551     s << qrand();
552
553     if (d_player.state() == QProcess::NotRunning)
554     {
555         QStringList args;
556 #ifdef Q_WS_MAC
557         args << QCoreApplication::applicationDirPath() + "/../../../" + DENNCO_ENGINE;
558         args << "--args";
559 #endif
560         args << "-creatorControlled";
561         args << d_IPCServerName;
562
563 #ifdef Q_WS_MAC
564         d_player.start("open" , args);
565 #else
566         d_player.start(DENNCO_ENGINE, args);
567 #endif
568         d_player.waitForStarted();
569     }
570
571     if (d_player.state() == QProcess::Running)
572     {
573         QLocalSocket *socket = new QLocalSocket(this);
574         connect(socket, SIGNAL(disconnected()), socket,SLOT(deleteLater()));
575         for (int retry = 0; retry < 20; retry++)
576         {
577             socket->connectToServer(d_IPCServerName);
578             if (socket->waitForConnected())
579             {
580                 break;
581             }
582             SleeperThread::msleep(500);
583         }
584         QString requestData = "load,";
585         requestData.append(d_creator->getCurrentContent()->getContentRootPath());
586         socket->write(requestData.toLocal8Bit());
587         socket->flush();
588         socket->waitForBytesWritten();
589         socket->close();
590     }
591 }
592
593 void MainWindow::manageCellCode()
594 {
595     if (!d_creator)
596         return;
597
598     DCManageCellCodeDialog dialog(d_creator, NULL, this);
599     dialog.exec();
600 }
601
602
603 //static
604 bool MainWindow::openExternalEditorFor(const QString &path)
605 {
606     static QList<QProcess*> s_processes;
607     static QMutex s_mutex;
608
609     QMutexLocker locker(&s_mutex);
610
611     QString editorPath = readSettingsForExternalScriptEditorPath();
612     if (editorPath.isEmpty() || editorPath.length() == 0)
613     {
614         DCCodeEditorExternalEditorSettingDialog dialog;
615         dialog.exec();
616         editorPath = readSettingsForExternalScriptEditorPath();
617     }
618     if (editorPath.isEmpty() || editorPath.length() == 0)
619         return false;
620
621     QString parameterOrg = readSettingsForExternalScriptEditorParameters();
622     QString parameter = "";
623     int i = 0;
624     int j = parameterOrg.indexOf(QRegExp("%F[ \t\"']|%F$"),i);
625     while (j >= 0)
626     {
627         parameter += parameterOrg.mid(i, j-i);
628         parameter += path;
629         i = j + 2;
630         if (i > parameterOrg.length())
631             break;
632         j = parameterOrg.indexOf(QRegExp("%F[ \t\"']|%F$"),i);
633     }
634     if (i < parameterOrg.length())
635         parameter += parameterOrg.mid(i);
636
637     QList<QProcess*>::iterator it = s_processes.begin();
638     while (it != s_processes.end())
639     {
640         if ((*it)->state() != QProcess::Running)
641         {
642             delete (*it);
643             it = s_processes.erase(it);
644         }
645         else
646             ++it;
647     }
648     QProcess *newProcess = new QProcess;
649     newProcess->start("\"" + editorPath + "\" " + parameter);
650
651     s_processes.append(newProcess);
652
653     return true;
654 }