diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b6bc0c6c..abc9d409e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,10 @@ option(USE_QT_5 "Use Qt 5 for building" OFF) message(STATUS "[${APP_LOW_NAME}] Welcome to ${APP_NAME} compilation process.") message(STATUS "[${APP_LOW_NAME}] Compilation process begins right now.") +if(USE_QT_5 EQUAL false) + message(FATAL_ERROR "[${APP_LOW_NAME}] Only Qt 5 is supported now.") +endif(USE_QT_5 EQUAL false) + # Setup name for executable file. if(APPLE) set(EXE_NAME ${APP_UP_NAME}) diff --git a/src/core/defs.h.in b/src/core/defs.h.in index 75d5fc897..d3ae1948b 100644 --- a/src/core/defs.h.in +++ b/src/core/defs.h.in @@ -2,13 +2,13 @@ #define DEFS_H #include + #if QT_VERSION >= 0x050000 #include #elif QT_VERSION >= 0x040600 #include #endif - #define CMAKE_VERSION "@CMAKE_VERSION@" #define CMAKE_SYSTEM "@CMAKE_SYSTEM@" diff --git a/src/core/messagesmodel.cpp b/src/core/messagesmodel.cpp index e2789688e..4b90ad764 100644 --- a/src/core/messagesmodel.cpp +++ b/src/core/messagesmodel.cpp @@ -11,102 +11,132 @@ MessagesModel::MessagesModel(QObject *parent) - : QAbstractItemModel(parent) { + : QSqlTableModel(parent, DatabaseFactory::getInstance()->addConnection("MessagesModel")) { setObjectName("MessagesModel"); - // TODO: Separovat do samostatné skupiny metod. - // Bude potřeba metoda "loadFeed(int feed_id)" - // a v te se bude volat SELECT .... FROM Messages WHERE id IN (feed_id,feed_id2) - // a tak dále. - QSqlDatabase d = DatabaseFactory::getInstance()->addConnection("MessagesModel2"); - QSqlQuery prikaz = d.exec("SELECT id, read, deleted, important, feed, " - "title, url, author, date_created, " - "date_updated, contents FROM Messages;"); - - // TODO: Oddělit toto do samostatné metody setupIcons(), - // aby bylo možno měnit ikony dynamicky. - m_favoriteIcon = IconThemeFactory::getInstance()->fromTheme("mail-mark-important"); - m_readIcon = IconThemeFactory::getInstance()->fromTheme("mail-mark-read"); - m_unreadIcon = IconThemeFactory::getInstance()->fromTheme("mail-mark-unread"); - - // Prepare correct columns mappings. - m_columnMappings.insert(MSG_MODEL_READ_INDEX, MSG_DB_READ_INDEX); - m_columnMappings.insert(MSG_MODEL_IMPORTANT_INDEX, MSG_DB_IMPORTANT_INDEX); - m_columnMappings.insert(MSG_MODEL_TITLE_INDEX, MSG_DB_TITLE_INDEX); - m_columnMappings.insert(MSG_MODEL_AUTHOR_INDEX, MSG_DB_AUTHOR_INDEX); - m_columnMappings.insert(MSG_MODEL_DCREATED_INDEX, MSG_DB_DCREATED_INDEX); - m_columnMappings.insert(MSG_MODEL_DUPDATED_INDEX, MSG_DB_DUPDATED_INDEX); - - - QList list; - while (prikaz.next()) { - Message mess; - mess.m_data.append(prikaz.value(MSG_DB_ID_INDEX).toInt()); - mess.m_data.append(prikaz.value(MSG_DB_READ_INDEX).toInt()); - mess.m_data.append(prikaz.value(MSG_DB_DELETED_INDEX).toInt()); - mess.m_data.append(prikaz.value(MSG_DB_IMPORTANT_INDEX).toInt()); - mess.m_data.append(prikaz.value(MSG_DB_FEED_INDEX).toInt()); - mess.m_data.append(prikaz.value(MSG_DB_TITLE_INDEX).toString()); - mess.m_data.append(prikaz.value(MSG_DB_URL_INDEX).toString()); - mess.m_data.append(prikaz.value(MSG_DB_AUTHOR_INDEX).toString()); - mess.m_data.append(prikaz.value(MSG_DB_DCREATED_INDEX).toString()); - mess.m_data.append(prikaz.value(MSG_DB_DUPDATED_INDEX).toString()); - mess.m_data.append(prikaz.value(MSG_DB_CONTENTS_INDEX).toString()); - - list.append(mess); - } - m_messages = list; - setupFonts(); + setupIcons(); setupHeaderData(); + + // Set desired table and edit strategy. + setEditStrategy(QSqlTableModel::OnFieldChange); + setTable("Messages"); + + loadMessages(QList()); } MessagesModel::~MessagesModel() { qDebug("Destroying MessagesModel instance."); } +void MessagesModel::setupIcons() { + m_favoriteIcon = IconThemeFactory::getInstance()->fromTheme("mail-mark-important"); + m_readIcon = IconThemeFactory::getInstance()->fromTheme("mail-mark-read"); + m_unreadIcon = IconThemeFactory::getInstance()->fromTheme("mail-mark-unread"); +} + +void MessagesModel::fetchAll() { + while (canFetchMore()) { + fetchMore(); + } +} + void MessagesModel::setupFonts() { m_normalFont = QtSingleApplication::font("MessagesView"); m_boldFont = m_normalFont; m_boldFont.setBold(true); } +void MessagesModel::loadMessages(const QList feed_ids) { + // TODO: Doplnit "AND deleted = 0" + + // Conversion of parameter. + QStringList stringy_ids; + stringy_ids.reserve(feed_ids.count()); + + foreach (int feed_id, feed_ids) { + stringy_ids.append(QString::number(feed_id)); + } + + // TODO: časem povolit. + //setFilter(QString("feed IN (%1) ").arg(stringy_ids.join(','))); + select(); + fetchAll(); +} + void MessagesModel::setupHeaderData() { - m_headerData << tr("Read") << tr("Important") << - tr("Title") << tr("Author") << - tr("Created on") << tr("Updated on"); + m_headerData << tr("Id") << tr("Read") << tr("Deleted") << tr("Important") << + tr("Feed") << tr("Title") << tr("Url") << tr("Author") << + tr("Created on") << tr("Updated on") << tr("Contents"); } -Qt::ItemFlags MessagesModel::flags(const QModelIndex &index) const { - Q_UNUSED(index); +Qt::ItemFlags MessagesModel::flags(const QModelIndex &idx) const { + Q_UNUSED(idx); - // Each item can be selected and is enabled. - return Qt::ItemIsSelectable | Qt::ItemIsEnabled; + if (m_isInEditingMode) { + // NOTE: Editing of model must be temporarily enabled here. + return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable; + } + else { + return Qt::ItemIsSelectable | Qt::ItemIsEnabled; + } } -int MessagesModel::rowCount(const QModelIndex &parent) const { - Q_UNUSED(parent); +QVariant MessagesModel::data(const QModelIndex &idx, int role) const { + switch (role) { + case Qt::DisplayRole: + case Qt::EditRole: + return QSqlTableModel::data(idx, role); - return m_messages.count(); + case Qt::FontRole: + return record(idx.row()).value(MSG_DB_READ_INDEX).toInt() == 1 ? + m_normalFont : + m_boldFont; + + case Qt::DecorationRole: { + int index_column = idx.column(); + + if (index_column == MSG_DB_READ_INDEX) { + return record(idx.row()).value(MSG_DB_READ_INDEX).toInt() == 1 ? + m_readIcon : + m_unreadIcon; + } + else { + return QVariant(); + } + } + + + default: + return QVariant(); + } + /* + if (role == Qt::FontRole && idx.column() == 1) { + return record(idx.row()).value(1).toInt() == 1 ? m_normalFont : m_boldFont; + } + else if (role == Qt::DecorationRole && idx.column() == 3) { + if (record(idx.row()).value(1).toInt() == 1) { + return IconThemeFactory::getInstance()->fromTheme("mail-mark-read"); + } + else { + return IconThemeFactory::getInstance()->fromTheme("mail-mark-unread"); + } + } + else { + return QSqlTableModel::data(idx, role); + }*/ } -int MessagesModel::columnCount(const QModelIndex &parent) const { - Q_UNUSED(parent); +bool MessagesModel::setData(const QModelIndex &idx, const QVariant &value, int role) { + if (!idx.isValid()) { + return false; + } - return m_headerData.count(); -} + m_isInEditingMode = true; + bool set_data_result = QSqlTableModel::setData(idx, value, role); + m_isInEditingMode = false; -QModelIndex MessagesModel::parent(const QModelIndex &child) const { - Q_UNUSED(child); - - return QModelIndex(); -} - -QModelIndex MessagesModel::index(int row, int column, - const QModelIndex &parent) const { - Q_UNUSED(parent); - - return createIndex(row, column); + return set_data_result; } QVariant MessagesModel::headerData(int section, @@ -114,27 +144,29 @@ QVariant MessagesModel::headerData(int section, int role) const { Q_UNUSED(orientation); - // TODO: Ehance this with graphics and other roles. - switch (role) { case Qt::DisplayRole: - if (section > MSG_MODEL_IMPORTANT_INDEX) { + // Display textual headers for all columns except "read" and + // "important" columns. + if (section != MSG_DB_READ_INDEX && section != MSG_DB_IMPORTANT_INDEX) { return m_headerData.at(section); } else { return QVariant(); } + // Return RAW data for these roles. case Qt::ToolTipRole: case Qt::EditRole: return m_headerData.at(section); + // Display icons for "read" and "important" columns. case Qt::DecorationRole: { switch (section) { - case MSG_MODEL_READ_INDEX: + case MSG_DB_READ_INDEX: return m_readIcon; - case MSG_MODEL_IMPORTANT_INDEX: + case MSG_DB_IMPORTANT_INDEX: return m_favoriteIcon; default: @@ -146,100 +178,3 @@ QVariant MessagesModel::headerData(int section, return QVariant(); } } - -QVariant MessagesModel::data(int row, int column, int role) const { - return data(index(row, column), role); -} - -QVariant MessagesModel::data(const QModelIndex &index, int role) const { - // TODO: Return ISO date on EditRole and human readable date on - // DisplayRole. EditRole is used for sorting (and ISO date can be - // sorted as a string. - - switch (role) { - case Qt::EditRole: - // Just return RAW data. - return m_messages.at(index.row()).m_data.at(m_columnMappings[index.column()]); - - case Qt::ToolTipRole: - case Qt::DisplayRole: { - int index_column = index.column(); - - if (index_column > MSG_MODEL_IMPORTANT_INDEX) { - return m_messages.at(index.row()).m_data.at(m_columnMappings[index_column]); - } - else { - return QVariant(); - } - } - - case Qt::FontRole: - return m_messages.at(index.row()).m_data.at(m_columnMappings[MSG_MODEL_READ_INDEX]).toInt() == 1 ? - m_normalFont : - m_boldFont; - - case Qt::DecorationRole: { - int index_column = index.column(); - - switch (index_column) { - case MSG_MODEL_READ_INDEX: - if (m_messages.at(index.row()).m_data.at(m_columnMappings[index_column]).toInt() == 1) { - return m_readIcon; - } - else { - return m_unreadIcon; - } - - case MSG_MODEL_IMPORTANT_INDEX: - if (m_messages.at(index.row()).m_data.at(m_columnMappings[index_column]).toInt() == 1) { - return m_favoriteIcon; - } - - default: - return QVariant(); - } - } - - default: - return QVariant(); - } -} - -bool MessagesModel::setData(const QModelIndex &index, const QVariant &value, - int role) { - Q_UNUSED(role); - - m_messages[index.row()].m_data[m_columnMappings[index.column()]] = value; - - QModelIndex left = this->index(index.row(), 0); - QModelIndex right = this->index(index.row(), columnCount() - 1); - - emit dataChanged(left, right); - return true; -} - -bool MessagesModel::setData(int row, int column, const QVariant &value) { - return setData(index(row, column), value); -} - -const Message &MessagesModel::messageAt(int row_index) const { - return m_messages.at(row_index); -} - -void MessagesModel::setMessageRead(int row_index, int read) { - //int read_column = fieldIndex("read"); - //blockSignals(true); - - - if (data(row_index, MSG_MODEL_READ_INDEX).toInt() != read) { - // Old "read" status of this message differs from - // the new status, update it. - setData(row_index, MSG_MODEL_READ_INDEX, read); - } - - - //blockSignals(false); - //submitAll(); - //emit dataChanged(index(message_row_index, 0), index(message_row_index, columnCount())); -} - diff --git a/src/core/messagesmodel.h b/src/core/messagesmodel.h index cc02a9ec8..581a7d1aa 100644 --- a/src/core/messagesmodel.h +++ b/src/core/messagesmodel.h @@ -1,21 +1,14 @@ #ifndef MESSAGESMODEL_H #define MESSAGESMODEL_H -#include +#include #include #include +#include "core/defs.h" -// Representation of ONE message. -class Message { - private: - QList m_data; - friend class MessagesModel; - friend class WebBrowser; -}; - -class MessagesModel : public QAbstractItemModel { +class MessagesModel : public QSqlTableModel { Q_OBJECT public: @@ -24,46 +17,40 @@ class MessagesModel : public QAbstractItemModel { virtual ~MessagesModel(); // Model implementation. - - // Data accessors/manipulators. + bool setData(const QModelIndex &idx, const QVariant &value, int role = Qt::EditRole); + QVariant data(const QModelIndex &idx, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; - QVariant data(const QModelIndex &index, int role) const; - QVariant data(int row, int column, int role = Qt::EditRole) const; - bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); - bool setData(int row, int column, const QVariant &value); - Qt::ItemFlags flags(const QModelIndex &index) const; + Qt::ItemFlags flags(const QModelIndex &idx) const; - // Model dimensions. - int rowCount(const QModelIndex &parent = QModelIndex()) const; - int columnCount(const QModelIndex &parent = QModelIndex()) const; - - // Model navigation. - QModelIndex parent(const QModelIndex &child) const; - QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; - - const Message &messageAt(int row_index) const; + public: + // Sets up all icons which are used directly by this model. + void setupIcons(); public slots: - // Sets "read" status of message with given row index. - void setMessageRead(int row_index, int read); + // Fetches ALL available data to the model. + // NOTE: This is almost needed when sorting + // and makes the model more predictable. + void fetchAll(); + + // Loads messages of given feeds. + void loadMessages(const QList feed_ids); private: + // Sets up header data. void setupHeaderData(); // Creates "normal" and "bold" fonts. void setupFonts(); private: - QHash m_columnMappings; - QList m_messages; + QList m_headerData; + bool m_isInEditingMode; QFont m_normalFont; QFont m_boldFont; QIcon m_favoriteIcon; QIcon m_readIcon; QIcon m_unreadIcon; - QList m_headerData; - }; #endif // MESSAGESMODEL_H diff --git a/src/gui/messagesview.cpp b/src/gui/messagesview.cpp index ff9b3367e..aed2c264b 100644 --- a/src/gui/messagesview.cpp +++ b/src/gui/messagesview.cpp @@ -1,4 +1,5 @@ #include +#include #include "gui/messagesview.h" #include "core/messagesproxymodel.h" @@ -10,6 +11,12 @@ MessagesView::MessagesView(QWidget *parent) : QTreeView(parent) { m_sourceModel = m_proxyModel->sourceModel(); setModel(m_proxyModel); + + hideColumn(0); + header()->setSectionResizeMode(3, QHeaderView::ResizeToContents); + + // NOTE: It is recommended to call this after the model is set + // due to sorting performance. setupAppearance(); } @@ -28,13 +35,14 @@ MessagesProxyModel *MessagesView::model() { void MessagesView::setupAppearance() { header()->setStretchLastSection(true); + setUniformRowHeights(true); setAcceptDrops(false); setDragEnabled(false); setDragDropMode(QAbstractItemView::NoDragDrop); setExpandsOnDoubleClick(false); setRootIsDecorated(false); setItemsExpandable(false); - setSortingEnabled(false); + setSortingEnabled(true); setAllColumnsShowFocus(true); setSelectionMode(QAbstractItemView::ExtendedSelection); } @@ -44,6 +52,10 @@ void MessagesView::selectionChanged(const QItemSelection &selected, QTreeView::selectionChanged(selected, deselected); } +void MessagesView::keyPressEvent(QKeyEvent *event) { + QTreeView::keyPressEvent(event); +} + void MessagesView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) { QModelIndex ind = m_proxyModel->mapToSource(current); @@ -53,9 +65,7 @@ void MessagesView::currentChanged(const QModelIndex ¤t, current.row(), current.column(), ind.row(), ind.column()); - m_sourceModel->setMessageRead(ind.row(), 1); - - emit currentMessageChanged(m_sourceModel->messageAt(ind.row())); + m_sourceModel->setData(m_sourceModel->index(ind.row(), 1), 1, Qt::EditRole); QTreeView::currentChanged(current, previous); } diff --git a/src/gui/messagesview.h b/src/gui/messagesview.h index e1d5e6ab3..74d2155b9 100644 --- a/src/gui/messagesview.h +++ b/src/gui/messagesview.h @@ -16,12 +16,15 @@ class MessagesView : public QTreeView { explicit MessagesView(QWidget *parent = 0); virtual ~MessagesView(); + // Model accessors. MessagesProxyModel *model(); MessagesModel *sourceModel(); protected: void setupAppearance(); + void keyPressEvent(QKeyEvent *event); + void currentChanged(const QModelIndex ¤t, const QModelIndex &previous); @@ -29,7 +32,8 @@ class MessagesView : public QTreeView { const QItemSelection &deselected); signals: - void currentMessageChanged(const Message &message); + // TODO: dodělat signál. + void currentMessageChanged(/* const Message &message */); void currentMessageRemoved(); private: diff --git a/src/gui/webbrowser.cpp b/src/gui/webbrowser.cpp index 76d4a9f61..d2497d661 100644 --- a/src/gui/webbrowser.cpp +++ b/src/gui/webbrowser.cpp @@ -167,8 +167,9 @@ void WebBrowser::navigateToUrl(const QUrl &url) { } } -void WebBrowser::navigateToMessage(const Message &message) { +void WebBrowser::navigateToMessage() { // TODO: dodělat. + /* m_webView->setHtml(SkinFactory::getInstance()->getCurrentMarkup().arg(message.m_data.at(MSG_DB_TITLE_INDEX).toString(), tr("Check your internet connection or website address"), QString(), @@ -180,7 +181,7 @@ void WebBrowser::navigateToMessage(const Message &message) { "
  • many other things.
  • " ""), "aa")); - + */ } void WebBrowser::updateZoomGui() { diff --git a/src/gui/webbrowser.h b/src/gui/webbrowser.h index ba181a552..d864bbad1 100644 --- a/src/gui/webbrowser.h +++ b/src/gui/webbrowser.h @@ -61,7 +61,7 @@ class WebBrowser : public TabContent { void navigateToUrl(const QUrl &url); // Navigates to message. - void navigateToMessage(const Message &message); + void navigateToMessage(); // Zoom manipulators. void increaseZoom();