946582e893109bca7de928f7e9b06e51e5b29483
[profile/ivi/qtxmlpatterns.git] / tests / auto / xmlpatternsview / view / MainWindow.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the test suite of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <QCloseEvent>
43 #include <QFileDialog>
44 #include <QFileInfo>
45 #include <QHeaderView>
46 #include <QMenu>
47 #include <QMessageBox>
48 #include <QProcess>
49 #include <QSettings>
50 #include <QTextStream>
51
52 #include "ASTItem.h"
53 #include "FunctionSignaturesView.h"
54 #include "Global.h"
55 #include "TestCaseView.h"
56 #include "TestResultView.h"
57 #include "TestSuite.h"
58 #include "TreeModel.h"
59 #include "TreeSortFilter.h"
60 #include "UserTestCase.h"
61
62 #include "MainWindow.h"
63
64 using namespace QPatternistSDK;
65
66 MainWindow::MainWindow() : m_userTC(new UserTestCase()),
67                            m_currentTC(0)
68 {
69     setupUi(this);
70
71     /* I want to do this in Designer.. */
72     testSuiteView->header()->setSortIndicator(0, Qt::AscendingOrder);
73     testSuiteView->header()->setSortIndicatorShown(true);
74     testSuiteView->header()->setClickable(true);
75
76     setupActions();
77
78     QStringList suiteHeaders;
79     suiteHeaders << QLatin1String("Name")
80                  << QLatin1String("Pass")
81                  << QLatin1String("Fail")
82                  << QLatin1String("Total");
83
84     TreeSortFilter *const proxy = new TreeSortFilter(this);
85     connect(searchInput,    SIGNAL(textChanged(const QString &)),
86             proxy,          SLOT(setFilterFixedString(const QString &)));
87
88     proxy->setSourceModel(new TreeModel(suiteHeaders, this));
89     testSuiteView->setModel(proxy);
90
91     /* --------- Test Result View ---------- */
92     testResultView = new TestResultView(this);
93     testResultView->setAllowedAreas(Qt::AllDockWidgetAreas);
94     addDockWidget(Qt::RightDockWidgetArea, testResultView);
95     /* ----------------------------------- */
96
97     /* --------- Test Case View ---------- */
98     testCaseView = new TestCaseView(this);
99     testCaseView->setAllowedAreas(Qt::AllDockWidgetAreas);
100     addDockWidget(Qt::LeftDockWidgetArea, testCaseView);
101
102     connect(this,           SIGNAL(testCaseSelected(TestCase *const)),
103             testCaseView,   SLOT(displayTestCase(TestCase *const)));
104     connect(this,           SIGNAL(testCaseSelected(TestCase *const)),
105             testResultView, SLOT(displayTestResult(TestCase *const)));
106     connect(focusURI,       SIGNAL(textChanged(const QString &)),
107             m_userTC,       SLOT(focusDocumentChanged(const QString &)));
108     /* ----------------------------------- */
109
110     /* ----- Function Signature View ----- */
111     functionView = new FunctionSignaturesView(this);
112     functionView->setAllowedAreas(Qt::AllDockWidgetAreas);
113     addDockWidget(Qt::RightDockWidgetArea, functionView);
114     /* ----------------------------------- */
115
116     /* Appears here, because the menu uses actions in the QDockWidgets. */
117     setupMenu();
118
119     readSettings();
120
121     /* Connect this after readSettings(), otherwise readSettings() triggers writeSettings(). */
122     connect(sourceTab,      SIGNAL(currentChanged(int)),
123                             SLOT(writeSettings()));
124     connect(testSuiteView,  SIGNAL(clicked(const QModelIndex &)),
125                             SLOT(writeSettings()));
126     connect(sourceInput,    SIGNAL(textChanged()),
127                             SLOT(writeSettings()));
128 }
129
130 MainWindow::~MainWindow()
131 {
132     delete m_userTC;
133 }
134
135 QModelIndex MainWindow::sourceIndex(const QModelIndex &proxyIndex) const
136 {
137     return static_cast<TreeSortFilter *>(testSuiteView->model())->mapToSource(proxyIndex);
138 }
139
140 TreeModel *MainWindow::sourceModel() const
141 {
142     const TreeSortFilter *const proxy = static_cast<TreeSortFilter *>(testSuiteView->model());
143     return static_cast<TreeModel *>(proxy->sourceModel());
144 }
145
146 void MainWindow::on_testSuiteView_clicked(const QModelIndex &index)
147 {
148     if(index.isValid())
149     {
150         TestItem *const node = static_cast<TestItem *>(sourceIndex(index).internalPointer());
151         Q_ASSERT(node);
152
153         if(node->isFinalNode())
154         {
155             m_currentTC = static_cast<TestCase *>(node);
156             testCaseSelected(m_currentTC);
157             return;
158         }
159     }
160
161     /* In all other cases: */
162     m_currentTC = 0;
163     testCaseSelected(0);
164 }
165
166 void MainWindow::on_sourceInput_textChanged()
167 {
168     m_userTC->setSourceCode(sourceInput->toPlainText());
169 }
170
171 void MainWindow::on_actionOpen_triggered()
172 {
173     const QString fileName(QFileDialog::getOpenFileName(this,
174                                                         QLatin1String("Open Test Suite Catalog"),
175                                                         m_previousOpenedCatalog.toLocalFile(),
176                                                         QLatin1String("Test Suite Catalog file (*.xml)")));
177
178     /* "If the user presses Cancel, it returns a null string." */
179     if(fileName.isNull())
180         return;
181
182     m_currentSuiteType = TestSuite::XQuerySuite;
183     openCatalog(QUrl::fromLocalFile(fileName), true, TestSuite::XQuerySuite);
184 }
185
186 void MainWindow::on_actionOpenXSLTSCatalog_triggered()
187 {
188     const QString fileName(QFileDialog::getOpenFileName(this,
189                                                         QLatin1String("Open Test Suite Catalog"),
190                                                         m_previousOpenedCatalog.toLocalFile(),
191                                                         QLatin1String("Test Suite Catalog file (*.xml)")));
192
193     /* "If the user presses Cancel, it returns a null string." */
194     if(fileName.isNull())
195         return;
196
197     m_currentSuiteType = TestSuite::XsltSuite;
198     openCatalog(QUrl::fromLocalFile(fileName), true, TestSuite::XsltSuite);
199 }
200
201 void MainWindow::on_actionOpenXSDTSCatalog_triggered()
202 {
203     const QString fileName(QFileDialog::getOpenFileName(this,
204                                                         QLatin1String("Open Test Suite Catalog"),
205                                                         m_previousOpenedCatalog.toLocalFile(),
206                                                         QLatin1String("Test Suite Catalog file (*.xml)")));
207
208     /* "If the user presses Cancel, it returns a null string." */
209     if(fileName.isNull())
210         return;
211
212     m_currentSuiteType = TestSuite::XsdSuite;
213     openCatalog(QUrl::fromLocalFile(fileName), true, TestSuite::XsdSuite);
214 }
215
216 void MainWindow::openCatalog(const QUrl &fileName,
217                              const bool reportError,
218                              const TestSuite::SuiteType suiteType)
219 {
220     setCurrentFile(fileName);
221     m_previousOpenedCatalog = fileName;
222
223     QString errorMsg;
224     TestSuite *const loadedSuite = TestSuite::openCatalog(fileName, errorMsg, false, suiteType);
225
226     if(!loadedSuite)
227     {
228         if(reportError)
229         {
230             QMessageBox::information(this, QLatin1String("Failed to load catalog file"),
231                                      errorMsg, QMessageBox::Ok);
232         }
233
234         return;
235     }
236
237     TreeModel *const prevModel = sourceModel();
238     prevModel->setRoot(loadedSuite);
239     m_currentTC = 0;
240
241     testCaseCount->setText(QString::number(loadedSuite->resultSummary().second));
242     /* Switch to the tab containing the loaded test suite. */
243     sourceTab->setCurrentIndex(0);
244
245     setWindowTitle(QCoreApplication::applicationName() +
246                    QLatin1String(" -- ") +
247                    QFileInfo(fileName.toLocalFile()).fileName());
248
249     /* @p reportError is set when not auto-loading on startup, and
250      * we only want to save when the user opens from the GUI. */
251     if(reportError)
252         writeSettings();
253 }
254
255 void MainWindow::on_sourceTab_currentChanged(int index)
256 {
257     if(index == 1)
258     {
259         m_currentTC = m_userTC;
260         testCaseSelected(m_userTC);
261     }
262     else
263         on_testSuiteView_clicked(testSuiteView->currentIndex());
264 }
265
266 void MainWindow::on_actionExecute_triggered()
267 {
268     Q_ASSERT(testCaseView);
269     TestSuite *const ts = static_cast<TestSuite *>(sourceModel()->root());
270
271     const TestItem::ExecutionStage stage = compileOnly->isChecked() ? TestItem::CompileOnly
272                                                                     : TestItem::CompileAndRun;
273
274     m_userTC->setLanguage(isXSLT20->isChecked() ? QXmlQuery::XSLT20 : QXmlQuery::XQuery10);
275
276     if(m_currentTC)
277     {
278         const TestResult::List rlist(m_currentTC->execute(stage, ts));
279         Q_ASSERT(rlist.count() == 1);
280         const TestResult *const result = rlist.first();
281         Q_ASSERT(result);
282         testResultView->displayTestResult(result);
283     }
284     else
285     {
286         const QModelIndexList indexes = testSuiteView->selectionModel()->selectedIndexes();
287         for (int i = 0; i < indexes.count(); ++i) {
288             const QModelIndex source(sourceIndex(indexes.at(i)));
289
290             TestItem *const ti = static_cast<TestItem *>(source.internalPointer());
291             if(!ti)
292                 return;
293
294             /* ti is a TestGroup. It now executes its children, changed(TreeItem *) signals is
295              * emitted which the view receives, and thus updates. */
296             ti->execute(stage, ts);
297         }
298     }
299 }
300
301 void MainWindow::readSettings()
302 {
303     QSettings settings;
304
305     settings.beginGroup(QLatin1String("MainWindow"));
306     restoreState(settings.value(QLatin1String("state")).toByteArray(), Global::versionNumber);
307     resize(settings.value(QLatin1String("size"), QSize(400, 400)).toSize());
308     move(settings.value(QLatin1String("pos"), QPoint(200, 200)).toPoint());
309     m_previousOpenedCatalog = settings.value(QLatin1String("PreviousOpenedCatalogFile")).toUrl();
310     focusURI->setText(settings.value(QLatin1String("focusURI")).toString());
311     isXSLT20->setChecked(settings.value(QLatin1String("isXSLT20")).toBool());
312     compileOnly->setChecked(settings.value(QLatin1String("compileOnly")).toBool());
313     m_currentSuiteType = (TestSuite::SuiteType)settings.value(QLatin1String("PreviousSuiteType"), isXSLT20->isChecked() ? TestSuite::XsltSuite : TestSuite::XQuerySuite).toInt();
314
315     /* Open the previously opened catalog. */
316     if(!m_previousOpenedCatalog.isEmpty())
317     {
318         openCatalog(m_previousOpenedCatalog, false, m_currentSuiteType);
319     }
320
321     sourceInput->setPlainText(settings.value(QLatin1String("sourceInput")).toString());
322     testResultView->resultViewSelection->setCurrentIndex(
323             settings.value(QLatin1String("ResultViewMethod"), 0).toInt());
324     testResultView->outputStack->setCurrentIndex(settings.value(
325             QLatin1String("ResultViewMethod"), 0).toInt());
326
327     /* Restore the selected test case/group. */
328     const QStringList rows(settings.value(QLatin1String("SelectedTestSuiteRow"),
329                                           QString())
330                            .toString().split(QLatin1Char(',')));
331
332     if(!rows.isEmpty()) /* Ok, we have a selection. */
333     {
334         QAbstractItemModel *const model = testSuiteView->model();
335         Q_ASSERT(model);
336         QModelIndex p;
337
338         for(int i = rows.count() - 1; i >= 0;  --i)
339         {
340             const QModelIndex childIndex(model->index(rows.at(i).toInt(), 0 , p));
341
342             if(childIndex.isValid())
343             {
344                 testSuiteView->scrollTo(p); /* Work around for Qt issue #87575. */
345                 p = childIndex;
346             }
347         }
348
349         testSuiteView->scrollTo(p); /* Scrolls to it. */
350         testSuiteView->setCurrentIndex(p); /* Selects it. */
351         on_testSuiteView_clicked(p); /* Loads the test case in the Test Case View. */
352     }
353
354     /* Do it here. In this way the user-entered test case gets selected, if that tab
355      * was previously used. */
356     sourceTab->setCurrentIndex(settings.value(QLatin1String("SelectedTab"), 0).toInt());
357     on_sourceTab_currentChanged(sourceTab->currentIndex());
358
359     settings.endGroup();
360 }
361
362 void MainWindow::writeSettings()
363 {
364     QSettings settings;
365
366     settings.beginGroup(QLatin1String("MainWindow"));
367     settings.setValue(QLatin1String("state"), saveState(Global::versionNumber));
368     settings.setValue(QLatin1String("pos"), pos());
369     settings.setValue(QLatin1String("size"), size());
370     settings.setValue(QLatin1String("sourceInput"), sourceInput->toPlainText());
371     settings.setValue(QLatin1String("PreviousOpenedCatalogFile"), m_previousOpenedCatalog);
372     settings.setValue(QLatin1String("PreviousSuiteType"), m_currentSuiteType);
373     settings.setValue(QLatin1String("SelectedTab"), sourceTab->currentIndex());
374     settings.setValue(QLatin1String("ResultViewMethod"),
375                       testResultView->resultViewSelection->currentIndex());
376     settings.setValue(QLatin1String("focusURI"),
377                       focusURI->text());
378     settings.setValue(QLatin1String("isXSLT20"),
379                       isXSLT20->isChecked());
380     settings.setValue(QLatin1String("compileOnly"),
381                       compileOnly->isChecked());
382
383     /* Store the selected test case/group. */
384     QModelIndex selected(sourceIndex(testSuiteView->currentIndex()));
385     if(selected.isValid())
386     {
387         QString result;
388
389         do
390         {
391             result.append(QString::number(selected.row()));
392             selected = selected.parent();
393
394             if(selected.isValid())
395                 result.append(QLatin1Char(','));
396             else
397                 break;
398         }
399         while(true);
400
401         settings.setValue(QLatin1String("SelectedTestSuiteRow"), result);
402     }
403
404     settings.endGroup();
405 }
406
407 void MainWindow::setCurrentFile(const QUrl &f)
408 {
409     const QString fileName(f.toLocalFile());
410     QSettings settings;
411     settings.beginGroup(QLatin1String("MainWindow"));
412     QStringList files(settings.value(QLatin1String("RecentFileList")).toStringList());
413
414     files.removeAll(fileName);
415     files.prepend(fileName);
416     while(files.size() > MaximumRecentFiles)
417         files.removeLast();
418
419     settings.setValue(QLatin1String("RecentFileList"), files);
420     settings.endGroup();
421
422     updateRecentFileActions();
423 }
424
425 void MainWindow::updateRecentFileActions()
426 {
427     QSettings settings;
428     settings.beginGroup(QLatin1String("MainWindow"));
429     const QStringList files(settings.value(QLatin1String("RecentFileList")).toStringList());
430     settings.endGroup();
431
432     const int numRecentFiles = qMin(files.size(), static_cast<int>(MaximumRecentFiles));
433
434     for(int i = 0; i < numRecentFiles; ++i)
435     {
436         const QString text(QString::fromLatin1("&%1 %2").arg(i + 1).arg(QFileInfo(files[i]).filePath()));
437         m_recentFileActs[i]->setText(text);
438         m_recentFileActs[i]->setData(QUrl::fromLocalFile(files[i]));
439         m_recentFileActs[i]->setVisible(true);
440     }
441
442     for(int j = numRecentFiles; j < MaximumRecentFiles; ++j)
443         m_recentFileActs[j]->setVisible(false);
444 }
445
446 void MainWindow::openRecentFile()
447 {
448     const QAction *const action = qobject_cast<QAction *>(sender());
449     if(action)
450         openCatalog(action->data().toUrl(), true, TestSuite::XQuerySuite);
451 }
452
453 void MainWindow::closeEvent(QCloseEvent *ev)
454 {
455     writeSettings();
456     ev->accept();
457 }
458
459 void MainWindow::setupActions()
460 {
461     connect(actionQuit, SIGNAL(triggered()), qApp, SLOT(closeAllWindows()));
462
463     for(int i = 0; i < MaximumRecentFiles; ++i)
464     {
465         m_recentFileActs[i] = new QAction(this);
466         m_recentFileActs[i]->setVisible(false);
467         connect(m_recentFileActs[i], SIGNAL(triggered()),
468                 this, SLOT(openRecentFile()));
469     }
470 }
471
472 void MainWindow::setupMenu()
473 {
474     QMenu *const menFile = findChild<QMenu *>(QLatin1String("menuFile"));
475     Q_ASSERT(menFile);
476     QAction *const actOpen = findChild<QAction *>(QLatin1String("actionExecute"));
477     Q_ASSERT(actOpen);
478     QMenu *const recent = new QMenu(QLatin1String("O&pen Recent"), this);
479
480     menFile->insertMenu(actOpen, recent);
481     menFile->insertSeparator(actOpen);
482
483     for(int i = 0; i < MaximumRecentFiles; ++i)
484         recent->addAction(m_recentFileActs[i]);
485
486     updateRecentFileActions();
487
488     QMenu *const menWindows = findChild<QMenu *>(QLatin1String("menuWindows"));
489     Q_ASSERT(menWindows);
490
491     menWindows->addAction(testCaseView->toggleViewAction());
492     menWindows->addAction(testResultView->toggleViewAction());
493     menWindows->addAction(functionView->toggleViewAction());
494 }
495
496 void MainWindow::on_actionRestart_triggered()
497 {
498     if(QProcess::startDetached(QCoreApplication::applicationFilePath()))
499         QApplication::closeAllWindows();
500     else
501     {
502         QTextStream err(stderr);
503         err << "Failed to start " << qPrintable(QCoreApplication::applicationFilePath()) << endl;
504     }
505 }
506
507
508 // vim: et:ts=4:sw=4:sts=4