/* Qlom is copyright Openismus GmbH, 2009 * * This file is part of Qlom * * Qlom is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Qlom is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Qlom. If not, see . */ #include "main_window.h" #include "document.h" #include "error.h" #include "tables_model.h" #include "utils.h" #include #include #include #include #include #include #include "config.h" QlomMainWindow::QlomMainWindow() : glomDocument(this), valid(true) { setup(); } QlomMainWindow::QlomMainWindow(const QString &filepath) : glomDocument(this), valid(true) { setup(); if(!glomDocument.loadDocument(filepath)) { valid = false; return; } QlomTablesModel *model = glomDocument.createTablesModel(); centralTreeView->setModel(model); connect(centralTreeView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(treeviewDoubleclicked(QModelIndex))); show(); // Open default table. showDefaultTable(); } bool QlomMainWindow::isValid() const { return valid; } void QlomMainWindow::receiveError(const QlomError &error) { if(!error.what().isNull()) { QPointer dialog = new QMessageBox(this); dialog->setText(errorDomainLookup(error.domain())); dialog->setDetailedText(error.what()); // Set icon style and dialog title according to error severity. switch (error.severity()) { case Qlom::CRITICAL_ERROR_SEVERITY: dialog->setWindowTitle(tr("Critical error")); dialog->setIcon(QMessageBox::Critical); break; case Qlom::WARNING_ERROR_SEVERITY: dialog->setWindowTitle(tr("Warning")); dialog->setIcon(QMessageBox::Warning); break; } dialog->exec(); delete dialog; } // TODO: Whether to shut down the application should be for the caller to // decide. murrayc. // QApplication::exit() does not work for us because libglom eats up the // important signals: // http://git.gnome.org/browse/glom/tree/glom/libglom/connectionpool.cc#n538 /* If the error message was non-empty then the error message was shown to * the user too. All that remains is to shut down the application. */ if(Qlom::CRITICAL_ERROR_SEVERITY == error.severity()) { exit(EXIT_FAILURE); } } void QlomMainWindow::setup() { setWindowTitle(qApp->applicationName()); // Create the menu. QAction *fileOpen = new QAction(tr("&Open"), this); fileOpen->setShortcut(tr("Ctrl+O", "Open file")); fileOpen->setStatusTip(tr("Open a Glom document")); QAction *fileClose = new QAction(tr("&Close"), this); fileClose->setShortcut(tr("Ctrl+W", "Close file")); fileClose->setStatusTip(tr("Close the current Glom document")); QAction *fileQuit = new QAction(tr("&Quit"), this); fileQuit->setShortcut(tr("Ctrl+Q", "Quit application")); fileQuit->setStatusTip(tr("Quit the application")); QAction *helpAbout = new QAction(tr("About"), this); helpAbout->setShortcut(tr("Ctrl+A", "About application")); helpAbout->setStatusTip( tr("Display credits and license information for Qlom")); QMenu *fileMenu = menuBar()->addMenu(tr("&File")); fileMenu->addAction(fileOpen); fileMenu->addAction(fileClose); fileMenu->addAction(fileQuit); QMenu *aboutMenu = menuBar()->addMenu(tr("&Help")); aboutMenu->addAction(helpAbout); connect(fileOpen, SIGNAL(triggered(bool)), this, SLOT(fileOpenTriggered())); connect(fileClose, SIGNAL(triggered(bool)), this, SLOT(fileCloseTriggered())); connect(fileQuit, SIGNAL(triggered(bool)), this, SLOT(fileQuitTriggered())); connect(helpAbout, SIGNAL(triggered(bool)), this, SLOT(helpAboutTriggered())); connect(&glomDocument.errorReporter(), SIGNAL(errorRaised(QlomError)), this, SLOT(receiveError(QlomError))); centralTreeView = new QTreeView(this); centralTreeView->setEditTriggers(QAbstractItemView::NoEditTriggers); setCentralWidget(centralTreeView); readSettings(); } QlomMainWindow::~QlomMainWindow() { writeSettings(); QSqlDatabase::database().close(); } void QlomMainWindow::showAboutDialog() { // About dialogs are window-modal in Qt, except on Mac OS X. QMessageBox::about(this, tr("About Qlom"), tr(PACKAGE_NAME "\n" "A Qt Glom database viewer\n" "Copyright 2009 Openismus GmbH")); /* lupdate does not recognise the above string, although if the string is * manually concatenated then it works fine. TODO: File bug. */ } void QlomMainWindow::writeSettings() { QSettings settings; settings.setValue("MainWindow/Size", size()); settings.setValue("MainWindow/InternalProperties", saveState()); } void QlomMainWindow::readSettings() { QSettings settings; resize(settings.value("MainWindow/Size", sizeHint()).toSize()); restoreState(settings.value("MainWindow/InternalProperties").toByteArray()); } QString QlomMainWindow::errorDomainLookup( const Qlom::QlomErrorDomain errorDomain) { switch (errorDomain) { case Qlom::DOCUMENT_ERROR_DOMAIN: return tr("An error occurred while reading the Glom Document"); break; case Qlom::DATABASE_ERROR_DOMAIN: return tr("An error occurred with the database"); break; case Qlom::LOGIC_ERROR_DOMAIN: return tr("The programmmer had a logic error."); break; default: qCritical("Unhandled error domain: %i", errorDomain); return tr("Unhandled error domain"); break; } } void QlomMainWindow::fileOpenTriggered() { QPointer dialog = new QFileDialog(this); dialog->setFileMode(QFileDialog::ExistingFile); dialog->setNameFilter("Glom document (*.glom)"); if (dialog->exec()) { // Close the document before opening a document. fileCloseTriggered(); QStringList files = dialog->selectedFiles(); if(!glomDocument.loadDocument(files.first())) { delete dialog; return; } QlomTablesModel *model = glomDocument.createTablesModel(); centralTreeView->setModel(model); // Open default table. showDefaultTable(); } delete dialog; } void QlomMainWindow::fileCloseTriggered() { centralTreeView->deleteLater(); centralTreeView = new QTreeView(this); setCentralWidget(centralTreeView); connect(centralTreeView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(treeviewDoubleclicked(QModelIndex))); } void QlomMainWindow::fileQuitTriggered() { qApp->quit(); } void QlomMainWindow::helpAboutTriggered() { showAboutDialog(); } void QlomMainWindow::treeviewDoubleclicked(const QModelIndex& index) { const QString &tableName = index.data(Qlom::TableNameRole).toString(); QlomListLayoutModel *model = glomDocument.createListLayoutModel(tableName); showTable(model); const QString tableDisplayName = index.data().toString(); } void QlomMainWindow::showDefaultTable() { // Show the default table, or the first non-hidden table, if there is one. QlomListLayoutModel *model = glomDocument.createDefaultTableListLayoutModel(); if (model) { showTable(model); } } void QlomMainWindow::showTable(QlomListLayoutModel *model) { Q_ASSERT(0 != model); QMainWindow *tableModelWindow = new QMainWindow; QlomListView *view = new QlomListView(tableModelWindow); tableModelWindow->setAttribute(Qt::WA_DeleteOnClose); tableModelWindow->setCentralWidget(view); tableModelWindow->setWindowTitle(model->tableDisplayName()); tableModelWindow->show(); model->setParent(view); view->setModel(model); // Marks model as "read-only" here, because the view has no way to edit it. view->setEditTriggers(QAbstractItemView::NoEditTriggers); // Setup delegates for all columns, if available. for(int idx = 0; idx < model->columnCount(); ++idx) { view->setupDelegateForColumn(idx); } // Setup edit button for last column. const int colIdx = model->columnCount(); model->insertColumnAt(colIdx); model->setHeaderData(colIdx, Qt::Horizontal, QVariant(tr("Actions"))); QlomButtonDelegate *buttonDelegate = new QlomButtonDelegate(tr("Details"), view); connect(buttonDelegate, SIGNAL(buttonPressed(QObject *)), this, SLOT(onDetailsPressed(QObject *))); view->setItemDelegateForColumn(colIdx, buttonDelegate); view->resizeColumnsToContents(); } void QlomMainWindow::onDetailsPressed(QObject *obj) { QlomModelIndexObject *indexObj = qobject_cast(obj); if(indexObj) QMessageBox::critical(this, tr("Details button pressed"), tr("Cell index: (%1, %2)").arg(indexObj->index().column()) .arg(indexObj->index().row())); } QlomListView::QlomListView(QWidget *parent) : QTableView(parent) {} QlomListView::~QlomListView() {} void QlomListView::setupDelegateForColumn(int column) { QlomListLayoutModel *model = qobject_cast(this->model()); if (model) { QStyledItemDelegate *delegate = QlomListView::createDelegateFromColumn(model, column); setItemDelegateForColumn(column, delegate); } } QStyledItemDelegate * QlomListView::createDelegateFromColumn(QlomListLayoutModel *model, int column) { /* Need to respect the following constraint: The layout item in * theLayoutGroup that can be found at the position column points to has to * be a LayoutItem_Text or a LayoutItem_Field. * However, this method is not used efficiently, considering how most items * in a list view are field items. If LayoutItem_Text and LayoutItem_Field * had a common base clase featuring the get_formatting_used() API we could * get rid of the most annoying part at least: the dynamic casts. */ const QlomListLayoutModel::GlomSharedLayoutItems items = model->getLayoutItems(); for (Glom::LayoutGroup::type_list_const_items::const_iterator iter = items.begin(); iter != items.end(); ++iter) { if (column == std::distance(items.begin(), iter)) { Glom::sharedptr textItem = Glom::sharedptr::cast_dynamic(*iter); if(textItem) return new QlomLayoutItemTextDelegate( textItem->get_formatting_used(), QlomLayoutItemTextDelegate::GlomSharedField(), ustringToQstring(textItem->get_text())); Glom::sharedptr fieldItem = Glom::sharedptr::cast_dynamic(*iter); if(fieldItem) return new QlomLayoutItemFieldDelegate( fieldItem->get_formatting_used(), fieldItem->get_full_field_details()); } } return 0; }