53c23f4 by David King at 2009-07-21 1
/* Qlom is copyright Openismus GmbH, 2009
2
 *
3
 * This file is part of Qlom
4
 *
5
 * Qlom is free software: you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation, either version 3 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * Qlom is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with Qlom. If not, see <http://www.gnu.org/licenses/>.
17
 */
18
19
#include "main_window.h"
aaadceb by David King at 2010-01-19 20
#include "document.h"
21
#include "error.h"
22
#include "tables_model.h"
67716f4 by Michael Hasselmann at 2010-01-29 23
#include "utils.h"
53c23f4 by David King at 2009-07-21 24
d0c3843 by David King at 2009-11-17 25
#include <memory>
417f334 by David King at 2009-10-21 26
#include <QSqlField>
27
#include <QSqlIndex>
28
#include <QSqlRecord>
47610dd by Michael Hasselmann at 2010-01-28 29
#include <QtCore>
30
#include <QtGui>
53c23f4 by David King at 2009-07-21 31
27ce4bd by David King at 2009-11-09 32
#include "config.h"
53c23f4 by David King at 2009-07-21 33
aaadceb by David King at 2010-01-19 34
QlomMainWindow::QlomMainWindow() :
652c32b by Murray Cumming at 2010-01-11 35
    glomDocument(this),
36
    valid(true)
0e3e295 by David King at 2009-11-09 37
{
38
  setup();
39
}
40
aaadceb by David King at 2010-01-19 41
QlomMainWindow::QlomMainWindow(const QString &filepath) :
31dbfbf by Murray Cumming at 2010-01-11 42
    glomDocument(this),
43
    valid(true)
0e3e295 by David King at 2009-11-09 44
{
bd64b1b by David King at 2009-11-10 45
    setup();
46
31dbfbf by Murray Cumming at 2010-01-11 47
    if(!glomDocument.loadDocument(filepath)) {
48
        valid = false;
49
        return;
50
    }
51
aaadceb by David King at 2010-01-19 52
    QlomTablesModel *model = glomDocument.createTablesModel();
0e3e295 by David King at 2009-11-09 53
    centralTreeView->setModel(model);
f4afe67 by David King at 2009-11-13 54
3cdf36d by David King at 2009-11-24 55
    connect(centralTreeView, SIGNAL(doubleClicked(QModelIndex)),
56
        this, SLOT(treeviewDoubleclicked(QModelIndex)));
f4afe67 by David King at 2009-11-13 57
    show();
31dbfbf by Murray Cumming at 2010-01-11 58
f4afe67 by David King at 2009-11-13 59
    // Open default table.
60
    showDefaultTable();
0e3e295 by David King at 2009-11-09 61
}
62
aaadceb by David King at 2010-01-19 63
bool QlomMainWindow::isValid() const
31dbfbf by Murray Cumming at 2010-01-11 64
{
65
    return valid;
66
}
67
aaadceb by David King at 2010-01-19 68
void QlomMainWindow::receiveError(const QlomError &error)
efe1247 by David King at 2009-11-24 69
{
70
    if(!error.what().isNull()) {
097b0c5 by David King at 2009-12-18 71
        QPointer<QMessageBox> dialog = new QMessageBox(this);
72
        dialog->setText(errorDomainLookup(error.domain()));
73
        dialog->setDetailedText(error.what());
74e33df by David King at 2009-11-24 74
75
        // Set icon style and dialog title according to error severity.
76
        switch (error.severity()) {
77
        case Qlom::CRITICAL_ERROR_SEVERITY:
097b0c5 by David King at 2009-12-18 78
            dialog->setWindowTitle(tr("Critical error"));
79
            dialog->setIcon(QMessageBox::Critical);
766f921 by Michael Hasselmann at 2009-11-26 80
            break;
81
        case Qlom::WARNING_ERROR_SEVERITY:
097b0c5 by David King at 2009-12-18 82
            dialog->setWindowTitle(tr("Warning"));
83
            dialog->setIcon(QMessageBox::Warning);
766f921 by Michael Hasselmann at 2009-11-26 84
            break;
74e33df by David King at 2009-11-24 85
        }
86
097b0c5 by David King at 2009-12-18 87
        dialog->exec();
88
        delete dialog;
342413d by Michael Hasselmann at 2009-11-26 89
    }
90
31dbfbf by Murray Cumming at 2010-01-11 91
    // TODO: Whether to shut down the application should be for the caller to 
92
    // decide. murrayc.
91e852f by Michael Hasselmann at 2010-01-27 93
    // QApplication::exit() does not work for us because libglom eats up the
94
    // important signals:
95
    // http://git.gnome.org/browse/glom/tree/glom/libglom/connectionpool.cc#n538
2675656 by David King at 2009-12-18 96
    /* If the error message was non-empty then the error message was shown to
31dbfbf by Murray Cumming at 2010-01-11 97
     * the user too. All that remains is to shut down the application. */
342413d by Michael Hasselmann at 2009-11-26 98
    if(Qlom::CRITICAL_ERROR_SEVERITY == error.severity()) {
91e852f by Michael Hasselmann at 2010-01-27 99
        exit(EXIT_FAILURE);
efe1247 by David King at 2009-11-24 100
    }
101
}
102
aaadceb by David King at 2010-01-19 103
void QlomMainWindow::setup()
53c23f4 by David King at 2009-07-21 104
{
c1ce018 by David King at 2009-10-21 105
    setWindowTitle(qApp->applicationName());
106
107
    // Create the menu.
bd64b1b by David King at 2009-11-10 108
    QAction *fileOpen = new QAction(tr("&Open"), this);
6734a0a by David King at 2009-11-10 109
    fileOpen->setShortcut(tr("Ctrl+O", "Open file"));
bd64b1b by David King at 2009-11-10 110
    fileOpen->setStatusTip(tr("Open a Glom document"));
111
    QAction *fileClose = new QAction(tr("&Close"), this);
6734a0a by David King at 2009-11-10 112
    fileClose->setShortcut(tr("Ctrl+W", "Close file"));
bd64b1b by David King at 2009-11-10 113
    fileClose->setStatusTip(tr("Close the current Glom document"));
c1ce018 by David King at 2009-10-21 114
    QAction *fileQuit = new QAction(tr("&Quit"), this);
6734a0a by David King at 2009-11-10 115
    fileQuit->setShortcut(tr("Ctrl+Q", "Quit application"));
c1ce018 by David King at 2009-10-21 116
    fileQuit->setStatusTip(tr("Quit the application"));
117
    QAction *helpAbout = new QAction(tr("About"), this);
6734a0a by David King at 2009-11-10 118
    helpAbout->setShortcut(tr("Ctrl+A", "About application"));
c1ce018 by David King at 2009-10-21 119
    helpAbout->setStatusTip(
120
        tr("Display credits and license information for Qlom"));
121
122
    QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
bd64b1b by David King at 2009-11-10 123
    fileMenu->addAction(fileOpen);
124
    fileMenu->addAction(fileClose);
c1ce018 by David King at 2009-10-21 125
    fileMenu->addAction(fileQuit);
126
    QMenu *aboutMenu = menuBar()->addMenu(tr("&Help"));
127
    aboutMenu->addAction(helpAbout);
128
3cdf36d by David King at 2009-11-24 129
    connect(fileOpen, SIGNAL(triggered(bool)),
130
        this, SLOT(fileOpenTriggered()));
131
    connect(fileClose, SIGNAL(triggered(bool)),
132
        this, SLOT(fileCloseTriggered()));
133
    connect(fileQuit, SIGNAL(triggered(bool)),
134
        this, SLOT(fileQuitTriggered()));
135
    connect(helpAbout, SIGNAL(triggered(bool)),
136
        this, SLOT(helpAboutTriggered()));
c1ce018 by David King at 2009-10-21 137
ee97302 by David King at 2009-12-18 138
    connect(&glomDocument.errorReporter(), SIGNAL(errorRaised(QlomError)),
766f921 by Michael Hasselmann at 2009-11-26 139
        this, SLOT(receiveError(QlomError)));
140
0e3e295 by David King at 2009-11-09 141
    centralTreeView = new QTreeView(this);
b53a22f by David King at 2010-01-19 142
    centralTreeView->setEditTriggers(QAbstractItemView::NoEditTriggers);
0e3e295 by David King at 2009-11-09 143
    setCentralWidget(centralTreeView);
c1ce018 by David King at 2009-10-21 144
145
    readSettings();
53c23f4 by David King at 2009-07-21 146
}
147
aaadceb by David King at 2010-01-19 148
QlomMainWindow::~QlomMainWindow()
53c23f4 by David King at 2009-07-21 149
{
c1ce018 by David King at 2009-10-21 150
    writeSettings();
151
    QSqlDatabase::database().close();
53c23f4 by David King at 2009-07-21 152
}
153
aaadceb by David King at 2010-01-19 154
void QlomMainWindow::showAboutDialog()
53c23f4 by David King at 2009-07-21 155
{
c1ce018 by David King at 2009-10-21 156
    // About dialogs are window-modal in Qt, except on Mac OS X.
157
    QMessageBox::about(this, tr("About Qlom"), tr(PACKAGE_NAME "\n"
6734a0a by David King at 2009-11-10 158
          "A Qt Glom database viewer\n"
159
          "Copyright 2009 Openismus GmbH"));
160
    /* lupdate does not recognise the above string, although if the string is
161
     * manually concatenated then it works fine. TODO: File bug. */
53c23f4 by David King at 2009-07-21 162
}
163
aaadceb by David King at 2010-01-19 164
void QlomMainWindow::writeSettings()
53c23f4 by David King at 2009-07-21 165
{
c1ce018 by David King at 2009-10-21 166
    QSettings settings;
167
    settings.setValue("MainWindow/Size", size());
168
    settings.setValue("MainWindow/InternalProperties", saveState());
53c23f4 by David King at 2009-07-21 169
}
170
aaadceb by David King at 2010-01-19 171
void QlomMainWindow::readSettings()
53c23f4 by David King at 2009-07-21 172
{
c1ce018 by David King at 2009-10-21 173
    QSettings settings;
174
    resize(settings.value("MainWindow/Size", sizeHint()).toSize());
175
    restoreState(settings.value("MainWindow/InternalProperties").toByteArray());
53c23f4 by David King at 2009-07-21 176
}
177
aaadceb by David King at 2010-01-19 178
QString QlomMainWindow::errorDomainLookup(
74e33df by David King at 2009-11-24 179
    const Qlom::QlomErrorDomain errorDomain)
180
{
181
    switch (errorDomain) {
182
    case Qlom::DOCUMENT_ERROR_DOMAIN:
183
        return tr("An error occurred while reading the Glom Document");
184
        break;
185
    case Qlom::DATABASE_ERROR_DOMAIN:
186
        return tr("An error occurred with the database");
187
        break;
91e852f by Michael Hasselmann at 2010-01-27 188
    case Qlom::LOGIC_ERROR_DOMAIN:
189
        return tr("The programmmer had a logic error.");
190
        break;
74e33df by David King at 2009-11-24 191
    default:
192
        qCritical("Unhandled error domain: %i", errorDomain);
193
        return tr("Unhandled error domain");
194
        break;
195
    }
196
}
197
aaadceb by David King at 2010-01-19 198
void QlomMainWindow::fileOpenTriggered()
bd64b1b by David King at 2009-11-10 199
{
097b0c5 by David King at 2009-12-18 200
    QPointer<QFileDialog> dialog = new QFileDialog(this);
201
    dialog->setFileMode(QFileDialog::ExistingFile);
202
    dialog->setNameFilter("Glom document (*.glom)");
203
    if (dialog->exec()) {
bd64b1b by David King at 2009-11-10 204
        // Close the document before opening a document.
205
        fileCloseTriggered();
206
097b0c5 by David King at 2009-12-18 207
        QStringList files = dialog->selectedFiles();
efe1247 by David King at 2009-11-24 208
        if(!glomDocument.loadDocument(files.first()))
209
        {
097b0c5 by David King at 2009-12-18 210
            delete dialog;
efe1247 by David King at 2009-11-24 211
            return;
d0c3843 by David King at 2009-11-17 212
        }
213
aaadceb by David King at 2010-01-19 214
        QlomTablesModel *model = glomDocument.createTablesModel();
bd64b1b by David King at 2009-11-10 215
        centralTreeView->setModel(model);
f4afe67 by David King at 2009-11-13 216
        // Open default table.
217
        showDefaultTable();
bd64b1b by David King at 2009-11-10 218
    }
097b0c5 by David King at 2009-12-18 219
    delete dialog;
bd64b1b by David King at 2009-11-10 220
}
221
aaadceb by David King at 2010-01-19 222
void QlomMainWindow::fileCloseTriggered()
bd64b1b by David King at 2009-11-10 223
{
e21f553 by David King at 2009-11-11 224
    centralTreeView->deleteLater();
bd64b1b by David King at 2009-11-10 225
    centralTreeView = new QTreeView(this);
226
    setCentralWidget(centralTreeView);
3cdf36d by David King at 2009-11-24 227
    connect(centralTreeView, SIGNAL(doubleClicked(QModelIndex)),
228
        this, SLOT(treeviewDoubleclicked(QModelIndex)));
bd64b1b by David King at 2009-11-10 229
}
230
aaadceb by David King at 2010-01-19 231
void QlomMainWindow::fileQuitTriggered()
53c23f4 by David King at 2009-07-21 232
{
c1ce018 by David King at 2009-10-21 233
    qApp->quit();
53c23f4 by David King at 2009-07-21 234
}
235
aaadceb by David King at 2010-01-19 236
void QlomMainWindow::helpAboutTriggered()
53c23f4 by David King at 2009-07-21 237
{
c1ce018 by David King at 2009-10-21 238
    showAboutDialog();
53c23f4 by David King at 2009-07-21 239
}
63b6617 by David King at 2009-08-03 240
aaadceb by David King at 2010-01-19 241
void QlomMainWindow::treeviewDoubleclicked(const QModelIndex& index)
ee5ee78 by David King at 2009-10-21 242
{
3e59554 by David King at 2009-11-09 243
    const QString &tableName = index.data(Qlom::TableNameRole).toString();
aaadceb by David King at 2010-01-19 244
    QlomListLayoutModel *model = glomDocument.createListLayoutModel(tableName);
07ed5d5 by Michael Hasselmann at 2009-12-18 245
    showTable(model);
766f921 by Michael Hasselmann at 2009-11-26 246
701cf05 by David King at 2009-11-18 247
    const QString tableDisplayName = index.data().toString();
63b6617 by David King at 2009-08-03 248
}
f4afe67 by David King at 2009-11-13 249
aaadceb by David King at 2010-01-19 250
void QlomMainWindow::showDefaultTable()
f4afe67 by David King at 2009-11-13 251
{
aaadceb by David King at 2010-01-19 252
    // Show the default table, or the first non-hidden table, if there is one.
253
    QlomListLayoutModel *model =
ee97302 by David King at 2009-12-18 254
      glomDocument.createDefaultTableListLayoutModel();
0fa759c by Murray Cumming at 2010-01-12 255
256
    if (model) {
257
        showTable(model);
258
    }
07ed5d5 by Michael Hasselmann at 2009-12-18 259
}
260
aaadceb by David King at 2010-01-19 261
void QlomMainWindow::showTable(QlomListLayoutModel *model)
07ed5d5 by Michael Hasselmann at 2009-12-18 262
{
db737f0 by Michael Hasselmann at 2010-01-28 263
    Q_ASSERT(0 != model);
47610dd by Michael Hasselmann at 2010-01-28 264
265
    QMainWindow *tableModelWindow = new QMainWindow;
67716f4 by Michael Hasselmann at 2010-01-29 266
    QlomListView *view = new QlomListView(tableModelWindow);
0fa759c by Murray Cumming at 2010-01-12 267
faddaf2 by Michael Hasselmann at 2009-12-18 268
    tableModelWindow->setAttribute(Qt::WA_DeleteOnClose);
47610dd by Michael Hasselmann at 2010-01-28 269
    tableModelWindow->setCentralWidget(view);
270
    tableModelWindow->setWindowTitle(model->tableDisplayName());
271
    tableModelWindow->show();
faddaf2 by Michael Hasselmann at 2009-12-18 272
47610dd by Michael Hasselmann at 2010-01-28 273
    model->setParent(view);
274
    view->setModel(model);
275
276
    // Marks model as "read-only" here, because the view has no way to edit it.
b53a22f by David King at 2010-01-19 277
    view->setEditTriggers(QAbstractItemView::NoEditTriggers);
f4afe67 by David King at 2009-11-13 278
47610dd by Michael Hasselmann at 2010-01-28 279
    // Setup delegates for all columns, if available.
aaadceb by David King at 2010-01-19 280
    for(int idx = 0; idx < model->columnCount(); ++idx) {
67716f4 by Michael Hasselmann at 2010-01-29 281
        view->setupDelegateForColumn(idx);
39a716d by Michael Hasselmann at 2009-12-31 282
    }
f3ddb80 by Michael Hasselmann at 2009-12-18 283
47610dd by Michael Hasselmann at 2010-01-28 284
    // Setup edit button for last column.
2a9e63f by Michael Hasselmann at 2010-01-28 285
    const int colIdx = model->columnCount();
47610dd by Michael Hasselmann at 2010-01-28 286
    model->insertColumnAt(colIdx);
2a9e63f by Michael Hasselmann at 2010-01-28 287
    model->setHeaderData(colIdx, Qt::Horizontal, QVariant(tr("Actions")));
f87c8d0 by Michael Hasselmann at 2010-01-29 288
    QlomButtonDelegate *buttonDelegate = new QlomButtonDelegate(tr("Details"), view);
289
    connect(buttonDelegate, SIGNAL(buttonPressed(QObject *)),
290
            this, SLOT(onDetailsPressed(QObject *)));
291
    view->setItemDelegateForColumn(colIdx, buttonDelegate);
f4afe67 by David King at 2009-11-13 292
47610dd by Michael Hasselmann at 2010-01-28 293
    view->resizeColumnsToContents();
f4afe67 by David King at 2009-11-13 294
}
f87c8d0 by Michael Hasselmann at 2010-01-29 295
296
void QlomMainWindow::onDetailsPressed(QObject *obj)
297
{
298
    QlomModelIndexObject *indexObj = qobject_cast<QlomModelIndexObject *>(obj);
299
    if(indexObj)
300
        QMessageBox::critical(this, tr("Details button pressed"),
301
                                    tr("Cell index: (%1, %2)").arg(indexObj->index().column())
302
                                                              .arg(indexObj->index().row()));
303
}
67716f4 by Michael Hasselmann at 2010-01-29 304
305
QlomListView::QlomListView(QWidget *parent)
306
: QTableView(parent)
307
{}
308
309
QlomListView::~QlomListView()
310
{}
311
312
void QlomListView::setupDelegateForColumn(int column)
313
{
314
    QlomListLayoutModel *model = qobject_cast<QlomListLayoutModel *>(this->model());
315
316
    if (model)
317
    {
318
        QStyledItemDelegate *delegate = QlomListView::createDelegateFromColumn(model, column);
319
        setItemDelegateForColumn(column, delegate);
320
    }
321
}
322
323
QStyledItemDelegate * QlomListView::createDelegateFromColumn(QlomListLayoutModel *model,
324
                                                             int column)
325
{
326
    /* Need to respect the following constraint: The layout item in
327
     * theLayoutGroup that can be found at the position column points to has to
328
     * be a LayoutItem_Text or a LayoutItem_Field.
329
     * However, this method is not used efficiently, considering how most items
330
     * in a list view are field items. If LayoutItem_Text and LayoutItem_Field
331
     * had a common base clase featuring the get_formatting_used() API we could
332
     * get rid of the most annoying part at least: the dynamic casts. */
333
    const QlomListLayoutModel::GlomSharedLayoutItems items = model->getLayoutItems();
334
    for (Glom::LayoutGroup::type_list_const_items::const_iterator iter =
335
        items.begin(); iter != items.end(); ++iter) {
336
        if (column == std::distance(items.begin(), iter)) {
337
            Glom::sharedptr<const Glom::LayoutItem_Text> textItem =
338
                Glom::sharedptr<const Glom::LayoutItem_Text>::cast_dynamic(*iter);
339
            if(textItem)
340
                return new QlomLayoutItemTextDelegate(
341
                    textItem->get_formatting_used(),
342
                    QlomLayoutItemTextDelegate::GlomSharedField(),
343
                    ustringToQstring(textItem->get_text()));
344
345
            Glom::sharedptr<const Glom::LayoutItem_Field> fieldItem =
346
                Glom::sharedptr<const Glom::LayoutItem_Field>::cast_dynamic(*iter);
347
            if(fieldItem)
348
                return new QlomLayoutItemFieldDelegate(
349
                    fieldItem->get_formatting_used(),
350
                    fieldItem->get_full_field_details());
351
        }
352
    }
353
354
    return 0;
355
}