| /**************************************************************************** |
| ** |
| ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). |
| ** All rights reserved. |
| ** Contact: Nokia Corporation (qt-info@nokia.com) |
| ** |
| ** This file is part of the tools applications of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** GNU Lesser General Public License Usage |
| ** This file may be used under the terms of the GNU Lesser General Public |
| ** License version 2.1 as published by the Free Software Foundation and |
| ** appearing in the file LICENSE.LGPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU Lesser |
| ** General Public License version 2.1 requirements will be met: |
| ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
| ** |
| ** In addition, as a special exception, Nokia gives you certain additional |
| ** rights. These rights are described in the Nokia Qt LGPL Exception |
| ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU General |
| ** Public License version 3.0 as published by the Free Software Foundation |
| ** and appearing in the file LICENSE.GPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU General |
| ** Public License version 3.0 requirements will be met: |
| ** http://www.gnu.org/copyleft/gpl.html. |
| ** |
| ** Other Usage |
| ** Alternatively, this file may be used in accordance with the terms and |
| ** conditions contained in a signed written agreement between you and Nokia. |
| ** |
| ** |
| ** |
| ** |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "graphics.h" |
| #include "feature.h" |
| #include "featuretreemodel.h" |
| |
| #include <QtGui> |
| |
| QT_BEGIN_NAMESPACE |
| |
| static QString defaultPath; |
| |
| class FeatureTextBrowser : public QTextBrowser { |
| Q_OBJECT |
| public: |
| FeatureTextBrowser(QWidget *parent) : QTextBrowser(parent) { |
| QString docRoot; |
| docRoot = QLibraryInfo::location(QLibraryInfo::DocumentationPath) |
| + "/html"; |
| setSearchPaths(searchPaths() << docRoot); |
| } |
| |
| signals: |
| void featureClicked(const QString &feature); |
| |
| public slots: |
| void setSource(const QUrl &url) |
| { |
| if (url.scheme() == "feature") |
| emit featureClicked(url.authority()); |
| else |
| QTextBrowser::setSource(url); |
| } |
| }; |
| |
| class Main : public QMainWindow { |
| Q_OBJECT |
| public: |
| Main(); |
| ~Main(); |
| void loadFeatures(const QString& filename); |
| void loadConfig(const QString& filename); |
| |
| public slots: |
| void modelChanged(); |
| void showInfo(const QModelIndex &index); |
| void showInfo(const QString &feature); |
| void openConfig(); |
| void saveConfig(); |
| void expandView(); |
| void collapseView(); |
| void about(); |
| void aboutQt(); |
| void quit(); |
| void clear(); |
| void enableAll(); |
| void disableAll(); |
| |
| private: |
| QTextBrowser *textBrowser; |
| QTreeView *featureTree; |
| FeatureTreeModel *featureModel; |
| |
| void init(); |
| void updateStatus(int numFeatures = -1); |
| void completelyExpandIndex(const QModelIndex &parent); |
| }; |
| |
| template<typename Func> |
| void foreachIndex_helper(const QModelIndex &parent, Func func) |
| { |
| const QAbstractItemModel *model = parent.model(); |
| const int rows = model->rowCount(parent); |
| for (int i = 0; i < rows; ++i) { |
| const QModelIndex child = model->index(i, 0, parent); |
| func(child); |
| foreachIndex_helper(child, func); |
| } |
| } |
| |
| template<typename Func> |
| void foreachIndex(const QAbstractItemModel *model, Func func) |
| { |
| const int rows = model->rowCount(QModelIndex()); |
| for (int i = 0; i < rows; ++i) { |
| const QModelIndex child = model->index(i, 0, QModelIndex()); |
| func(child); |
| foreachIndex_helper(child, func); |
| } |
| } |
| |
| struct CheckStateSetter { |
| |
| CheckStateSetter(Qt::CheckState state, QAbstractItemModel *m) |
| : checkState(state), model(m) {} |
| |
| void operator()(const QModelIndex &index) { |
| model->setData(index, checkState, Qt::CheckStateRole); |
| } |
| |
| Qt::CheckState checkState; |
| QAbstractItemModel *model; |
| }; |
| |
| void Main::disableAll() |
| { |
| QAbstractItemModel *model = featureTree->model(); |
| foreachIndex(model, CheckStateSetter(Qt::Unchecked, model)); |
| } |
| |
| void Main::enableAll() |
| { |
| QAbstractItemModel *model = featureTree->model(); |
| foreachIndex(model, CheckStateSetter(Qt::Checked, model)); |
| } |
| |
| Main::Main() |
| { |
| setWindowIcon(QIcon(QPixmap(logo_xpm))); |
| |
| QSplitter *splitter = new QSplitter(this); |
| |
| featureModel = new FeatureTreeModel(this); |
| featureTree = new QTreeView(splitter); |
| splitter->addWidget(featureTree); |
| featureTree->setRootIsDecorated(true); |
| featureTree->setModel(featureModel); |
| featureTree->show(); |
| |
| textBrowser = new FeatureTextBrowser(splitter); |
| textBrowser->setFrameStyle(QFrame::WinPanel|QFrame::Sunken); |
| splitter->addWidget(textBrowser); |
| textBrowser->show(); |
| |
| connect(textBrowser, SIGNAL(featureClicked(QString)), |
| this, SLOT(showInfo(QString))); |
| connect(featureTree, SIGNAL(activated(QModelIndex)), |
| this, SLOT(showInfo(QModelIndex))); |
| connect(featureModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), |
| this, SLOT(modelChanged())); |
| connect(featureTree, SIGNAL(clicked(QModelIndex)), |
| this, SLOT(showInfo(QModelIndex))); |
| |
| setCentralWidget(splitter); |
| |
| QMenu *file = menuBar()->addMenu("&File"); |
| file->addAction("&Open...", this, SLOT(openConfig()), |
| Qt::CTRL + Qt::Key_O); |
| file->addAction("&Save As...", this, SLOT(saveConfig()), |
| Qt::CTRL + Qt::Key_S); |
| file->addSeparator(); |
| file->addAction("&Reset", this, SLOT(clear())); |
| file->addSeparator(); |
| file->addAction("E&xit", this, SLOT(quit()), Qt::CTRL + Qt::Key_Q); |
| |
| QMenu *edit = menuBar()->addMenu("&Tools"); |
| edit->addAction("&Enable all features", this, SLOT(enableAll())); |
| edit->addAction("&Disable all features", this, SLOT(disableAll())); |
| |
| menuBar()->addSeparator(); |
| |
| QMenu *help = menuBar()->addMenu("&Help"); |
| help->addAction("&About", this, SLOT(about())); |
| help->addAction("About &Qt", this, SLOT(aboutQt())); |
| |
| QToolBar *tb = new QToolBar("Expand/Collapse features"); |
| QToolButton *button; |
| |
| button = new QToolButton(tb); |
| button->setIcon(QIcon(QPixmap(collapsed_xpm))); |
| button->setText("Collapse"); |
| button->setToolTip("Collapse"); |
| connect(button, SIGNAL(clicked()), this, SLOT(collapseView())); |
| tb->addWidget(button); |
| |
| button = new QToolButton(tb); |
| button->setIcon(QIcon(QPixmap(expanded_xpm))); |
| button->setText("Expand"); |
| button->setToolTip("Expand"); |
| connect(button, SIGNAL(clicked()), this, SLOT(expandView())); |
| tb->addWidget(button); |
| addToolBar(tb); |
| |
| init(); |
| } |
| |
| Main::~Main() |
| { |
| delete textBrowser; |
| delete featureModel; |
| delete featureTree; |
| } |
| |
| void Main::clear() |
| { |
| QSettings settings; |
| settings.clear(); |
| featureModel->clear(); |
| featureTree->reset(); |
| init(); |
| } |
| |
| void Main::quit() |
| { |
| if (isWindowModified()) { |
| int button = QMessageBox::question(this, "Quit Program", |
| "You have unsaved changes.\n" |
| "Do you want to quit anyway?", |
| QMessageBox::Yes, |
| QMessageBox::No); |
| if (static_cast<QMessageBox::Button>(button) != QMessageBox::Yes) |
| return; |
| } |
| QApplication::instance()->quit(); |
| } |
| |
| /* |
| Recursively expand expand \a parent and all of its children. |
| */ |
| void Main::completelyExpandIndex(const QModelIndex &parent) |
| { |
| featureTree->setExpanded(parent, true); |
| |
| const QAbstractItemModel *model = featureTree->model(); |
| const int rows = model->rowCount(parent); |
| for (int i = 0; i < rows; ++i) |
| completelyExpandIndex(model->index(i, 0, parent)); |
| } |
| |
| void Main::expandView() |
| { |
| completelyExpandIndex(QModelIndex()); |
| } |
| |
| void Main::collapseView() |
| { |
| const QAbstractItemModel *model = featureTree->model(); |
| const int rows = model->rowCount(QModelIndex()); |
| for (int i = 0; i < rows; ++i) { |
| QModelIndex index = model->index(i, 0, QModelIndex()); |
| featureTree->setExpanded(index, false); |
| } |
| } |
| |
| void Main::updateStatus(int numFeatures) |
| { |
| QSettings settings; |
| QString featureFile = settings.value("featureFile").toString(); |
| QString configFile = settings.value("lastConfig").toString(); |
| QString message("Using features from %1"); |
| |
| if (numFeatures >= 0) { |
| QString s("%1 features loaded from %2"); |
| statusBar()->showMessage(s.arg(numFeatures).arg(featureFile)); |
| } |
| QString appName = QApplication::applicationName(); |
| if (configFile.isEmpty()) |
| configFile = "New File"; |
| setWindowTitle(appName + " - " + configFile + "[*]"); |
| } |
| |
| void Main::modelChanged() |
| { |
| setWindowModified(true); |
| } |
| |
| void Main::init() |
| { |
| QSettings settings; |
| QString features = settings.value("featureFile").toString(); |
| |
| if (features.isEmpty() || !QFileInfo(features).isFile()) { |
| features = QFileDialog::getOpenFileName(this, |
| "Open a feature file", |
| defaultPath, |
| "Qt Features (qfeatures.txt)"); |
| } |
| settings.setValue("featureFile", features); |
| loadFeatures(features); |
| |
| expandView(); |
| collapseView(); |
| |
| QString confFile = settings.value("lastConfig").toString(); |
| if (confFile.isEmpty()) |
| return; |
| loadConfig(confFile); |
| } |
| |
| void Main::openConfig() |
| { |
| QSettings settings; |
| QString configDir; |
| |
| QString prevFile = settings.value("lastConfig").toString(); |
| if (!prevFile.isEmpty()) |
| configDir = QFileInfo(prevFile).path(); |
| |
| if (configDir.isEmpty()) |
| configDir = defaultPath; |
| |
| QString configFile; |
| configFile = QFileDialog::getOpenFileName(this, |
| "Open a configuration file", |
| configDir, |
| "Header files (*.h)"); |
| enableAll(); |
| if (!configFile.isEmpty()) |
| loadConfig(configFile); |
| settings.setValue("lastConfig", QFileInfo(configFile).absoluteFilePath()); |
| } |
| |
| void Main::saveConfig() |
| { |
| QSettings settings; |
| QString configDir; |
| |
| QString prevFile = settings.value("lastConfig").toString(); |
| if (!prevFile.isEmpty()) |
| configDir = QFileInfo(prevFile).path(); |
| |
| if (configDir.isEmpty()) |
| configDir = defaultPath; |
| |
| QString configFile; |
| configFile = QFileDialog::getSaveFileName(this, |
| "Save configuration file", |
| configDir, |
| "Header files (*.h)"); |
| if (configFile.isEmpty()) |
| return; |
| |
| QFile file(configFile); |
| if (!file.open(QIODevice::WriteOnly)) { |
| QMessageBox::warning(this,"Warning", |
| "Cannot write to file " + configFile); |
| return; |
| } |
| |
| QTextStream stream(&file); |
| FeatureTreeModel *model; |
| model = static_cast<FeatureTreeModel*>(featureTree->model()); |
| model->writeConfig(stream); |
| |
| settings.setValue("lastConfig", QFileInfo(configFile).absoluteFilePath()); |
| setWindowModified(false); |
| updateStatus(); |
| } |
| |
| void Main::loadConfig(const QString &filename) |
| { |
| if (!QFileInfo(filename).isFile()) |
| return; |
| |
| QFile file(filename); |
| if (!file.open(QIODevice::ReadOnly)) { |
| QMessageBox::warning(this,"Warning", "Cannot open file " + filename); |
| return; |
| } |
| |
| QTextStream stream(&file); |
| FeatureTreeModel *model; |
| model = static_cast<FeatureTreeModel*>(featureTree->model()); |
| model->readConfig(stream); |
| |
| QSettings settings; |
| settings.setValue("lastConfig", QFileInfo(filename).absoluteFilePath()); |
| setWindowModified(false); |
| updateStatus(); |
| } |
| |
| void Main::loadFeatures(const QString &filename) |
| { |
| Feature::clear(); |
| |
| QFile file(filename); |
| if (!file.open(QIODevice::ReadOnly)) { |
| QMessageBox::warning(this,"Warning", "Cannot open file " + filename); |
| return; |
| } |
| |
| Feature *feature = 0; |
| int numFeatures = 0; |
| updateStatus(numFeatures); |
| QTextStream s(&file); |
| for (QString line = s.readLine(); !s.atEnd(); line = s.readLine()) { |
| line = line.simplified(); |
| if (line.isEmpty()) |
| continue; |
| if (line.startsWith('#')) |
| continue; |
| |
| int colon = line.indexOf(':'); |
| if (colon < 0) { // assume description |
| QString description = feature->description().simplified(); |
| description += " " + line; |
| feature->setDescription(description); |
| continue; |
| } |
| |
| QString tag = line.left(colon); |
| QString value = line.mid(colon+1).simplified(); |
| if (tag == "Feature") { |
| if (feature) |
| featureModel->addFeature(feature); |
| feature = Feature::getInstance(value); |
| updateStatus(++numFeatures); |
| } else if (tag == "Requires") { |
| Q_ASSERT(feature); |
| feature->setDependencies(value.split(' ', QString::SkipEmptyParts)); |
| } else if (tag == "Name") { |
| Q_ASSERT(feature); |
| feature->setTitle(value); |
| } else if (tag == "Section") { |
| Q_ASSERT(feature); |
| feature->setSection(value); |
| } else if (tag == "SeeAlso") { |
| Q_ASSERT(feature); |
| feature->setRelations(value.split(' ', QString::SkipEmptyParts)); |
| } else if (tag == "Description") { |
| Q_ASSERT(feature); |
| feature->setDescription(value); |
| } |
| } |
| if (feature) |
| featureModel->addFeature(feature); |
| |
| featureTree->resizeColumnToContents(0); |
| |
| QSettings settings; |
| settings.setValue("featureFile", QFileInfo(filename).absoluteFilePath()); |
| |
| updateStatus(); |
| } |
| |
| void Main::showInfo(const QModelIndex &index) |
| { |
| FeatureTreeModel *model; |
| model = static_cast<FeatureTreeModel*>(featureTree->model()); |
| |
| if (const Feature *feature = model->getFeature(index)) |
| textBrowser->setHtml(feature->toHtml()); |
| |
| // Ensure index is visible |
| QModelIndex parent = model->parent(index); |
| while (parent.isValid()) { |
| featureTree->setExpanded(parent, true); |
| parent = model->parent(parent); |
| } |
| |
| featureTree->scrollTo(index); |
| featureTree->setCurrentIndex(index); |
| } |
| |
| void Main::showInfo(const QString &feature) |
| { |
| const Feature *f = Feature::getInstance(feature); |
| FeatureTreeModel *model; |
| model = static_cast<FeatureTreeModel*>(featureTree->model()); |
| showInfo(model->index(f)); |
| } |
| |
| void Main::about() |
| { |
| QMessageBox::about(this, "About qconfig", |
| "<p><b><font size=\"+2\">Qtopia Core build configuration</font></b></p>" |
| "<p></p>" |
| "<p>Version 2.0</p>" |
| "<p>Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).</p>" |
| "<p></p>" |
| ); |
| } |
| |
| void Main::aboutQt() |
| { |
| QMessageBox::aboutQt( this, tr("qconfig") ); |
| } |
| |
| QT_END_NAMESPACE |
| |
| int main(int argc, char** argv) |
| { |
| QT_USE_NAMESPACE |
| QApplication app(argc,argv); |
| app.setOrganizationDomain("trolltech.com"); |
| app.setOrganizationName("Trolltech"); |
| app.setApplicationName("QConfig"); |
| Main m; |
| |
| defaultPath = QLibraryInfo::location(QLibraryInfo::PrefixPath) |
| + "/src/corelib/global"; |
| |
| for (int i = 1; i < argc; ++i) { |
| QString arg = argv[i]; |
| if (arg == "-f" && i+1 < argc) |
| m.loadFeatures(argv[++i]); |
| else if (arg == "-c" && i+1 < argc) |
| m.loadConfig(argv[++i]); |
| } |
| m.resize(m.sizeHint() + QSize(500,300)); |
| m.show(); |
| return app.exec(); |
| } |
| |
| #include "main.moc" |