|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 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 tools applications of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "graphics.h" |
|
43 #include "feature.h" |
|
44 #include "featuretreemodel.h" |
|
45 |
|
46 #include <QtGui> |
|
47 |
|
48 QT_BEGIN_NAMESPACE |
|
49 |
|
50 static QString defaultPath; |
|
51 |
|
52 class FeatureTextBrowser : public QTextBrowser { |
|
53 Q_OBJECT |
|
54 public: |
|
55 FeatureTextBrowser(QWidget *parent) : QTextBrowser(parent) { |
|
56 QString docRoot; |
|
57 docRoot = QLibraryInfo::location(QLibraryInfo::DocumentationPath) |
|
58 + "/html"; |
|
59 setSearchPaths(searchPaths() << docRoot); |
|
60 } |
|
61 |
|
62 signals: |
|
63 void featureClicked(const QString &feature); |
|
64 |
|
65 public slots: |
|
66 void setSource(const QUrl &url) |
|
67 { |
|
68 if (url.scheme() == "feature") |
|
69 emit featureClicked(url.authority()); |
|
70 else |
|
71 QTextBrowser::setSource(url); |
|
72 } |
|
73 }; |
|
74 |
|
75 class Main : public QMainWindow { |
|
76 Q_OBJECT |
|
77 public: |
|
78 Main(); |
|
79 ~Main(); |
|
80 void loadFeatures(const QString& filename); |
|
81 void loadConfig(const QString& filename); |
|
82 |
|
83 public slots: |
|
84 void modelChanged(); |
|
85 void showInfo(const QModelIndex &index); |
|
86 void showInfo(const QString &feature); |
|
87 void openConfig(); |
|
88 void saveConfig(); |
|
89 void expandView(); |
|
90 void collapseView(); |
|
91 void about(); |
|
92 void aboutQt(); |
|
93 void quit(); |
|
94 void clear(); |
|
95 void enableAll(); |
|
96 void disableAll(); |
|
97 |
|
98 private: |
|
99 QTextBrowser *textBrowser; |
|
100 QTreeView *featureTree; |
|
101 FeatureTreeModel *featureModel; |
|
102 |
|
103 void init(); |
|
104 void updateStatus(int numFeatures = -1); |
|
105 void completelyExpandIndex(const QModelIndex &parent); |
|
106 }; |
|
107 |
|
108 template<typename Func> |
|
109 void foreachIndex_helper(const QModelIndex &parent, Func func) |
|
110 { |
|
111 const QAbstractItemModel *model = parent.model(); |
|
112 const int rows = model->rowCount(parent); |
|
113 for (int i = 0; i < rows; ++i) { |
|
114 const QModelIndex child = model->index(i, 0, parent); |
|
115 func(child); |
|
116 foreachIndex_helper(child, func); |
|
117 } |
|
118 } |
|
119 |
|
120 template<typename Func> |
|
121 void foreachIndex(const QAbstractItemModel *model, Func func) |
|
122 { |
|
123 const int rows = model->rowCount(QModelIndex()); |
|
124 for (int i = 0; i < rows; ++i) { |
|
125 const QModelIndex child = model->index(i, 0, QModelIndex()); |
|
126 func(child); |
|
127 foreachIndex_helper(child, func); |
|
128 } |
|
129 } |
|
130 |
|
131 struct CheckStateSetter { |
|
132 |
|
133 CheckStateSetter(Qt::CheckState state, QAbstractItemModel *m) |
|
134 : checkState(state), model(m) {} |
|
135 |
|
136 void operator()(const QModelIndex &index) { |
|
137 model->setData(index, checkState, Qt::CheckStateRole); |
|
138 } |
|
139 |
|
140 Qt::CheckState checkState; |
|
141 QAbstractItemModel *model; |
|
142 }; |
|
143 |
|
144 void Main::disableAll() |
|
145 { |
|
146 QAbstractItemModel *model = featureTree->model(); |
|
147 foreachIndex(model, CheckStateSetter(Qt::Unchecked, model)); |
|
148 } |
|
149 |
|
150 void Main::enableAll() |
|
151 { |
|
152 QAbstractItemModel *model = featureTree->model(); |
|
153 foreachIndex(model, CheckStateSetter(Qt::Checked, model)); |
|
154 } |
|
155 |
|
156 Main::Main() |
|
157 { |
|
158 setWindowIcon(QIcon(QPixmap(logo_xpm))); |
|
159 |
|
160 QSplitter *splitter = new QSplitter(this); |
|
161 |
|
162 featureModel = new FeatureTreeModel(this); |
|
163 featureTree = new QTreeView(splitter); |
|
164 splitter->addWidget(featureTree); |
|
165 featureTree->setRootIsDecorated(true); |
|
166 featureTree->setModel(featureModel); |
|
167 featureTree->show(); |
|
168 |
|
169 textBrowser = new FeatureTextBrowser(splitter); |
|
170 textBrowser->setFrameStyle(QFrame::WinPanel|QFrame::Sunken); |
|
171 splitter->addWidget(textBrowser); |
|
172 textBrowser->show(); |
|
173 |
|
174 connect(textBrowser, SIGNAL(featureClicked(QString)), |
|
175 this, SLOT(showInfo(QString))); |
|
176 connect(featureTree, SIGNAL(activated(QModelIndex)), |
|
177 this, SLOT(showInfo(QModelIndex))); |
|
178 connect(featureModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), |
|
179 this, SLOT(modelChanged())); |
|
180 connect(featureTree, SIGNAL(clicked(QModelIndex)), |
|
181 this, SLOT(showInfo(QModelIndex))); |
|
182 |
|
183 setCentralWidget(splitter); |
|
184 |
|
185 QMenu *file = menuBar()->addMenu("&File"); |
|
186 file->addAction("&Open...", this, SLOT(openConfig()), |
|
187 Qt::CTRL + Qt::Key_O); |
|
188 file->addAction("&Save As...", this, SLOT(saveConfig()), |
|
189 Qt::CTRL + Qt::Key_S); |
|
190 file->addSeparator(); |
|
191 file->addAction("&Reset", this, SLOT(clear())); |
|
192 file->addSeparator(); |
|
193 file->addAction("E&xit", this, SLOT(quit()), Qt::CTRL + Qt::Key_Q); |
|
194 |
|
195 QMenu *edit = menuBar()->addMenu("&Tools"); |
|
196 edit->addAction("&Enable all features", this, SLOT(enableAll())); |
|
197 edit->addAction("&Disable all features", this, SLOT(disableAll())); |
|
198 |
|
199 menuBar()->addSeparator(); |
|
200 |
|
201 QMenu *help = menuBar()->addMenu("&Help"); |
|
202 help->addAction("&About", this, SLOT(about())); |
|
203 help->addAction("About &Qt", this, SLOT(aboutQt())); |
|
204 |
|
205 QToolBar *tb = new QToolBar("Expand/Collapse features"); |
|
206 QToolButton *button; |
|
207 |
|
208 button = new QToolButton(tb); |
|
209 button->setIcon(QIcon(QPixmap(collapsed_xpm))); |
|
210 button->setText("Collapse"); |
|
211 button->setToolTip("Collapse"); |
|
212 connect(button, SIGNAL(clicked()), this, SLOT(collapseView())); |
|
213 tb->addWidget(button); |
|
214 |
|
215 button = new QToolButton(tb); |
|
216 button->setIcon(QIcon(QPixmap(expanded_xpm))); |
|
217 button->setText("Expand"); |
|
218 button->setToolTip("Expand"); |
|
219 connect(button, SIGNAL(clicked()), this, SLOT(expandView())); |
|
220 tb->addWidget(button); |
|
221 addToolBar(tb); |
|
222 |
|
223 init(); |
|
224 } |
|
225 |
|
226 Main::~Main() |
|
227 { |
|
228 delete textBrowser; |
|
229 delete featureModel; |
|
230 delete featureTree; |
|
231 } |
|
232 |
|
233 void Main::clear() |
|
234 { |
|
235 QSettings settings; |
|
236 settings.clear(); |
|
237 featureModel->clear(); |
|
238 featureTree->reset(); |
|
239 init(); |
|
240 } |
|
241 |
|
242 void Main::quit() |
|
243 { |
|
244 if (isWindowModified()) { |
|
245 int button = QMessageBox::question(this, "Quit Program", |
|
246 "You have unsaved changes.\n" |
|
247 "Do you want to quit anyway?", |
|
248 QMessageBox::Yes, |
|
249 QMessageBox::No); |
|
250 if (static_cast<QMessageBox::Button>(button) != QMessageBox::Yes) |
|
251 return; |
|
252 } |
|
253 QApplication::instance()->quit(); |
|
254 } |
|
255 |
|
256 /* |
|
257 Recursively expand expand \a parent and all of its children. |
|
258 */ |
|
259 void Main::completelyExpandIndex(const QModelIndex &parent) |
|
260 { |
|
261 featureTree->setExpanded(parent, true); |
|
262 |
|
263 const QAbstractItemModel *model = featureTree->model(); |
|
264 const int rows = model->rowCount(parent); |
|
265 for (int i = 0; i < rows; ++i) |
|
266 completelyExpandIndex(model->index(i, 0, parent)); |
|
267 } |
|
268 |
|
269 void Main::expandView() |
|
270 { |
|
271 completelyExpandIndex(QModelIndex()); |
|
272 } |
|
273 |
|
274 void Main::collapseView() |
|
275 { |
|
276 const QAbstractItemModel *model = featureTree->model(); |
|
277 const int rows = model->rowCount(QModelIndex()); |
|
278 for (int i = 0; i < rows; ++i) { |
|
279 QModelIndex index = model->index(i, 0, QModelIndex()); |
|
280 featureTree->setExpanded(index, false); |
|
281 } |
|
282 } |
|
283 |
|
284 void Main::updateStatus(int numFeatures) |
|
285 { |
|
286 QSettings settings; |
|
287 QString featureFile = settings.value("featureFile").toString(); |
|
288 QString configFile = settings.value("lastConfig").toString(); |
|
289 QString message("Using features from %1"); |
|
290 |
|
291 if (numFeatures >= 0) { |
|
292 QString s("%1 features loaded from %2"); |
|
293 statusBar()->showMessage(s.arg(numFeatures).arg(featureFile)); |
|
294 } |
|
295 QString appName = QApplication::applicationName(); |
|
296 if (configFile.isEmpty()) |
|
297 configFile = "New File"; |
|
298 setWindowTitle(appName + " - " + configFile + "[*]"); |
|
299 } |
|
300 |
|
301 void Main::modelChanged() |
|
302 { |
|
303 setWindowModified(true); |
|
304 } |
|
305 |
|
306 void Main::init() |
|
307 { |
|
308 QSettings settings; |
|
309 QString features = settings.value("featureFile").toString(); |
|
310 |
|
311 if (features.isEmpty() || !QFileInfo(features).isFile()) { |
|
312 features = QFileDialog::getOpenFileName(this, |
|
313 "Open a feature file", |
|
314 defaultPath, |
|
315 "Qt Features (qfeatures.txt)"); |
|
316 } |
|
317 settings.setValue("featureFile", features); |
|
318 loadFeatures(features); |
|
319 |
|
320 expandView(); |
|
321 collapseView(); |
|
322 |
|
323 QString confFile = settings.value("lastConfig").toString(); |
|
324 if (confFile.isEmpty()) |
|
325 return; |
|
326 loadConfig(confFile); |
|
327 } |
|
328 |
|
329 void Main::openConfig() |
|
330 { |
|
331 QSettings settings; |
|
332 QString configDir; |
|
333 |
|
334 QString prevFile = settings.value("lastConfig").toString(); |
|
335 if (!prevFile.isEmpty()) |
|
336 configDir = QFileInfo(prevFile).path(); |
|
337 |
|
338 if (configDir.isEmpty()) |
|
339 configDir = defaultPath; |
|
340 |
|
341 QString configFile; |
|
342 configFile = QFileDialog::getOpenFileName(this, |
|
343 "Open a configuration file", |
|
344 configDir, |
|
345 "Header files (*.h)"); |
|
346 enableAll(); |
|
347 if (!configFile.isEmpty()) |
|
348 loadConfig(configFile); |
|
349 settings.setValue("lastConfig", QFileInfo(configFile).absoluteFilePath()); |
|
350 } |
|
351 |
|
352 void Main::saveConfig() |
|
353 { |
|
354 QSettings settings; |
|
355 QString configDir; |
|
356 |
|
357 QString prevFile = settings.value("lastConfig").toString(); |
|
358 if (!prevFile.isEmpty()) |
|
359 configDir = QFileInfo(prevFile).path(); |
|
360 |
|
361 if (configDir.isEmpty()) |
|
362 configDir = defaultPath; |
|
363 |
|
364 QString configFile; |
|
365 configFile = QFileDialog::getSaveFileName(this, |
|
366 "Save configuration file", |
|
367 configDir, |
|
368 "Header files (*.h)"); |
|
369 if (configFile.isEmpty()) |
|
370 return; |
|
371 |
|
372 QFile file(configFile); |
|
373 if (!file.open(QIODevice::WriteOnly)) { |
|
374 QMessageBox::warning(this,"Warning", |
|
375 "Cannot write to file " + configFile); |
|
376 return; |
|
377 } |
|
378 |
|
379 QTextStream stream(&file); |
|
380 FeatureTreeModel *model; |
|
381 model = static_cast<FeatureTreeModel*>(featureTree->model()); |
|
382 model->writeConfig(stream); |
|
383 |
|
384 settings.setValue("lastConfig", QFileInfo(configFile).absoluteFilePath()); |
|
385 setWindowModified(false); |
|
386 updateStatus(); |
|
387 } |
|
388 |
|
389 void Main::loadConfig(const QString &filename) |
|
390 { |
|
391 if (!QFileInfo(filename).isFile()) |
|
392 return; |
|
393 |
|
394 QFile file(filename); |
|
395 if (!file.open(QIODevice::ReadOnly)) { |
|
396 QMessageBox::warning(this,"Warning", "Cannot open file " + filename); |
|
397 return; |
|
398 } |
|
399 |
|
400 QTextStream stream(&file); |
|
401 FeatureTreeModel *model; |
|
402 model = static_cast<FeatureTreeModel*>(featureTree->model()); |
|
403 model->readConfig(stream); |
|
404 |
|
405 QSettings settings; |
|
406 settings.setValue("lastConfig", QFileInfo(filename).absoluteFilePath()); |
|
407 setWindowModified(false); |
|
408 updateStatus(); |
|
409 } |
|
410 |
|
411 void Main::loadFeatures(const QString &filename) |
|
412 { |
|
413 Feature::clear(); |
|
414 |
|
415 QFile file(filename); |
|
416 if (!file.open(QIODevice::ReadOnly)) { |
|
417 QMessageBox::warning(this,"Warning", "Cannot open file " + filename); |
|
418 return; |
|
419 } |
|
420 |
|
421 Feature *feature = 0; |
|
422 int numFeatures = 0; |
|
423 updateStatus(numFeatures); |
|
424 QTextStream s(&file); |
|
425 for (QString line = s.readLine(); !s.atEnd(); line = s.readLine()) { |
|
426 line = line.simplified(); |
|
427 if (line.isEmpty()) |
|
428 continue; |
|
429 if (line.startsWith('#')) |
|
430 continue; |
|
431 |
|
432 int colon = line.indexOf(':'); |
|
433 if (colon < 0) { // assume description |
|
434 QString description = feature->description().simplified(); |
|
435 description += " " + line; |
|
436 feature->setDescription(description); |
|
437 continue; |
|
438 } |
|
439 |
|
440 QString tag = line.left(colon); |
|
441 QString value = line.mid(colon+1).simplified(); |
|
442 if (tag == "Feature") { |
|
443 if (feature) |
|
444 featureModel->addFeature(feature); |
|
445 feature = Feature::getInstance(value); |
|
446 updateStatus(++numFeatures); |
|
447 } else if (tag == "Requires") { |
|
448 Q_ASSERT(feature); |
|
449 feature->setDependencies(value.split(' ', QString::SkipEmptyParts)); |
|
450 } else if (tag == "Name") { |
|
451 Q_ASSERT(feature); |
|
452 feature->setTitle(value); |
|
453 } else if (tag == "Section") { |
|
454 Q_ASSERT(feature); |
|
455 feature->setSection(value); |
|
456 } else if (tag == "SeeAlso") { |
|
457 Q_ASSERT(feature); |
|
458 feature->setRelations(value.split(' ', QString::SkipEmptyParts)); |
|
459 } else if (tag == "Description") { |
|
460 Q_ASSERT(feature); |
|
461 feature->setDescription(value); |
|
462 } |
|
463 } |
|
464 if (feature) |
|
465 featureModel->addFeature(feature); |
|
466 |
|
467 featureTree->resizeColumnToContents(0); |
|
468 |
|
469 QSettings settings; |
|
470 settings.setValue("featureFile", QFileInfo(filename).absoluteFilePath()); |
|
471 |
|
472 updateStatus(); |
|
473 } |
|
474 |
|
475 void Main::showInfo(const QModelIndex &index) |
|
476 { |
|
477 FeatureTreeModel *model; |
|
478 model = static_cast<FeatureTreeModel*>(featureTree->model()); |
|
479 |
|
480 if (const Feature *feature = model->getFeature(index)) |
|
481 textBrowser->setHtml(feature->toHtml()); |
|
482 |
|
483 // Ensure index is visible |
|
484 QModelIndex parent = model->parent(index); |
|
485 while (parent.isValid()) { |
|
486 featureTree->setExpanded(parent, true); |
|
487 parent = model->parent(parent); |
|
488 } |
|
489 |
|
490 featureTree->scrollTo(index); |
|
491 featureTree->setCurrentIndex(index); |
|
492 } |
|
493 |
|
494 void Main::showInfo(const QString &feature) |
|
495 { |
|
496 const Feature *f = Feature::getInstance(feature); |
|
497 FeatureTreeModel *model; |
|
498 model = static_cast<FeatureTreeModel*>(featureTree->model()); |
|
499 showInfo(model->index(f)); |
|
500 } |
|
501 |
|
502 void Main::about() |
|
503 { |
|
504 QMessageBox::about(this, "About qconfig", |
|
505 "<p><b><font size=\"+2\">Qtopia Core build configuration</font></b></p>" |
|
506 "<p></p>" |
|
507 "<p>Version 2.0</p>" |
|
508 "<p>Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).</p>" |
|
509 "<p></p>" |
|
510 ); |
|
511 } |
|
512 |
|
513 void Main::aboutQt() |
|
514 { |
|
515 QMessageBox::aboutQt( this, tr("qconfig") ); |
|
516 } |
|
517 |
|
518 QT_END_NAMESPACE |
|
519 |
|
520 int main(int argc, char** argv) |
|
521 { |
|
522 QT_USE_NAMESPACE |
|
523 QApplication app(argc,argv); |
|
524 app.setOrganizationDomain("trolltech.com"); |
|
525 app.setOrganizationName("Trolltech"); |
|
526 app.setApplicationName("QConfig"); |
|
527 Main m; |
|
528 |
|
529 defaultPath = QLibraryInfo::location(QLibraryInfo::PrefixPath) |
|
530 + "/src/corelib/global"; |
|
531 |
|
532 for (int i = 1; i < argc; ++i) { |
|
533 QString arg = argv[i]; |
|
534 if (arg == "-f" && i+1 < argc) |
|
535 m.loadFeatures(argv[++i]); |
|
536 else if (arg == "-c" && i+1 < argc) |
|
537 m.loadConfig(argv[++i]); |
|
538 } |
|
539 m.resize(m.sizeHint() + QSize(500,300)); |
|
540 m.show(); |
|
541 return app.exec(); |
|
542 } |
|
543 |
|
544 #include "main.moc" |