mainwindow.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. #include <QFileDialog>
  2. #include <QDesktopServices>
  3. #include <QUrl>
  4. #include <QMessageBox>
  5. #include <QFileInfo>
  6. #include <QFile>
  7. #include <QTimer>
  8. #include <QDebug>
  9. #include <QSettings>
  10. #include <QInputDialog>
  11. #include "mainwindow.h"
  12. #include "ui_mainwindow.h"
  13. #include "defines.h"
  14. #include "generatorfileoutput.h"
  15. #include "settingsdialog.h"
  16. #include "common/networksettingsdialog.h"
  17. #include "settings.h"
  18. #include "rcsession.h"
  19. #include "qtprojectgeneratorfactory.h"
  20. #include "symbian/symbiandefines.h" // TODO this should be removed eventually
  21. MainWindow::MainWindow(QWidget *parent) :
  22. QMainWindow(parent),
  23. ui(new Ui::MainWindow),
  24. m_generator(NULL),
  25. m_statusUpdater(NULL),
  26. m_rcSession(NULL)
  27. {
  28. ui->setupUi(this);
  29. // Init variables
  30. m_buildEnabled = false;
  31. m_rebuildEnabled = false;
  32. // Remote generator
  33. // TODO: no more acting as a server, this can be moved out from here
  34. m_remote = new RemoteGenerator(this);
  35. // Init log file. UI progress bar is tied to log file updates.
  36. m_outputView = new GeneratorFileOutput(Settings::get(Settings::LogFilePath).toString());
  37. m_rcSession = new RcSession(*m_outputView,
  38. this);
  39. buildPlatformsMenu();
  40. // Init status bar with a progress bar
  41. m_statusBarLabel = new QLabel(this);
  42. m_statusBarLabel->setText(tr("Idle"));
  43. statusBar()->addWidget(m_statusBarLabel, 1);
  44. m_progressBar = new QProgressBar(this);
  45. m_progressBar->setVisible(false);
  46. m_progressBar->setRange(0, m_statusUpdater->maximumProgress());
  47. m_progressBar->setValue(0);
  48. statusBar()->addWidget(m_progressBar, 1);
  49. // Set icons. These are LGPL and *have* to be loaded at run-time!
  50. // Using LGPL compiled in the binary (i.e. Qt resource file) would risk causing
  51. // the whole binary to be LGPL. The current icon set is from Oxygen Project,
  52. // licensed under LGPL v3.
  53. ui->actionOpenSettings->setIcon(QIcon("oxygen-icons/configure.svgz"));
  54. ui->actionConnectToRemoteCompiler->setIcon(QIcon("oxygen-icons/network-wired.svgz"));
  55. ui->actionCloseProject->setIcon(QIcon("oxygen-icons/document-close.svgz"));
  56. ui->actionOpenWidgetFile->setIcon(QIcon("oxygen-icons/document-open.svgz"));
  57. ui->actionOpenURL->setIcon(QIcon("oxygen-icons/document-open-remote.svgz"));
  58. ui->actionOpenDirectory->setIcon(QIcon("oxygen-icons/document-open-folder.svgz"));
  59. ui->actionPreview->setIcon(QIcon("oxygen-icons/document-preview.svgz"));
  60. ui->actionBuild->setIcon(QIcon("oxygen-icons/run-build.svgz"));
  61. ui->actionRebuild->setIcon(QIcon("oxygen-icons/run-build-install.svgz"));
  62. // Prepare menu selected states
  63. ui->actionEnableTextSelection->setChecked(Settings::get(Settings::TextSelectionEnabled).toBool());
  64. ui->actionWidgetIsPannable->setChecked(Settings::get(Settings::PanningEnabled).toBool());
  65. // UI signals connected to slots
  66. connect(ui->actionExit, SIGNAL(triggered()), this, SLOT(exitApplication()));
  67. connect(ui->actionOpenWidgetFile, SIGNAL(triggered()), this, SLOT(openWidgetFile()));
  68. connect(ui->actionOpenDirectory, SIGNAL(triggered()), this, SLOT(openHTMLDirectory()));
  69. connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(about()));
  70. connect(ui->actionOpenDocumentation, SIGNAL(triggered()), this, SLOT(openDocumentation()));
  71. connect(ui->actionCloseProject, SIGNAL(triggered()), this, SLOT(closeProject()));
  72. connect(ui->actionPreview, SIGNAL(triggered()), this, SLOT(preview()));
  73. connect(ui->actionBuild, SIGNAL(triggered()), this, SLOT(build()));
  74. connect(ui->actionRebuild, SIGNAL(triggered()), this, SLOT(rebuild()));
  75. connect(ui->actionWidgetIsPannable, SIGNAL(triggered(bool)), this, SLOT(setPanningEnabled(bool)));
  76. connect(ui->actionEnableTextSelection, SIGNAL(triggered(bool)), this, SLOT(setTextSelectionEnabled(bool)));
  77. connect(ui->actionOpenSettings, SIGNAL(triggered()), this, SLOT(openSettingsDialog()));
  78. connect(ui->actionConnectToRemoteCompiler, SIGNAL(triggered()), this, SLOT(connectToRemoteCompiler()));
  79. connect(ui->buttonOpenLog, SIGNAL(clicked()), this, SLOT(openLogFile()));
  80. connect(ui->buttonOpenDirectory, SIGNAL(clicked()), this, SLOT(openBuildDirectory()));
  81. connect(ui->buttonInstallSis, SIGNAL(clicked()), this, SLOT(installSis()));
  82. connect(ui->actionOpenURL, SIGNAL(triggered()), this, SLOT(openURL()));
  83. connect(m_rcSession,
  84. SIGNAL(refreshRcPropertiesCompleted(bool, bool)),
  85. this,
  86. SLOT(rcPropertiesRefreshedSlot(bool, bool)));
  87. // At startup, there are no projects open
  88. closeProject();
  89. checkSettings();
  90. }
  91. MainWindow::~MainWindow()
  92. {
  93. delete ui;
  94. delete m_generator;
  95. m_generator = 0;
  96. delete m_statusUpdater;
  97. m_statusUpdater = 0;
  98. delete m_outputView;
  99. m_outputView = 0;
  100. }
  101. /**
  102. * Change event handler
  103. */
  104. void MainWindow::changeEvent(QEvent *e)
  105. {
  106. QMainWindow::changeEvent(e);
  107. switch (e->type()) {
  108. case QEvent::LanguageChange:
  109. ui->retranslateUi(this);
  110. break;
  111. default:
  112. break;
  113. }
  114. }
  115. /**
  116. * Closes the application
  117. */
  118. void MainWindow::exitApplication()
  119. {
  120. QCoreApplication::exit(0);
  121. }
  122. /**
  123. * Gives the user a dialog that is used to select a file
  124. */
  125. void MainWindow::openWidgetFile()
  126. {
  127. QString lastPath = Settings::get(Settings::LastWidgetPath).toString();
  128. QString fileName = QFileDialog::getOpenFileName(this, tr("Select Widget File"), lastPath, tr("Widget Files") + " (*.wgz *.wgt);;" + tr("All Files (*)"));
  129. if (!fileName.isEmpty())
  130. {
  131. closeProject();
  132. Settings::set(Settings::LastWidgetPath, QDir::toNativeSeparators(QFileInfo(fileName).absolutePath()));
  133. setActivePath(fileName);
  134. }
  135. }
  136. /**
  137. * Gives the user a dialog requesting URL to be fetched
  138. */
  139. void MainWindow::openURL()
  140. {
  141. QUrl url = QUrl::fromUserInput(QInputDialog::getText(this, tr("Give URL"), "Give URL of the page to be fetched:"));
  142. if (url.isValid())
  143. {
  144. closeProject();
  145. connect(m_remote, SIGNAL(message(QString)), m_outputView, SLOT(printOutput(QString)));
  146. connect(m_remote, SIGNAL(finished()), this, SLOT(remoteContentFetched()));
  147. m_progressBar->setRange(0, 0);
  148. m_statusBarLabel->setVisible(false);
  149. m_progressBar->setVisible(true);
  150. ui->labelStatusValue->setText("Fetching " + url.toString());
  151. m_remote->getPage(url.toString(), url.host());
  152. }
  153. else if (!url.isEmpty())
  154. {
  155. QMessageBox::critical(this, tr("Invalid URL"), tr("The given URL is not valid."));
  156. }
  157. }
  158. /**
  159. * Remote content fetch is finished, reflect in the UI
  160. */
  161. void MainWindow::remoteContentFetched()
  162. {
  163. m_progressBar->setRange(0, m_statusUpdater->maximumProgress());
  164. m_progressBar->setValue(m_statusUpdater->maximumProgress());
  165. ui->labelStatusValue->setText(tr("Content fetch done"));
  166. }
  167. /**
  168. * Gives the user a dialog that is used to select a directory
  169. */
  170. void MainWindow::openHTMLDirectory()
  171. {
  172. QString lastPath = Settings::get(Settings::LastDirectoryPath).toString();
  173. QString directoryName = QFileDialog::getExistingDirectory(this, tr("Select HTML Directory. The application will be generated from the contents of the selected directory."), lastPath);
  174. if (!directoryName.isEmpty())
  175. {
  176. closeProject();
  177. Settings::set(Settings::LastDirectoryPath, QDir::toNativeSeparators(directoryName));
  178. setActivePath(directoryName);
  179. }
  180. }
  181. /**
  182. * Sets the path to the opened file/directory. This will be called
  183. * also from the remote generator.
  184. */
  185. void MainWindow::setActivePath(QString path)
  186. {
  187. m_currentFileOrDirectory = path;
  188. ui->labelProjectValue->setText(QDir::toNativeSeparators(path));
  189. setBuildActionsEnabled(true);
  190. }
  191. /**
  192. * Opens a pre-defined URL in a system's preferred browser
  193. */
  194. void MainWindow::openDocumentation()
  195. {
  196. QString documentation = "HAG User Guide.pdf";
  197. if (!QFile::exists(documentation))
  198. {
  199. // PDF does not exist any more
  200. QMessageBox::critical(this, "Documentation not found", "Documentation file was not found! Please reinstall application.");
  201. }
  202. else
  203. {
  204. // Just presume the user has a PDF viewer installed
  205. QDesktopServices::openUrl(QUrl::fromLocalFile(documentation));
  206. }
  207. }
  208. /**
  209. * Calls the SIS, and if the user has PC Suite installed, it will
  210. * prompt the installation
  211. */
  212. void MainWindow::installSis()
  213. {
  214. QFileInfo
  215. pkgFile = m_statusUpdater->pkgFileInfo();
  216. IQtProjectGenerator::PkgInstallResult
  217. result = m_generator->installPkg(pkgFile);
  218. // TODO maybe some action about supportedness / success
  219. Q_UNUSED(result)
  220. }
  221. /**
  222. * Shows "about" message box
  223. */
  224. void MainWindow::about()
  225. {
  226. QMessageBox aboutBox;
  227. QPixmap pixmap(":/gfx/qt.svg");
  228. pixmap = pixmap.scaled(QSize(100, 100), Qt::KeepAspectRatio, Qt::SmoothTransformation);
  229. aboutBox.setIconPixmap(pixmap);
  230. aboutBox.setWindowTitle(tr("About Hybrid Application Generator"));
  231. aboutBox.setText(tr("Hybrid Application Generator 0.1"));
  232. aboutBox.setInformativeText("Built on Apr 14 2010\n\nCopyright 2009-2010 Nokia Corporation. All rights reserved.\n\nThe program is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.");
  233. aboutBox.setStyleSheet("#qt_msgbox_label { font-weight: bold; font-size: 10pt; }");
  234. aboutBox.exec();
  235. }
  236. void MainWindow::platformChosen()
  237. {
  238. QAction
  239. * action = qobject_cast<QAction*>(sender());
  240. if (action != NULL)
  241. {
  242. QString
  243. platformId = action->data().toString();
  244. setPlatform(platformId);
  245. m_progressBar->setRange(0, m_statusUpdater->maximumProgress());
  246. m_progressBar->setValue(0);
  247. }
  248. }
  249. /**
  250. * Enable actions that are directly related to a widget only if a
  251. * widget is currectly open
  252. */
  253. void MainWindow::setBuildActionsEnabled(bool enabled)
  254. {
  255. // Set new value buildEnabled; rebuildEnabled also has
  256. // to be allowed beforehand to be allowed now
  257. m_buildEnabled = enabled;
  258. m_rebuildEnabled = m_rebuildEnabled && m_buildEnabled;
  259. if (enabled == false)
  260. {
  261. ui->labelStatusValue->setText(tr("Idle"));
  262. m_statusBarLabel->setText(tr("Idle"));
  263. ui->labelLogValue->setText(tr("<no log available>"));
  264. ui->buttonOpenDirectory->setEnabled(false);
  265. ui->buttonInstallSis->setEnabled(false);
  266. m_progressBar->setVisible(false);
  267. m_statusBarLabel->setVisible(true);
  268. }
  269. // Check that settings allow building
  270. checkSettings();
  271. // Actions are enabled only when a project is open
  272. ui->actionCloseProject->setEnabled(enabled);
  273. }
  274. /**
  275. * Closes the project, i.e. clear selected file and disables
  276. * relevant menu items.
  277. */
  278. void MainWindow::closeProject()
  279. {
  280. m_generator->closeProject();
  281. m_currentFileOrDirectory = "";
  282. ui->labelProjectValue->setText(tr("<no file selected>"));
  283. setBuildActionsEnabled(false);
  284. }
  285. /**
  286. * If the selected file / folder still exists, prepares the necessary
  287. * folder structure and opens an external application that shows the
  288. * widget-to-be in a window that is sized to match device resolution
  289. */
  290. void MainWindow::preview()
  291. {
  292. // Check that file exists
  293. if (QFile::exists(m_currentFileOrDirectory))
  294. {
  295. m_generator->preview(m_currentFileOrDirectory);
  296. }
  297. }
  298. /**
  299. * Extract, prepare, and build the selected file / folder
  300. */
  301. void MainWindow::build()
  302. {
  303. m_generator->build(m_currentFileOrDirectory,
  304. *m_rcSession);
  305. }
  306. /**
  307. * Rebuild without cleaning the target folder structure first.
  308. * Can be used to keep changes if e.g. widget content is changed
  309. * directly in the build directory -- otherwise the target would
  310. * be deleted and replaced with fresh source files.
  311. */
  312. void MainWindow::rebuild()
  313. {
  314. m_generator->rebuild(*m_rcSession);
  315. }
  316. /**
  317. * Enables / disables the option to enable panning canvas in widgets
  318. */
  319. void MainWindow::setPanningEnabled(bool enabled)
  320. {
  321. Settings::set(Settings::PanningEnabled, enabled);
  322. }
  323. /**
  324. * Enables / disables the option to disable text selection in widgets
  325. */
  326. void MainWindow::setTextSelectionEnabled(bool enabled)
  327. {
  328. Settings::set(Settings::TextSelectionEnabled, enabled);
  329. }
  330. /**
  331. * Update the UI when the generation progresses
  332. */
  333. void MainWindow::progressUpdated()
  334. {
  335. m_statusBarLabel->setVisible(false);
  336. m_progressBar->setVisible(true);
  337. m_progressBar->setValue(m_statusUpdater->currentProgress());
  338. ui->labelStatusValue->setText(m_statusUpdater->statusText());
  339. ui->labelLogValue->setText(tr("Logged") + " " + QString::number(m_statusUpdater->logSize()) + " " + tr("rows"));
  340. }
  341. /**
  342. * When the generation progress is done, update the UI
  343. * to reflect the completed state
  344. */
  345. void MainWindow::progressFinished(bool success)
  346. {
  347. m_rebuildEnabled = m_buildEnabled;
  348. ui->actionRebuild->setEnabled(m_buildEnabled);
  349. ui->buttonOpenDirectory->setEnabled(true);
  350. if (success && m_generator->getTarget() == BUILD)
  351. {
  352. ui->buttonInstallSis->setEnabled(true);
  353. }
  354. }
  355. /**
  356. * Opens the log file in an external viewer.
  357. */
  358. void MainWindow::openLogFile()
  359. {
  360. QDesktopServices::openUrl(QUrl::fromLocalFile(Settings::get(Settings::LogFilePath).toString()));
  361. }
  362. /**
  363. * Opens the settings dialog that has further configurations
  364. */
  365. void MainWindow::openSettingsDialog()
  366. {
  367. SettingsDialog
  368. sd(m_generator,
  369. m_currentFileOrDirectory,
  370. this);
  371. sd.exec();
  372. checkSettings();
  373. }
  374. /**
  375. * Opens the network settings dialog.
  376. */
  377. void MainWindow::connectToRemoteCompiler()
  378. {
  379. NetworkSettingsDialog
  380. nsd(m_rcSession,
  381. this);
  382. nsd.exec();
  383. if (nsd.justSignedIn())
  384. {
  385. m_rcSession->refreshRcProperties();
  386. }
  387. checkSettings();
  388. }
  389. /**
  390. * Check settings that has to be correct before building can proceed.
  391. */
  392. void MainWindow::checkSettings()
  393. {
  394. QStringList
  395. errors = m_generator->errorsForTarget(BUILD);
  396. if (errors.empty())
  397. {
  398. ui->labelBuildValue->setText(Settings::get(Settings::QtVersionString).toString());
  399. ui->labelSettingsValue->setStyleSheet("");
  400. ui->labelSettingsValue->setText("Settings OK");
  401. }
  402. else
  403. {
  404. ui->labelBuildValue->setText("<not available>");
  405. ui->labelSettingsValue->setStyleSheet("QLabel { color: red; }");
  406. ui->labelSettingsValue->setText(errors.at(0));
  407. }
  408. // The preview Qt version is the version with which the generator is compiled
  409. ui->labelPreviewValue->setText("4.6.2");
  410. // Settings affect the building: disable if qtenv.bat is not configured
  411. ui->actionPreview->setEnabled(m_buildEnabled && errors.empty());
  412. ui->actionBuild->setEnabled(m_buildEnabled && errors.empty());
  413. ui->actionRebuild->setEnabled(m_rebuildEnabled && errors.empty());
  414. }
  415. void MainWindow::setPlatform(QString platformId)
  416. {
  417. if (platformId != m_platformId || m_generator == NULL)
  418. {
  419. delete m_generator;
  420. m_generator = 0;
  421. delete m_statusUpdater;
  422. m_statusUpdater = 0;
  423. m_platformId = platformId;
  424. QtProjectGeneratorFactory
  425. generatorFactory;
  426. m_generator = generatorFactory.createProjectGenerator(m_platformId,
  427. *m_outputView,
  428. NULL); // parent
  429. // Status updater
  430. m_statusUpdater = m_generator->createStatusUpdater(NULL); // parent
  431. connect(m_statusUpdater, SIGNAL(updated()), this, SLOT(progressUpdated()));
  432. connect(m_statusUpdater, SIGNAL(finished(bool)), this, SLOT(progressFinished(bool)));
  433. setActivePath(m_currentFileOrDirectory);
  434. }
  435. }
  436. void MainWindow::openBuildDirectory()
  437. {
  438. QDesktopServices::openUrl(QUrl::fromLocalFile(m_generator->generatorData().projectDirectory()));
  439. }
  440. void MainWindow::rcPropertiesRefreshedSlot(bool success,
  441. bool hasRcPropertiesChanged)
  442. {
  443. if (success)
  444. {
  445. if (hasRcPropertiesChanged)
  446. {
  447. buildPlatformsMenu();
  448. QMessageBox::information(this,
  449. "Connecting Remote Compiler Succeeded",
  450. "Please check build menu for potential\n"
  451. "new remote build targets",
  452. QMessageBox::Ok);
  453. }
  454. }
  455. else
  456. {
  457. QMessageBox::warning(this,
  458. "Remote Compiler Request Failed",
  459. "Querying Remote Compiler for supporter OS/QT platforms failed.",
  460. QMessageBox::Ok);
  461. }
  462. }
  463. void MainWindow::buildPlatformsMenu()
  464. {
  465. // we clear up the build menu after the second separator
  466. QObjectList
  467. buildMenuKids = ui->menuBuild->children();
  468. foreach (QObject * kid, buildMenuKids)
  469. {
  470. QAction
  471. * kidAction = qobject_cast<QAction*>(kid);
  472. if (kidAction == NULL)
  473. continue;
  474. if (kidAction->data().toString().indexOf(DYN_BUILDTARGET) == 0)
  475. {
  476. ui->menubar->removeAction(kidAction);
  477. delete kidAction;
  478. }
  479. }
  480. // Getting platform infos, and building menu items & actions for them
  481. QList<QAction*>
  482. platformActions;
  483. QString
  484. defaultPlatformId;
  485. QtProjectGeneratorFactory
  486. generatorFactory;
  487. QList<PlatformInfo>
  488. platformInfos = generatorFactory.supportedPlatforms();
  489. QList<PlatformInfo>::const_iterator
  490. piIt = platformInfos.begin(),
  491. piEnd = platformInfos.end();
  492. for (; piIt != piEnd; ++piIt)
  493. {
  494. QAction
  495. * action = new QAction(piIt->m_platformName,
  496. ui->menuBuild);
  497. action->setData(piIt->m_platformId);
  498. action->setCheckable(true);
  499. // OBS action->setStatusTip(tr("Choose platform to build for"));
  500. action->setStatusTip(piIt->m_shortDescription);
  501. connect(action,
  502. SIGNAL(triggered()),
  503. this,
  504. SLOT(platformChosen()));
  505. platformActions.push_back(action);
  506. if (piIt->m_isDefault)
  507. {
  508. action->setChecked(true);
  509. defaultPlatformId = piIt->m_platformId;
  510. }
  511. }
  512. // QActionGroup makes these platform menu items behave like "radio buttons"
  513. // only one can be selected at a time
  514. QActionGroup
  515. * platformActionGroup = new QActionGroup(this);
  516. QList<QAction*>::const_iterator
  517. paIt = platformActions.begin(),
  518. paEnd = platformActions.end();
  519. for (; paIt != paEnd; ++paIt)
  520. platformActionGroup->addAction(*paIt);
  521. // Adding actions to the "build" menu
  522. paIt = platformActions.begin(),
  523. paEnd = platformActions.end();
  524. for (; paIt != paEnd; ++paIt)
  525. ui->menuBuild->addAction(*paIt);
  526. // setting the default platform name (and m_generator and m_statusUpdater
  527. // instances accordingly)
  528. setPlatform(defaultPlatformId);
  529. }