diff --git a/plugins/FeedReader/FeedReader.pro b/plugins/FeedReader/FeedReader.pro index c2f0c1ba2..651222e73 100644 --- a/plugins/FeedReader/FeedReader.pro +++ b/plugins/FeedReader/FeedReader.pro @@ -7,6 +7,7 @@ SOURCES = FeedReaderPlugin.cpp \ services/p3FeedReaderThread.cc \ services/rsFeedReaderItems.cc \ gui/FeedReaderDialog.cpp \ + gui/FeedReaderMessageWidget.cpp \ gui/AddFeedDialog.cpp \ gui/PreviewFeedDialog.cpp \ gui/FeedReaderNotify.cpp \ @@ -24,6 +25,7 @@ HEADERS = FeedReaderPlugin.h \ services/p3FeedReaderThread.h \ services/rsFeedReaderItems.h \ gui/FeedReaderDialog.h \ + gui/FeedReaderMessageWidget.h \ gui/AddFeedDialog.h \ gui/PreviewFeedDialog.h \ gui/FeedReaderNotify.h \ @@ -36,6 +38,7 @@ HEADERS = FeedReaderPlugin.h \ util/XPathWrapper.h FORMS = gui/FeedReaderDialog.ui \ + gui/FeedReaderMessageWidget.ui \ gui/AddFeedDialog.ui \ gui/PreviewFeedDialog.ui \ gui/FeedReaderConfig.ui diff --git a/plugins/FeedReader/gui/FeedReaderDialog.cpp b/plugins/FeedReader/gui/FeedReaderDialog.cpp index bbe95e571..16a1f86f9 100644 --- a/plugins/FeedReader/gui/FeedReaderDialog.cpp +++ b/plugins/FeedReader/gui/FeedReaderDialog.cpp @@ -19,23 +19,19 @@ * Boston, MA 02110-1301, USA. ****************************************************************/ -#include #include #include #include -#include #include #include -#include -#include #include "FeedReaderDialog.h" +#include "FeedReaderMessageWidget.h" #include "ui_FeedReaderDialog.h" #include "FeedReaderNotify.h" #include "AddFeedDialog.h" #include "FeedReaderStringDefs.h" #include "gui/common/RSTreeWidgetItem.h" -#include "util/HandleRichText.h" #include "gui/settings/rsharesettings.h" #include "FeedReaderUserNotify.h" @@ -57,55 +53,29 @@ #define ROLE_FEED_ERROR Qt::UserRole + 8 #define ROLE_FEED_DEACTIVATED Qt::UserRole + 9 -#define COLUMN_MSG_COUNT 4 -#define COLUMN_MSG_TITLE 0 -#define COLUMN_MSG_READ 1 -#define COLUMN_MSG_PUBDATE 2 -#define COLUMN_MSG_AUTHOR 3 -#define COLUMN_MSG_DATA 0 - -#define ROLE_MSG_ID Qt::UserRole -#define ROLE_MSG_SORT Qt::UserRole + 1 -#define ROLE_MSG_NEW Qt::UserRole + 2 -#define ROLE_MSG_READ Qt::UserRole + 3 -#define ROLE_MSG_LINK Qt::UserRole + 4 - FeedReaderDialog::FeedReaderDialog(RsFeedReader *feedReader, QWidget *parent) : MainPage(parent), mFeedReader(feedReader), ui(new Ui::FeedReaderDialog) { /* Invoke the Qt Designer generated object setup routine */ ui->setupUi(this); + mProcessSettings = false; + mOpenFeedIds = NULL; + mNotify = new FeedReaderNotify(); mFeedReader->setNotify(mNotify); connect(mNotify, SIGNAL(notifyFeedChanged(QString,int)), this, SLOT(feedChanged(QString,int))); connect(mNotify, SIGNAL(notifyMsgChanged(QString,QString,int)), this, SLOT(msgChanged(QString,QString,int))); - mProcessSettings = false; - /* connect signals */ connect(ui->feedTreeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(feedItemChanged(QTreeWidgetItem*))); - connect(ui->msgTreeWidget, SIGNAL(itemSelectionChanged()), this, SLOT(msgItemChanged())); - connect(ui->msgTreeWidget, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(msgItemClicked(QTreeWidgetItem*,int))); + connect(ui->messageTabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(messageTabCloseRequested(int))); connect(ui->feedTreeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(feedTreeCustomPopupMenu(QPoint))); - connect(ui->msgTreeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(msgTreeCustomPopupMenu(QPoint))); - - connect(ui->filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterItems(QString))); - connect(ui->filterLineEdit, SIGNAL(filterChanged(int)), this, SLOT(filterColumnChanged(int))); - - connect(ui->linkButton, SIGNAL(clicked()), this, SLOT(openLinkMsg())); - connect(ui->expandButton, SIGNAL(clicked()), this, SLOT(toggleMsgText())); mFeedCompareRole = new RSTreeWidgetItemCompareRole; mFeedCompareRole->setRole(COLUMN_FEED_NAME, ROLE_FEED_SORT); - mMsgCompareRole = new RSTreeWidgetItemCompareRole; - mMsgCompareRole->setRole(COLUMN_MSG_TITLE, ROLE_MSG_SORT); - mMsgCompareRole->setRole(COLUMN_MSG_READ, ROLE_MSG_SORT); - mMsgCompareRole->setRole(COLUMN_MSG_PUBDATE, ROLE_MSG_SORT); - mMsgCompareRole->setRole(COLUMN_MSG_AUTHOR, ROLE_MSG_SORT); - /* initialize root item */ mRootItem = new QTreeWidgetItem(ui->feedTreeWidget); QString name = tr("Message Folders"); @@ -116,54 +86,18 @@ FeedReaderDialog::FeedReaderDialog(RsFeedReader *feedReader, QWidget *parent) mRootItem->setData(COLUMN_FEED_DATA, ROLE_FEED_ICON, QIcon(":/images/Root.png")); mRootItem->setExpanded(true); - /* initialize msg list */ - ui->msgTreeWidget->sortItems(COLUMN_MSG_PUBDATE, Qt::DescendingOrder); - /* set initial size the splitter */ QList sizes; sizes << 300 << width(); // Qt calculates the right sizes ui->splitter->setSizes(sizes); - /* set header resize modes and initial section sizes */ - QHeaderView *header = ui->msgTreeWidget->header(); - header->setResizeMode(COLUMN_MSG_TITLE, QHeaderView::Interactive); - header->resizeSection(COLUMN_MSG_TITLE, 350); - header->resizeSection(COLUMN_MSG_PUBDATE, 140); - header->resizeSection(COLUMN_MSG_AUTHOR, 150); - - /* set text of column "Read" to empty - without this the column has a number as header text */ - QTreeWidgetItem *headerItem = ui->msgTreeWidget->headerItem(); - headerItem->setText(COLUMN_MSG_READ, ""); - - /* add filter actions */ - ui->filterLineEdit->addFilter(QIcon(), tr("Title"), COLUMN_MSG_TITLE, tr("Search Title")); - ui->filterLineEdit->addFilter(QIcon(), tr("Date"), COLUMN_MSG_PUBDATE, tr("Search Date")); - ui->filterLineEdit->addFilter(QIcon(), tr("Author"), COLUMN_MSG_AUTHOR, tr("Search Author")); - ui->filterLineEdit->setCurrentFilter(COLUMN_MSG_TITLE); - /* load settings */ processSettings(true); - /* Set header sizes for the fixed columns and resize modes, must be set after processSettings */ - header->resizeSection(COLUMN_MSG_READ, 24); - header->setResizeMode(COLUMN_MSG_READ, QHeaderView::Fixed); - /* initialize feed list */ ui->feedTreeWidget->sortItems(COLUMN_FEED_NAME, Qt::AscendingOrder); - /* build menu for link button */ - QMenu *menu = new QMenu(this); - QAction *action = menu->addAction(tr("Open link in browser"), this, SLOT(openLinkMsg())); - menu->addAction(tr("Copy link to clipboard"), this, SLOT(copyLinkMsg())); - - QFont font = action->font(); - font.setBold(true); - action->setFont(font); - - ui->linkButton->setMenu(menu); - ui->linkButton->setEnabled(false); - - ui->msgTreeWidget->installEventFilter(this); + ui->feedTreeWidget->installEventFilter(this); } FeedReaderDialog::~FeedReaderDialog() @@ -172,11 +106,15 @@ FeedReaderDialog::~FeedReaderDialog() processSettings(false); delete(mFeedCompareRole); - delete(mMsgCompareRole); delete(ui); mFeedReader->setNotify(NULL); delete(mNotify); + + if (mOpenFeedIds) { + delete mOpenFeedIds; + mOpenFeedIds = NULL; + } } UserNotify *FeedReaderDialog::getUserNotify(QObject *parent) @@ -187,43 +125,74 @@ UserNotify *FeedReaderDialog::getUserNotify(QObject *parent) void FeedReaderDialog::processSettings(bool load) { mProcessSettings = true; - - QHeaderView *header = ui->msgTreeWidget->header (); - Settings->beginGroup(QString("FeedReaderDialog")); if (load) { // load settings - // expandButton - bool value = Settings->value("expandButton", true).toBool(); - ui->expandButton->setChecked(value); - toggleMsgText_internal(); - - // filterColumn - ui->filterLineEdit->setCurrentFilter(Settings->value("filterColumn", COLUMN_MSG_TITLE).toInt()); - - // state of thread tree - header->restoreState(Settings->value("msgTree").toByteArray()); - // state of splitter ui->splitter->restoreState(Settings->value("Splitter").toByteArray()); - ui->msgSplitter->restoreState(Settings->value("msgSplitter").toByteArray()); + + // open feeds + int arrayIndex = Settings->beginReadArray("Feeds"); + for (int index = 0; index < arrayIndex; index++) { + Settings->setArrayIndex(index); + addFeedToExpand(Settings->value("open").toString().toStdString()); + } + Settings->endArray(); } else { // save settings - // state of thread tree - Settings->setValue("msgTree", header->saveState()); - // state of splitter Settings->setValue("Splitter", ui->splitter->saveState()); - Settings->setValue("msgSplitter", ui->msgSplitter->saveState()); + + // open groups + Settings->beginWriteArray("Feeds"); + int arrayIndex = 0; + QList expandedFeedIds; + getExpandedFeedIds(expandedFeedIds); + foreach (std::string feedId, expandedFeedIds) { + Settings->setArrayIndex(arrayIndex++); + Settings->setValue("open", QString::fromStdString(feedId)); + } + Settings->endArray(); } Settings->endGroup(); mProcessSettings = false; } +void FeedReaderDialog::addFeedToExpand(const std::string &feedId) +{ + if (mOpenFeedIds == NULL) { + mOpenFeedIds = new QList; + } + if (mOpenFeedIds->contains(feedId)) { + return; + } + mOpenFeedIds->push_back(feedId); +} + +void FeedReaderDialog::getExpandedFeedIds(QList &feedIds) +{ + QTreeWidgetItemIterator it(ui->feedTreeWidget); + QTreeWidgetItem *item; + while ((item = *it) != NULL) { + ++it; + if (!item->isExpanded()) { + continue; + } + if (!item->data(COLUMN_FEED_DATA, ROLE_FEED_FOLDER).toBool()) { + continue; + } + std::string feedId = item->data(COLUMN_FEED_DATA, ROLE_FEED_ID).toString().toStdString(); + if (feedId.empty()) { + continue; + } + feedIds.push_back(feedId); + } +} + void FeedReaderDialog::showEvent(QShowEvent */*event*/) { updateFeeds("", mRootItem); @@ -231,24 +200,6 @@ void FeedReaderDialog::showEvent(QShowEvent */*event*/) bool FeedReaderDialog::eventFilter(QObject *obj, QEvent *event) { - if (obj == ui->msgTreeWidget) { - if (event->type() == QEvent::KeyPress) { - QKeyEvent *keyEvent = static_cast(event); - if (keyEvent) { - if (keyEvent->key() == Qt::Key_Space) { - /* Space pressed */ - QTreeWidgetItem *item = ui->msgTreeWidget->currentItem(); - msgItemClicked(item, COLUMN_MSG_READ); - return true; // eat event - } - if (keyEvent->key() == Qt::Key_Delete) { - /* Delete pressed */ - removeMsg(); - return true; // eat event - } - } - } - } if (obj == ui->feedTreeWidget) { if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(event); @@ -275,16 +226,6 @@ std::string FeedReaderDialog::currentFeedId() return item->data(COLUMN_FEED_DATA, ROLE_FEED_ID).toString().toStdString(); } -std::string FeedReaderDialog::currentMsgId() -{ - QTreeWidgetItem *item = ui->msgTreeWidget->currentItem(); - if (!item) { - return ""; - } - - return item->data(COLUMN_MSG_DATA, ROLE_MSG_ID).toString().toStdString(); -} - void FeedReaderDialog::feedTreeCustomPopupMenu(QPoint /*point*/) { QMenu contextMnu(this); @@ -335,31 +276,6 @@ void FeedReaderDialog::feedTreeCustomPopupMenu(QPoint /*point*/) contextMnu.exec(QCursor::pos()); } -void FeedReaderDialog::msgTreeCustomPopupMenu(QPoint /*point*/) -{ - QMenu contextMnu(this); - - QList selectedItems = ui->msgTreeWidget->selectedItems(); - - QAction *action = contextMnu.addAction(QIcon(""), tr("Mark as read"), this, SLOT(markAsReadMsg())); - action->setEnabled(!selectedItems.empty()); - - action = contextMnu.addAction(QIcon(""), tr("Mark as unread"), this, SLOT(markAsUnreadMsg())); - action->setEnabled(!selectedItems.empty()); - - action = contextMnu.addAction(QIcon(""), tr("Mark all as read"), this, SLOT(markAllAsReadMsg())); - - contextMnu.addSeparator(); - - action = contextMnu.addAction(QIcon(""), tr("Copy link"), this, SLOT(copyLinskMsg())); - action->setEnabled(!selectedItems.empty()); - - action = contextMnu.addAction(QIcon(""), tr("Remove"), this, SLOT(removeMsg())); - action->setEnabled(!selectedItems.empty()); - - contextMnu.exec(QCursor::pos()); -} - void FeedReaderDialog::updateFeeds(const std::string &parentId, QTreeWidgetItem *parentItem) { if (!parentItem) { @@ -412,8 +328,22 @@ void FeedReaderDialog::updateFeeds(const std::string &parentId, QTreeWidgetItem if (feedIt->flag.folder) { /* process child feeds */ updateFeeds(feedIt->feedId, item); + + if (mOpenFeedIds) { + int index = mOpenFeedIds->indexOf(feedIt->feedId); + if (index >= 0) { + item->setExpanded(true); + mOpenFeedIds->removeAt(index); + } + } } } + + if (mOpenFeedIds && mOpenFeedIds->empty()) { + delete mOpenFeedIds; + mOpenFeedIds = NULL; + } + calculateFeedItems(); } @@ -534,113 +464,6 @@ void FeedReaderDialog::updateFeedItem(QTreeWidgetItem *item, FeedInfo &info) item->setToolTip(COLUMN_FEED_NAME, (info.errorState != RS_FEED_ERRORSTATE_OK) ? FeedReaderStringDefs::errorString(info) : ""); } -void FeedReaderDialog::updateMsgs(const std::string &feedId) -{ - std::list msgInfos; - if (!mFeedReader->getFeedMsgList(feedId, msgInfos)) { - ui->msgTreeWidget->clear(); - return; - } - - int index = 0; - QTreeWidgetItem *item; - std::list::iterator msgIt; - - /* update existing and delete not existing msgs */ - while (index < ui->msgTreeWidget->topLevelItemCount()) { - item = ui->msgTreeWidget->topLevelItem(index); - std::string msgId = item->data(COLUMN_MSG_DATA, ROLE_MSG_ID).toString().toStdString(); - - /* search existing msg */ - int found = -1; - for (msgIt = msgInfos.begin (); msgIt != msgInfos.end (); ++msgIt) { - if (msgIt->msgId == msgId) { - /* found it, update it */ - updateMsgItem(item, *msgIt); - - msgInfos.erase(msgIt); - found = index; - break; - } - } - if (found >= 0) { - ++index; - } else { - delete(ui->msgTreeWidget->takeTopLevelItem(index)); - } - } - - /* add new msgs */ - for (msgIt = msgInfos.begin (); msgIt != msgInfos.end (); ++msgIt) { - item = new RSTreeWidgetItem(mMsgCompareRole); - updateMsgItem(item, *msgIt); - ui->msgTreeWidget->addTopLevelItem(item); - } - - filterItems(ui->filterLineEdit->text()); -} - -void FeedReaderDialog::calculateMsgIconsAndFonts(QTreeWidgetItem *item) -{ - if (!item) { - return; - } - - bool isnew = item->data(COLUMN_MSG_DATA, ROLE_MSG_NEW).toBool(); - bool read = item->data(COLUMN_MSG_DATA, ROLE_MSG_READ).toBool(); - - if (read) { - item->setIcon(COLUMN_MSG_READ, QIcon(":/images/message-state-read.png")); - } else { - item->setIcon(COLUMN_MSG_READ, QIcon(":/images/message-state-unread.png")); - } - if (isnew) { - item->setIcon(COLUMN_MSG_TITLE, QIcon(":/images/message-state-new.png")); - } else { - item->setIcon(COLUMN_MSG_TITLE, QIcon()); - } - - for (int i = 0; i < COLUMN_FEED_COUNT; i++) { - QFont font = item->font(i); - font.setBold(isnew || !read); - item->setFont(i, font); - } - - item->setData(COLUMN_MSG_READ, ROLE_MSG_SORT, QString("%1_%2_%3").arg(QString(isnew ? "1" : "0"), QString(read ? "0" : "1"), item->data(COLUMN_MSG_TITLE, ROLE_MSG_SORT).toString())); -} - -void FeedReaderDialog::updateMsgItem(QTreeWidgetItem *item, FeedMsgInfo &info) -{ - QString title = QString::fromUtf8(info.title.c_str()); - QDateTime qdatetime; - qdatetime.setTime_t(info.pubDate); - - /* add string to all data */ - QString sort = QString("%1_%2_%2").arg(title, qdatetime.toString("yyyyMMdd_hhmmss"), QString::fromStdString(info.feedId)); - - item->setText(COLUMN_MSG_TITLE, title); - item->setData(COLUMN_MSG_TITLE, ROLE_MSG_SORT, sort); - - QString author = QString::fromUtf8(info.author.c_str()); - item->setText(COLUMN_MSG_AUTHOR, author); - item->setData(COLUMN_MSG_AUTHOR, ROLE_MSG_SORT, author + "_" + sort); - - /* if the message is on same date show only time */ - if (qdatetime.daysTo(QDateTime::currentDateTime()) == 0) { - item->setData(COLUMN_MSG_PUBDATE, Qt::DisplayRole, qdatetime.time()); - } else { - item->setData(COLUMN_MSG_PUBDATE, Qt::DisplayRole, qdatetime); - } - item->setData(COLUMN_MSG_PUBDATE, ROLE_MSG_SORT, QString("%1_%2_%3").arg(qdatetime.toString("yyyyMMdd_hhmmss"), title, QString::fromStdString(info.msgId))); - - item->setData(COLUMN_MSG_DATA, ROLE_MSG_ID, QString::fromStdString(info.msgId)); - item->setData(COLUMN_MSG_DATA, ROLE_MSG_LINK, QString::fromUtf8(info.link.c_str())); - item->setData(COLUMN_MSG_DATA, ROLE_MSG_READ, info.flag.read); - item->setData(COLUMN_MSG_DATA, ROLE_MSG_NEW, info.flag.isnew); - - calculateMsgIconsAndFonts(item); -} - void FeedReaderDialog::feedChanged(const QString &feedId, int type) { if (!isVisible()) { @@ -697,218 +520,66 @@ void FeedReaderDialog::feedChanged(const QString &feedId, int type) calculateFeedItems(); } -void FeedReaderDialog::msgChanged(const QString &feedId, const QString &msgId, int type) +FeedReaderMessageWidget *FeedReaderDialog::feedMessageWidget(const std::string &id) { - if (!isVisible()) { - /* complete update in showEvent */ - return; - } - - if (feedId.isEmpty() || msgId.isEmpty()) { - return; - } - - if (feedId.toStdString() != currentFeedId()) { - return; - } - - FeedMsgInfo msgInfo; - if (type != NOTIFY_TYPE_DEL) { - if (!mFeedReader->getMsgInfo(feedId.toStdString(), msgId.toStdString(), msgInfo)) { - return; + int tabCount = ui->messageTabWidget->count(); + for (int index = 0; index < tabCount; ++index) { + FeedReaderMessageWidget *childWidget = dynamic_cast(ui->messageTabWidget->widget(index)); + if (childWidget && childWidget->feedId() == id) { + return childWidget; } } - if (type == NOTIFY_TYPE_MOD || type == NOTIFY_TYPE_DEL) { - QTreeWidgetItemIterator it(ui->msgTreeWidget); - QTreeWidgetItem *item; - while ((item = *it) != NULL) { - if (item->data(COLUMN_MSG_DATA, ROLE_MSG_ID).toString() == msgId) { - if (type == NOTIFY_TYPE_MOD) { - updateMsgItem(item, msgInfo); - filterItem(item); - } else { - delete(item); - } - break; - } - ++it; - } - } - - if (type == NOTIFY_TYPE_ADD) { - QTreeWidgetItem *item = new RSTreeWidgetItem(mMsgCompareRole); - updateMsgItem(item, msgInfo); - ui->msgTreeWidget->addTopLevelItem(item); - filterItem(item); - } + return NULL; } void FeedReaderDialog::feedItemChanged(QTreeWidgetItem *item) { if (!item) { - ui->msgTreeWidget->clear(); + return; + } + + if (item->data(COLUMN_FEED_DATA, ROLE_FEED_FOLDER).toBool()) { return; } std::string feedId = item->data(COLUMN_FEED_DATA, ROLE_FEED_ID).toString().toStdString(); - updateMsgs(feedId); + /* search exisiting tab */ + FeedReaderMessageWidget *messageWidget = feedMessageWidget(feedId); + + if (!messageWidget) { + /* create a message widget */ + messageWidget = new FeedReaderMessageWidget(feedId, mFeedReader, mNotify); + int index = ui->messageTabWidget->addTab(messageWidget, messageWidget->feedName(true)); + ui->messageTabWidget->setTabIcon(index, messageWidget->feedIcon()); + connect(messageWidget, SIGNAL(feedMessageChanged(QWidget*)), this, SLOT(messageTabChanged(QWidget*))); + } + + ui->messageTabWidget->setCurrentWidget(messageWidget); } -void FeedReaderDialog::msgItemClicked(QTreeWidgetItem *item, int column) +void FeedReaderDialog::messageTabCloseRequested(int index) { - if (item == NULL) { + FeedReaderMessageWidget *messageWidget = dynamic_cast(ui->messageTabWidget->widget(index)); + if (messageWidget) { + delete(messageWidget); + } +} + +void FeedReaderDialog::messageTabChanged(QWidget *widget) +{ + int index = ui->messageTabWidget->indexOf(widget); + if (index < 0) { return; } - if (column == COLUMN_MSG_READ) { - QList rows; - rows.append(item); - bool read = item->data(COLUMN_MSG_DATA, ROLE_MSG_READ).toBool(); - setMsgAsReadUnread(rows, !read); - return; - } -} - -void FeedReaderDialog::msgItemChanged() -{ - long todo; // show link somewhere - - std::string feedId = currentFeedId(); - std::string msgId = currentMsgId(); - - if (feedId.empty() || msgId.empty()) { - ui->msgTitle->clear(); -// ui->msgLink->clear(); - ui->msgText->clear(); - ui->linkButton->setEnabled(false); + FeedReaderMessageWidget *messageWidget = dynamic_cast(ui->messageTabWidget->widget(index)); + if (!messageWidget) { return; } - QTreeWidgetItem *item = ui->msgTreeWidget->currentItem(); - if (!item) { - /* there is something wrong */ - ui->msgTitle->clear(); -// ui->msgLink->clear(); - ui->msgText->clear(); - ui->linkButton->setEnabled(false); - return; - } - - /* get msg */ - FeedMsgInfo msgInfo; - if (!mFeedReader->getMsgInfo(feedId, msgId, msgInfo)) { - ui->msgTitle->clear(); -// ui->msgLink->clear(); - ui->msgText->clear(); - ui->linkButton->setEnabled(false); - return; - } - - bool setToReadOnActive = Settings->valueFromGroup("FeedReaderDialog", "SetMsgToReadOnActivate", true).toBool(); - bool isnew = item->data(COLUMN_MSG_DATA, ROLE_MSG_NEW).toBool(); - bool read = item->data(COLUMN_MSG_DATA, ROLE_MSG_READ).toBool(); - - QList row; - row.append(item); - if (read) { - if (isnew) { - /* something wrong, but set as read again to clear the new flag */ - setMsgAsReadUnread(row, true); - } - } else { - /* set to read/unread */ - setMsgAsReadUnread(row, setToReadOnActive); - } - - QString msgTxt = RsHtml().formatText(ui->msgText->document(), QString::fromUtf8(msgInfo.description.c_str()), RSHTML_FORMATTEXT_EMBED_LINKS); - - ui->msgText->setHtml(msgTxt); - ui->msgTitle->setText(QString::fromUtf8(msgInfo.title.c_str())); - - ui->linkButton->setEnabled(!msgInfo.link.empty()); -} - -void FeedReaderDialog::setMsgAsReadUnread(QList &rows, bool read) -{ - QList::iterator rowIt; - - std::string feedId = currentFeedId(); - if (feedId.empty()) { - return; - } - - for (rowIt = rows.begin(); rowIt != rows.end(); rowIt++) { - QTreeWidgetItem *item = *rowIt; - bool rowRead = item->data(COLUMN_MSG_DATA, ROLE_MSG_READ).toBool(); - bool rowNew = item->data(COLUMN_MSG_DATA, ROLE_MSG_NEW).toBool(); - - if (rowNew || read != rowRead) { - std::string msgId = item->data(COLUMN_MSG_DATA, ROLE_MSG_ID).toString().toStdString(); - mFeedReader->setMessageRead(feedId, msgId, read); - } - } -} - -void FeedReaderDialog::filterColumnChanged(int column) -{ - if (mProcessSettings) { - return; - } - - filterItems(ui->filterLineEdit->text()); - - // save index - Settings->setValueToGroup("FeedReaderDialog", "filterColumn", column); -} - -void FeedReaderDialog::filterItems(const QString& text) -{ - int filterColumn = ui->filterLineEdit->currentFilter(); - - int count = ui->msgTreeWidget->topLevelItemCount(); - for (int index = 0; index < count; ++index) { - filterItem(ui->msgTreeWidget->topLevelItem(index), text, filterColumn); - } -} - -void FeedReaderDialog::filterItem(QTreeWidgetItem *item, const QString &text, int filterColumn) -{ - if (text.isEmpty() == false) { - if (item->text(filterColumn).contains(text, Qt::CaseInsensitive)) { - item->setHidden(false); - } else { - item->setHidden(true); - } - } else { - item->setHidden(false); - } -} - -void FeedReaderDialog::filterItem(QTreeWidgetItem *item) -{ - filterItem(item, ui->filterLineEdit->text(), ui->filterLineEdit->currentFilter()); -} - -void FeedReaderDialog::toggleMsgText() -{ - // save state of button - Settings->setValueToGroup("FeedReaderDialog", "expandButton", ui->expandButton->isChecked()); - - toggleMsgText_internal(); -} - -void FeedReaderDialog::toggleMsgText_internal() -{ - if (ui->expandButton->isChecked()) { - ui->msgText->setVisible(true); - ui->expandButton->setIcon(QIcon(QString(":/images/edit_remove24.png"))); - ui->expandButton->setToolTip(tr("Hide")); - } else { - ui->msgText->setVisible(false); - ui->expandButton->setIcon(QIcon(QString(":/images/edit_add24.png"))); - ui->expandButton->setToolTip(tr("Expand")); - } + ui->messageTabWidget->setTabText(index, messageWidget->feedName(true)); + ui->messageTabWidget->setTabIcon(index, messageWidget->feedIcon()); } void FeedReaderDialog::newFolder() @@ -1014,98 +685,3 @@ void FeedReaderDialog::processFeed() mFeedReader->processFeed(feedId); } - -void FeedReaderDialog::markAsReadMsg() -{ - QList selectedItems = ui->msgTreeWidget->selectedItems(); - setMsgAsReadUnread(selectedItems, true); -} - -void FeedReaderDialog::markAsUnreadMsg() -{ - QList selectedItems = ui->msgTreeWidget->selectedItems(); - setMsgAsReadUnread(selectedItems, false); -} - -void FeedReaderDialog::markAllAsReadMsg() -{ - QList items; - - QTreeWidgetItemIterator it(ui->msgTreeWidget); - QTreeWidgetItem *item; - while ((item = *it) != NULL) { - if (!item->isHidden()) { - items.push_back(item); - } - ++it; - } - setMsgAsReadUnread(items, true); -} - -void FeedReaderDialog::copyLinksMsg() -{ - QString links; - - QTreeWidgetItemIterator it(ui->msgTreeWidget, QTreeWidgetItemIterator::Selected); - QTreeWidgetItem *item; - while ((item = *it) != NULL) { - QString link = item->data(COLUMN_MSG_DATA, ROLE_MSG_LINK).toString(); - if (!link.isEmpty()) { - links += link + "\n"; - } - ++it; - } - - if (links.isEmpty()) { - return; - } - - QApplication::clipboard()->setText(links); -} - -void FeedReaderDialog::removeMsg() -{ - std::string feedId = currentFeedId(); - if (feedId.empty()) { - return; - } - - QList selectedItems = ui->msgTreeWidget->selectedItems(); - QList::iterator it; - std::list msgIds; - - for (it = selectedItems.begin(); it != selectedItems.end(); ++it) { - msgIds.push_back((*it)->data(COLUMN_MSG_DATA, ROLE_MSG_ID).toString().toStdString()); - } - mFeedReader->removeMsgs(feedId, msgIds); -} - -void FeedReaderDialog::copyLinkMsg() -{ - QTreeWidgetItem *item = ui->msgTreeWidget->currentItem(); - if (!item) { - return; - } - - QString link = item->data(COLUMN_MSG_DATA, ROLE_MSG_LINK).toString(); - if (link.isEmpty()) { - return; - } - - QApplication::clipboard()->setText(link); -} - -void FeedReaderDialog::openLinkMsg() -{ - QTreeWidgetItem *item = ui->msgTreeWidget->currentItem(); - if (!item) { - return; - } - - QString link = item->data(COLUMN_MSG_DATA, ROLE_MSG_LINK).toString(); - if (link.isEmpty()) { - return; - } - - QDesktopServices::openUrl(QUrl(link)); -} diff --git a/plugins/FeedReader/gui/FeedReaderDialog.h b/plugins/FeedReader/gui/FeedReaderDialog.h index 2574bd51b..92858ee43 100644 --- a/plugins/FeedReader/gui/FeedReaderDialog.h +++ b/plugins/FeedReader/gui/FeedReaderDialog.h @@ -33,6 +33,7 @@ class QTreeWidgetItem; class RsFeedReader; class RSTreeWidgetItemCompareRole; class FeedReaderNotify; +class FeedReaderMessageWidget; class FeedReaderDialog : public MainPage { @@ -50,52 +51,37 @@ protected: private slots: void feedTreeCustomPopupMenu(QPoint point); - void msgTreeCustomPopupMenu(QPoint point); void feedItemChanged(QTreeWidgetItem *item); - void msgItemChanged(); - void msgItemClicked(QTreeWidgetItem *item, int column); - void filterColumnChanged(int column); - void filterItems(const QString &text); - void toggleMsgText(); void newFolder(); void newFeed(); void removeFeed(); void editFeed(); void activateFeed(); void processFeed(); - void markAsReadMsg(); - void markAsUnreadMsg(); - void markAllAsReadMsg(); - void copyLinksMsg(); - void removeMsg(); - void openLinkMsg(); - void copyLinkMsg(); + + void messageTabCloseRequested(int index); + void messageTabChanged(QWidget *widget); /* FeedReaderNotify */ void feedChanged(const QString &feedId, int type); - void msgChanged(const QString &feedId, const QString &msgId, int type); private: std::string currentFeedId(); - std::string currentMsgId(); void processSettings(bool load); + void addFeedToExpand(const std::string &feedId); + void getExpandedFeedIds(QList &feedIds); void updateFeeds(const std::string &parentId, QTreeWidgetItem *parentItem); void updateFeedItem(QTreeWidgetItem *item, FeedInfo &info); - void updateMsgs(const std::string &feedId); - void calculateMsgIconsAndFonts(QTreeWidgetItem *item); - void updateMsgItem(QTreeWidgetItem *item, FeedMsgInfo &info); - void setMsgAsReadUnread(QList &rows, bool read); - void filterItem(QTreeWidgetItem *item, const QString &text, int filterColumn); - void filterItem(QTreeWidgetItem *item); - void toggleMsgText_internal(); void calculateFeedItems(); void calculateFeedItem(QTreeWidgetItem *item, uint32_t &unreadCount, bool &loading); + FeedReaderMessageWidget *feedMessageWidget(const std::string &id); + bool mProcessSettings; + QList *mOpenFeedIds; QTreeWidgetItem *mRootItem; RSTreeWidgetItemCompareRole *mFeedCompareRole; - RSTreeWidgetItemCompareRole *mMsgCompareRole; // gui interface RsFeedReader *mFeedReader; diff --git a/plugins/FeedReader/gui/FeedReaderDialog.ui b/plugins/FeedReader/gui/FeedReaderDialog.ui index 581523506..cb610e0f7 100644 --- a/plugins/FeedReader/gui/FeedReaderDialog.ui +++ b/plugins/FeedReader/gui/FeedReaderDialog.ui @@ -86,7 +86,7 @@ - + 9 @@ -113,171 +113,13 @@ - - - Qt::Vertical + + + true + + + true - - - - - - QFrame::Box - - - QFrame::Sunken - - - - 2 - - - - - Search forums - - - - - - - - - - - 9 - - - - Qt::CustomContextMenu - - - QAbstractItemView::NoEditTriggers - - - QAbstractItemView::ExtendedSelection - - - true - - - true - - - - Title - - - - - - - - - :/images/message-state-header.png:/images/message-state-header.png - - - - - Date - - - - - Author - - - - - - - - - - - 0 - 0 - - - - - 10 - 75 - true - - - - Message: - - - - - - - true - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - - :/images/Link.png:/images/Link.png - - - QToolButton::MenuButtonPopup - - - true - - - - - - - - 24 - 24 - - - - Qt::NoFocus - - - - - - - :/images/edit_remove24.png:/images/edit_remove24.png - - - true - - - true - - - - - - - - - - - 0 - 10 - - - - - 9 - - - @@ -285,19 +127,13 @@ - LineEditClear - QLineEdit -
gui/common/LineEditClear.h
-
- - LinkTextBrowser - QTextBrowser -
gui/common/LinkTextBrowser.h
+ RSTreeWidget + QTreeWidget +
gui/common/RSTreeWidget.h
- diff --git a/plugins/FeedReader/gui/FeedReaderMessageWidget.cpp b/plugins/FeedReader/gui/FeedReaderMessageWidget.cpp new file mode 100644 index 000000000..00aa04cb0 --- /dev/null +++ b/plugins/FeedReader/gui/FeedReaderMessageWidget.cpp @@ -0,0 +1,688 @@ +#include +#include +#include +#include +#include + +#include "FeedReaderMessageWidget.h" +#include "ui_FeedReaderMessageWidget.h" +#include "FeedReaderNotify.h" + +#include "gui/common/RSTreeWidgetItem.h" +#include "gui/settings/rsharesettings.h" +#include "util/HandleRichText.h" + +#include "interface/rsFeedReader.h" +#include "retroshare/rsiface.h" + +#define COLUMN_MSG_COUNT 4 +#define COLUMN_MSG_TITLE 0 +#define COLUMN_MSG_READ 1 +#define COLUMN_MSG_PUBDATE 2 +#define COLUMN_MSG_AUTHOR 3 +#define COLUMN_MSG_DATA 0 + +#define ROLE_MSG_ID Qt::UserRole +#define ROLE_MSG_SORT Qt::UserRole + 1 +#define ROLE_MSG_NEW Qt::UserRole + 2 +#define ROLE_MSG_READ Qt::UserRole + 3 +#define ROLE_MSG_LINK Qt::UserRole + 4 + +FeedReaderMessageWidget::FeedReaderMessageWidget(const std::string &feedId, RsFeedReader *feedReader, FeedReaderNotify *notify, QWidget *parent) : + QWidget(parent), mFeedId(feedId), mFeedReader(feedReader), mNotify(notify), ui(new Ui::FeedReaderMessageWidget) +{ + ui->setupUi(this); + + mProcessSettings = false; + mUnreadCount = 0; + + /* connect signals */ + connect(mNotify, SIGNAL(notifyFeedChanged(QString,int)), this, SLOT(feedChanged(QString,int))); + connect(mNotify, SIGNAL(notifyMsgChanged(QString,QString,int)), this, SLOT(msgChanged(QString,QString,int))); + + connect(ui->msgTreeWidget, SIGNAL(itemSelectionChanged()), this, SLOT(msgItemChanged())); + connect(ui->msgTreeWidget, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(msgItemClicked(QTreeWidgetItem*,int))); + + connect(ui->msgTreeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(msgTreeCustomPopupMenu(QPoint))); + + connect(ui->filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterItems(QString))); + connect(ui->filterLineEdit, SIGNAL(filterChanged(int)), this, SLOT(filterColumnChanged(int))); + + connect(ui->linkButton, SIGNAL(clicked()), this, SLOT(openLinkMsg())); + connect(ui->expandButton, SIGNAL(clicked()), this, SLOT(toggleMsgText())); + + mMsgCompareRole = new RSTreeWidgetItemCompareRole; + mMsgCompareRole->setRole(COLUMN_MSG_TITLE, ROLE_MSG_SORT); + mMsgCompareRole->setRole(COLUMN_MSG_READ, ROLE_MSG_SORT); + mMsgCompareRole->setRole(COLUMN_MSG_PUBDATE, ROLE_MSG_SORT); + mMsgCompareRole->setRole(COLUMN_MSG_AUTHOR, ROLE_MSG_SORT); + + /* initialize msg list */ + ui->msgTreeWidget->sortItems(COLUMN_MSG_PUBDATE, Qt::DescendingOrder); + + /* set header resize modes and initial section sizes */ + QHeaderView *header = ui->msgTreeWidget->header(); + header->setResizeMode(COLUMN_MSG_TITLE, QHeaderView::Interactive); + header->resizeSection(COLUMN_MSG_TITLE, 350); + header->resizeSection(COLUMN_MSG_PUBDATE, 140); + header->resizeSection(COLUMN_MSG_AUTHOR, 150); + + /* set text of column "Read" to empty - without this the column has a number as header text */ + QTreeWidgetItem *headerItem = ui->msgTreeWidget->headerItem(); + headerItem->setText(COLUMN_MSG_READ, ""); + + /* add filter actions */ + ui->filterLineEdit->addFilter(QIcon(), tr("Title"), COLUMN_MSG_TITLE, tr("Search Title")); + ui->filterLineEdit->addFilter(QIcon(), tr("Date"), COLUMN_MSG_PUBDATE, tr("Search Date")); + ui->filterLineEdit->addFilter(QIcon(), tr("Author"), COLUMN_MSG_AUTHOR, tr("Search Author")); + ui->filterLineEdit->setCurrentFilter(COLUMN_MSG_TITLE); + + /* load settings */ + processSettings(true); + + /* Set header sizes for the fixed columns and resize modes, must be set after processSettings */ + header->resizeSection(COLUMN_MSG_READ, 24); + header->setResizeMode(COLUMN_MSG_READ, QHeaderView::Fixed); + + /* build menu for link button */ + QMenu *menu = new QMenu(this); + QAction *action = menu->addAction(tr("Open link in browser"), this, SLOT(openLinkMsg())); + menu->addAction(tr("Copy link to clipboard"), this, SLOT(copyLinkMsg())); + + QFont font = action->font(); + font.setBold(true); + action->setFont(font); + + ui->linkButton->setMenu(menu); + ui->linkButton->setEnabled(false); + + ui->msgTreeWidget->installEventFilter(this); + + FeedInfo feedInfo; + if (mFeedReader->getFeedInfo(mFeedId, feedInfo)) { + mFeedName = QString::fromUtf8(feedInfo.name.c_str()); + + mFeedReader->getMessageCount(mFeedId, NULL, NULL, &mUnreadCount); + } else { + mFeedId.clear(); + } + + updateMsgs(); +} + +FeedReaderMessageWidget::~FeedReaderMessageWidget() +{ + /* save settings */ + processSettings(false); + + delete(mMsgCompareRole); + delete ui; +} + +void FeedReaderMessageWidget::processSettings(bool load) +{ + mProcessSettings = true; + Settings->beginGroup(QString("FeedReaderDialog")); + + QHeaderView *header = ui->msgTreeWidget->header (); + + if (load) { + // load settings + + // expandButton + bool value = Settings->value("expandButton", true).toBool(); + ui->expandButton->setChecked(value); + toggleMsgText_internal(); + + // filterColumn + ui->filterLineEdit->setCurrentFilter(Settings->value("filterColumn", COLUMN_MSG_TITLE).toInt()); + + // state of thread tree + header->restoreState(Settings->value("msgTree").toByteArray()); + + // state of splitter + ui->msgSplitter->restoreState(Settings->value("msgSplitter").toByteArray()); + } else { + // save settings + + // state of thread tree + Settings->setValue("msgTree", header->saveState()); + + // state of splitter + Settings->setValue("msgSplitter", ui->msgSplitter->saveState()); + } + + Settings->endGroup(); + mProcessSettings = false; +} + +void FeedReaderMessageWidget::showEvent(QShowEvent */*event*/) +{ + updateMsgs(); +} + +bool FeedReaderMessageWidget::eventFilter(QObject *obj, QEvent *event) +{ + if (obj == ui->msgTreeWidget) { + if (event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(event); + if (keyEvent) { + if (keyEvent->key() == Qt::Key_Space) { + /* Space pressed */ + QTreeWidgetItem *item = ui->msgTreeWidget->currentItem(); + msgItemClicked(item, COLUMN_MSG_READ); + return true; // eat event + } + if (keyEvent->key() == Qt::Key_Delete) { + /* Delete pressed */ + removeMsg(); + return true; // eat event + } + } + } + } + /* pass the event on to the parent class */ + return QWidget::eventFilter(obj, event); +} + +QString FeedReaderMessageWidget::feedName(bool withUnreadCount) +{ + QString name = mFeedName.isEmpty() ? tr("No name") : mFeedName; + + if (withUnreadCount && mUnreadCount) { + name += QString(" (%1)").arg(mUnreadCount); + } + + return name; +} + +QIcon FeedReaderMessageWidget::feedIcon() +{ +// if (mThreadQueue->activeRequestExist(TOKEN_TYPE_CURRENTFORUM) || mFillThread) { +// return QIcon(":/images/kalarm.png"); +// } + +// if (mNewCount) { +// return QIcon(":/images/message-state-new.png"); +// } + + return QIcon(); +} + +std::string FeedReaderMessageWidget::currentMsgId() +{ + QTreeWidgetItem *item = ui->msgTreeWidget->currentItem(); + if (!item) { + return ""; + } + + return item->data(COLUMN_MSG_DATA, ROLE_MSG_ID).toString().toStdString(); +} + +void FeedReaderMessageWidget::msgTreeCustomPopupMenu(QPoint /*point*/) +{ + QMenu contextMnu(this); + + QList selectedItems = ui->msgTreeWidget->selectedItems(); + + QAction *action = contextMnu.addAction(QIcon(""), tr("Mark as read"), this, SLOT(markAsReadMsg())); + action->setEnabled(!selectedItems.empty()); + + action = contextMnu.addAction(QIcon(""), tr("Mark as unread"), this, SLOT(markAsUnreadMsg())); + action->setEnabled(!selectedItems.empty()); + + action = contextMnu.addAction(QIcon(""), tr("Mark all as read"), this, SLOT(markAllAsReadMsg())); + + contextMnu.addSeparator(); + + action = contextMnu.addAction(QIcon(""), tr("Copy link"), this, SLOT(copyLinskMsg())); + action->setEnabled(!selectedItems.empty()); + + action = contextMnu.addAction(QIcon(""), tr("Remove"), this, SLOT(removeMsg())); + action->setEnabled(!selectedItems.empty()); + + contextMnu.exec(QCursor::pos()); +} + +void FeedReaderMessageWidget::updateMsgs() +{ + if (mFeedId.empty()) { + ui->msgTreeWidget->clear(); + return; + } + + std::list msgInfos; + if (!mFeedReader->getFeedMsgList(mFeedId, msgInfos)) { + ui->msgTreeWidget->clear(); + return; + } + + int index = 0; + QTreeWidgetItem *item; + std::list::iterator msgIt; + + /* update existing and delete not existing msgs */ + while (index < ui->msgTreeWidget->topLevelItemCount()) { + item = ui->msgTreeWidget->topLevelItem(index); + std::string msgId = item->data(COLUMN_MSG_DATA, ROLE_MSG_ID).toString().toStdString(); + + /* search existing msg */ + int found = -1; + for (msgIt = msgInfos.begin (); msgIt != msgInfos.end (); ++msgIt) { + if (msgIt->msgId == msgId) { + /* found it, update it */ + updateMsgItem(item, *msgIt); + + msgInfos.erase(msgIt); + found = index; + break; + } + } + if (found >= 0) { + ++index; + } else { + delete(ui->msgTreeWidget->takeTopLevelItem(index)); + } + } + + /* add new msgs */ + for (msgIt = msgInfos.begin (); msgIt != msgInfos.end (); ++msgIt) { + item = new RSTreeWidgetItem(mMsgCompareRole); + updateMsgItem(item, *msgIt); + ui->msgTreeWidget->addTopLevelItem(item); + } + + filterItems(ui->filterLineEdit->text()); +} + +void FeedReaderMessageWidget::calculateMsgIconsAndFonts(QTreeWidgetItem *item) +{ + if (!item) { + return; + } + + bool isnew = item->data(COLUMN_MSG_DATA, ROLE_MSG_NEW).toBool(); + bool read = item->data(COLUMN_MSG_DATA, ROLE_MSG_READ).toBool(); + + if (read) { + item->setIcon(COLUMN_MSG_READ, QIcon(":/images/message-state-read.png")); + } else { + item->setIcon(COLUMN_MSG_READ, QIcon(":/images/message-state-unread.png")); + } + if (isnew) { + item->setIcon(COLUMN_MSG_TITLE, QIcon(":/images/message-state-new.png")); + } else { + item->setIcon(COLUMN_MSG_TITLE, QIcon()); + } + + for (int i = 0; i < COLUMN_MSG_COUNT; i++) { + QFont font = item->font(i); + font.setBold(isnew || !read); + item->setFont(i, font); + } + + item->setData(COLUMN_MSG_READ, ROLE_MSG_SORT, QString("%1_%2_%3").arg(QString(isnew ? "1" : "0"), QString(read ? "0" : "1"), item->data(COLUMN_MSG_TITLE, ROLE_MSG_SORT).toString())); +} + +void FeedReaderMessageWidget::updateMsgItem(QTreeWidgetItem *item, FeedMsgInfo &info) +{ + QString title = QString::fromUtf8(info.title.c_str()); + QDateTime qdatetime; + qdatetime.setTime_t(info.pubDate); + + /* add string to all data */ + QString sort = QString("%1_%2_%2").arg(title, qdatetime.toString("yyyyMMdd_hhmmss"), QString::fromStdString(info.feedId)); + + item->setText(COLUMN_MSG_TITLE, title); + item->setData(COLUMN_MSG_TITLE, ROLE_MSG_SORT, sort); + + QString author = QString::fromUtf8(info.author.c_str()); + item->setText(COLUMN_MSG_AUTHOR, author); + item->setData(COLUMN_MSG_AUTHOR, ROLE_MSG_SORT, author + "_" + sort); + + /* if the message is on same date show only time */ + if (qdatetime.daysTo(QDateTime::currentDateTime()) == 0) { + item->setData(COLUMN_MSG_PUBDATE, Qt::DisplayRole, qdatetime.time()); + } else { + item->setData(COLUMN_MSG_PUBDATE, Qt::DisplayRole, qdatetime); + } + item->setData(COLUMN_MSG_PUBDATE, ROLE_MSG_SORT, QString("%1_%2_%3").arg(qdatetime.toString("yyyyMMdd_hhmmss"), title, QString::fromStdString(info.msgId))); + + item->setData(COLUMN_MSG_DATA, ROLE_MSG_ID, QString::fromStdString(info.msgId)); + item->setData(COLUMN_MSG_DATA, ROLE_MSG_LINK, QString::fromUtf8(info.link.c_str())); + item->setData(COLUMN_MSG_DATA, ROLE_MSG_READ, info.flag.read); + item->setData(COLUMN_MSG_DATA, ROLE_MSG_NEW, info.flag.isnew); + + calculateMsgIconsAndFonts(item); +} + +void FeedReaderMessageWidget::feedChanged(const QString &feedId, int type) +{ + if (feedId.isEmpty()) { + return; + } + + if (feedId.toStdString() != mFeedId) { + return; + } + + if (type == NOTIFY_TYPE_DEL) { + deleteLater(); + return; + } + + if (type == NOTIFY_TYPE_MOD) { + FeedInfo feedInfo; + if (!mFeedReader->getFeedInfo(mFeedId, feedInfo)) { + return; + } + + QString name = QString::fromUtf8(feedInfo.name.c_str()); + if (name != mFeedName) { + mFeedName = name; + emit feedMessageChanged(this); + } + } +} + +void FeedReaderMessageWidget::msgChanged(const QString &feedId, const QString &msgId, int type) +{ + if (feedId.isEmpty() || msgId.isEmpty()) { + return; + } + + if (feedId.toStdString() != mFeedId) { + return; + } + + uint32_t unreadCount; + mFeedReader->getMessageCount(mFeedId, NULL, NULL, &unreadCount); + if (unreadCount != mUnreadCount) { + mUnreadCount = unreadCount; + emit feedMessageChanged(this); + } + + if (!isVisible()) { + /* complete update in showEvent */ + return; + } + + FeedMsgInfo msgInfo; + if (type != NOTIFY_TYPE_DEL) { + if (!mFeedReader->getMsgInfo(feedId.toStdString(), msgId.toStdString(), msgInfo)) { + return; + } + } + + if (type == NOTIFY_TYPE_MOD || type == NOTIFY_TYPE_DEL) { + QTreeWidgetItemIterator it(ui->msgTreeWidget); + QTreeWidgetItem *item; + while ((item = *it) != NULL) { + if (item->data(COLUMN_MSG_DATA, ROLE_MSG_ID).toString() == msgId) { + if (type == NOTIFY_TYPE_MOD) { + updateMsgItem(item, msgInfo); + filterItem(item); + } else { + delete(item); + } + break; + } + ++it; + } + } + + if (type == NOTIFY_TYPE_ADD) { + QTreeWidgetItem *item = new RSTreeWidgetItem(mMsgCompareRole); + updateMsgItem(item, msgInfo); + ui->msgTreeWidget->addTopLevelItem(item); + filterItem(item); + } +} + +void FeedReaderMessageWidget::msgItemClicked(QTreeWidgetItem *item, int column) +{ + if (item == NULL) { + return; + } + + if (column == COLUMN_MSG_READ) { + QList rows; + rows.append(item); + bool read = item->data(COLUMN_MSG_DATA, ROLE_MSG_READ).toBool(); + setMsgAsReadUnread(rows, !read); + return; + } +} + +void FeedReaderMessageWidget::msgItemChanged() +{ + long todo; // show link somewhere + + std::string msgId = currentMsgId(); + + if (mFeedId.empty() || msgId.empty()) { + ui->msgTitle->clear(); +// ui->msgLink->clear(); + ui->msgText->clear(); + ui->linkButton->setEnabled(false); + return; + } + + QTreeWidgetItem *item = ui->msgTreeWidget->currentItem(); + if (!item) { + /* there is something wrong */ + ui->msgTitle->clear(); +// ui->msgLink->clear(); + ui->msgText->clear(); + ui->linkButton->setEnabled(false); + return; + } + + /* get msg */ + FeedMsgInfo msgInfo; + if (!mFeedReader->getMsgInfo(mFeedId, msgId, msgInfo)) { + ui->msgTitle->clear(); +// ui->msgLink->clear(); + ui->msgText->clear(); + ui->linkButton->setEnabled(false); + return; + } + + bool setToReadOnActive = Settings->valueFromGroup("FeedReaderDialog", "SetMsgToReadOnActivate", true).toBool(); + bool isnew = item->data(COLUMN_MSG_DATA, ROLE_MSG_NEW).toBool(); + bool read = item->data(COLUMN_MSG_DATA, ROLE_MSG_READ).toBool(); + + QList row; + row.append(item); + if (read) { + if (isnew) { + /* something wrong, but set as read again to clear the new flag */ + setMsgAsReadUnread(row, true); + } + } else { + /* set to read/unread */ + setMsgAsReadUnread(row, setToReadOnActive); + } + + QString msgTxt = RsHtml().formatText(ui->msgText->document(), QString::fromUtf8(msgInfo.description.c_str()), RSHTML_FORMATTEXT_EMBED_LINKS); + + ui->msgText->setHtml(msgTxt); + ui->msgTitle->setText(QString::fromUtf8(msgInfo.title.c_str())); + + ui->linkButton->setEnabled(!msgInfo.link.empty()); +} + +void FeedReaderMessageWidget::setMsgAsReadUnread(QList &rows, bool read) +{ + QList::iterator rowIt; + + if (mFeedId.empty()) { + return; + } + + for (rowIt = rows.begin(); rowIt != rows.end(); rowIt++) { + QTreeWidgetItem *item = *rowIt; + bool rowRead = item->data(COLUMN_MSG_DATA, ROLE_MSG_READ).toBool(); + bool rowNew = item->data(COLUMN_MSG_DATA, ROLE_MSG_NEW).toBool(); + + if (rowNew || read != rowRead) { + std::string msgId = item->data(COLUMN_MSG_DATA, ROLE_MSG_ID).toString().toStdString(); + mFeedReader->setMessageRead(mFeedId, msgId, read); + } + } +} + +void FeedReaderMessageWidget::filterColumnChanged(int column) +{ + if (mProcessSettings) { + return; + } + + filterItems(ui->filterLineEdit->text()); + + // save index + Settings->setValueToGroup("FeedReaderDialog", "filterColumn", column); +} + +void FeedReaderMessageWidget::filterItems(const QString& text) +{ + int filterColumn = ui->filterLineEdit->currentFilter(); + + int count = ui->msgTreeWidget->topLevelItemCount(); + for (int index = 0; index < count; ++index) { + filterItem(ui->msgTreeWidget->topLevelItem(index), text, filterColumn); + } +} + +void FeedReaderMessageWidget::filterItem(QTreeWidgetItem *item, const QString &text, int filterColumn) +{ + if (text.isEmpty() == false) { + if (item->text(filterColumn).contains(text, Qt::CaseInsensitive)) { + item->setHidden(false); + } else { + item->setHidden(true); + } + } else { + item->setHidden(false); + } +} + +void FeedReaderMessageWidget::filterItem(QTreeWidgetItem *item) +{ + filterItem(item, ui->filterLineEdit->text(), ui->filterLineEdit->currentFilter()); +} + +void FeedReaderMessageWidget::toggleMsgText() +{ + // save state of button + Settings->setValueToGroup("FeedReaderDialog", "expandButton", ui->expandButton->isChecked()); + + toggleMsgText_internal(); +} + +void FeedReaderMessageWidget::toggleMsgText_internal() +{ + if (ui->expandButton->isChecked()) { + ui->msgText->setVisible(true); + ui->expandButton->setIcon(QIcon(QString(":/images/edit_remove24.png"))); + ui->expandButton->setToolTip(tr("Hide")); + } else { + ui->msgText->setVisible(false); + ui->expandButton->setIcon(QIcon(QString(":/images/edit_add24.png"))); + ui->expandButton->setToolTip(tr("Expand")); + } +} + +void FeedReaderMessageWidget::markAsReadMsg() +{ + QList selectedItems = ui->msgTreeWidget->selectedItems(); + setMsgAsReadUnread(selectedItems, true); +} + +void FeedReaderMessageWidget::markAsUnreadMsg() +{ + QList selectedItems = ui->msgTreeWidget->selectedItems(); + setMsgAsReadUnread(selectedItems, false); +} + +void FeedReaderMessageWidget::markAllAsReadMsg() +{ + QList items; + + QTreeWidgetItemIterator it(ui->msgTreeWidget); + QTreeWidgetItem *item; + while ((item = *it) != NULL) { + if (!item->isHidden()) { + items.push_back(item); + } + ++it; + } + setMsgAsReadUnread(items, true); +} + +void FeedReaderMessageWidget::copyLinksMsg() +{ + QString links; + + QTreeWidgetItemIterator it(ui->msgTreeWidget, QTreeWidgetItemIterator::Selected); + QTreeWidgetItem *item; + while ((item = *it) != NULL) { + QString link = item->data(COLUMN_MSG_DATA, ROLE_MSG_LINK).toString(); + if (!link.isEmpty()) { + links += link + "\n"; + } + ++it; + } + + if (links.isEmpty()) { + return; + } + + QApplication::clipboard()->setText(links); +} + +void FeedReaderMessageWidget::removeMsg() +{ + if (mFeedId.empty()) { + return; + } + + QList selectedItems = ui->msgTreeWidget->selectedItems(); + QList::iterator it; + std::list msgIds; + + for (it = selectedItems.begin(); it != selectedItems.end(); ++it) { + msgIds.push_back((*it)->data(COLUMN_MSG_DATA, ROLE_MSG_ID).toString().toStdString()); + } + mFeedReader->removeMsgs(mFeedId, msgIds); +} + +void FeedReaderMessageWidget::copyLinkMsg() +{ + QTreeWidgetItem *item = ui->msgTreeWidget->currentItem(); + if (!item) { + return; + } + + QString link = item->data(COLUMN_MSG_DATA, ROLE_MSG_LINK).toString(); + if (link.isEmpty()) { + return; + } + + QApplication::clipboard()->setText(link); +} + +void FeedReaderMessageWidget::openLinkMsg() +{ + QTreeWidgetItem *item = ui->msgTreeWidget->currentItem(); + if (!item) { + return; + } + + QString link = item->data(COLUMN_MSG_DATA, ROLE_MSG_LINK).toString(); + if (link.isEmpty()) { + return; + } + + QDesktopServices::openUrl(QUrl(link)); +} diff --git a/plugins/FeedReader/gui/FeedReaderMessageWidget.h b/plugins/FeedReader/gui/FeedReaderMessageWidget.h new file mode 100644 index 000000000..738059dd6 --- /dev/null +++ b/plugins/FeedReader/gui/FeedReaderMessageWidget.h @@ -0,0 +1,78 @@ +#ifndef FEEDREADERMESSAGEWIDGET_H +#define FEEDREADERMESSAGEWIDGET_H + +#include + +namespace Ui { +class FeedReaderMessageWidget; +} + +class FeedMsgInfo; +class QTreeWidgetItem; +class RSTreeWidgetItemCompareRole; +class RsFeedReader; +class FeedReaderNotify; + +class FeedReaderMessageWidget : public QWidget +{ + Q_OBJECT + +public: + explicit FeedReaderMessageWidget(const std::string &feedId, RsFeedReader *feedReader, FeedReaderNotify *notify, QWidget *parent = 0); + ~FeedReaderMessageWidget(); + + std::string feedId() { return mFeedId; } + QString feedName(bool withUnreadCount); + QIcon feedIcon(); + +protected: + virtual void showEvent(QShowEvent *e); + bool eventFilter(QObject *obj, QEvent *ev); + +signals: + void feedMessageChanged(QWidget *widget); + +private slots: + void msgTreeCustomPopupMenu(QPoint point); + void msgItemChanged(); + void msgItemClicked(QTreeWidgetItem *item, int column); + void filterColumnChanged(int column); + void filterItems(const QString &text); + void toggleMsgText(); + void markAsReadMsg(); + void markAsUnreadMsg(); + void markAllAsReadMsg(); + void copyLinksMsg(); + void removeMsg(); + void openLinkMsg(); + void copyLinkMsg(); + + /* FeedReaderNotify */ + void feedChanged(const QString &feedId, int type); + void msgChanged(const QString &feedId, const QString &msgId, int type); + +private: + std::string currentMsgId(); + void processSettings(bool load); + void updateMsgs(); + void calculateMsgIconsAndFonts(QTreeWidgetItem *item); + void updateMsgItem(QTreeWidgetItem *item, FeedMsgInfo &info); + void setMsgAsReadUnread(QList &rows, bool read); + void filterItem(QTreeWidgetItem *item, const QString &text, int filterColumn); + void filterItem(QTreeWidgetItem *item); + void toggleMsgText_internal(); + + bool mProcessSettings; + RSTreeWidgetItemCompareRole *mMsgCompareRole; + std::string mFeedId; + QString mFeedName; + unsigned int mUnreadCount; + + // gui interface + RsFeedReader *mFeedReader; + FeedReaderNotify *mNotify; + + Ui::FeedReaderMessageWidget *ui; +}; + +#endif // FEEDREADERMESSAGEWIDGET_H diff --git a/plugins/FeedReader/gui/FeedReaderMessageWidget.ui b/plugins/FeedReader/gui/FeedReaderMessageWidget.ui new file mode 100644 index 000000000..e4b748824 --- /dev/null +++ b/plugins/FeedReader/gui/FeedReaderMessageWidget.ui @@ -0,0 +1,193 @@ + + + FeedReaderMessageWidget + + + + 0 + 0 + 658 + 414 + + + + Form + + + + 0 + + + 0 + + + + + Qt::Vertical + + + + + + + QFrame::Box + + + QFrame::Sunken + + + + 2 + + + + + Search forums + + + + + + + + + + + 9 + + + + Qt::CustomContextMenu + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::ExtendedSelection + + + true + + + true + + + + Title + + + + + + + + + :/images/message-state-header.png:/images/message-state-header.png + + + + + Date + + + + + Author + + + + + + + + 2 + + + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + :/images/Link.png:/images/Link.png + + + QToolButton::MenuButtonPopup + + + true + + + + + + + + 24 + 24 + + + + Qt::NoFocus + + + + + + + :/images/edit_remove24.png:/images/edit_remove24.png + + + true + + + true + + + + + + + + + + + 0 + 10 + + + + + 9 + + + + + + + + + + LineEditClear + QLineEdit +
gui/common/LineEditClear.h
+
+ + LinkTextBrowser + QTextBrowser +
gui/common/LinkTextBrowser.h
+
+
+ + + + + +