mirror of
https://github.com/RetroShare/RetroShare.git
synced 2024-10-01 02:35:48 -04:00
f710dba2c6
- fixed compile on Linux - added compatibility with iconv git-svn-id: http://svn.code.sf.net/p/retroshare/code/branches/v0.5-gxs-b1@5378 b45a01b8-16f6-495d-af2f-9b41ad6348cc
1089 lines
31 KiB
C++
1089 lines
31 KiB
C++
/****************************************************************
|
|
* RetroShare GUI is distributed under the following license:
|
|
*
|
|
* Copyright (C) 2012 by Thunder
|
|
*
|
|
* This program 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 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program 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 this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
****************************************************************/
|
|
|
|
#include <QDateTime>
|
|
#include <QKeyEvent>
|
|
#include <QMenu>
|
|
#include <QInputDialog>
|
|
#include <QPixmap>
|
|
#include <QPainter>
|
|
#include <QMessageBox>
|
|
#include <QClipboard>
|
|
|
|
#include "FeedReaderDialog.h"
|
|
#include "ui_FeedReaderDialog.h"
|
|
#include "FeedReaderNotify.h"
|
|
#include "AddFeedDialog.h"
|
|
#include "gui/common/RSTreeWidgetItem.h"
|
|
#include "util/HandleRichText.h"
|
|
#include "gui/settings/rsharesettings.h"
|
|
#include "interface/rsFeedReader.h"
|
|
#include "retroshare/rsiface.h"
|
|
|
|
#define COLUMN_FEED_COUNT 1
|
|
#define COLUMN_FEED_NAME 0
|
|
#define COLUMN_FEED_DATA 0
|
|
|
|
#define ROLE_FEED_ID Qt::UserRole
|
|
#define ROLE_FEED_SORT Qt::UserRole + 1
|
|
#define ROLE_FEED_FOLDER Qt::UserRole + 2
|
|
#define ROLE_FEED_UNREAD Qt::UserRole + 3
|
|
#define ROLE_FEED_NAME Qt::UserRole + 4
|
|
#define ROLE_FEED_WORKSTATE Qt::UserRole + 5
|
|
#define ROLE_FEED_LOADING Qt::UserRole + 6
|
|
#define ROLE_FEED_ICON Qt::UserRole + 7
|
|
#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
|
|
|
|
static int filterColumnToComboBox(int nIndex)
|
|
{
|
|
switch (nIndex) {
|
|
case COLUMN_MSG_TITLE:
|
|
return 0;
|
|
case COLUMN_MSG_PUBDATE:
|
|
return 1;
|
|
case COLUMN_MSG_AUTHOR:
|
|
return 2;
|
|
}
|
|
|
|
return filterColumnToComboBox(COLUMN_MSG_TITLE);
|
|
}
|
|
|
|
static int filterColumnFromComboBox(int nIndex)
|
|
{
|
|
switch (nIndex) {
|
|
case 0:
|
|
return COLUMN_MSG_TITLE;
|
|
case 1:
|
|
return COLUMN_MSG_PUBDATE;
|
|
case 2:
|
|
return COLUMN_MSG_AUTHOR;
|
|
}
|
|
|
|
return COLUMN_MSG_TITLE;
|
|
}
|
|
|
|
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);
|
|
|
|
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->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->filterColumnComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(filterColumnChanged()));
|
|
|
|
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");
|
|
mRootItem->setText(COLUMN_FEED_NAME, name);
|
|
mRootItem->setIcon(COLUMN_FEED_NAME, QIcon(":/images/Root.png"));
|
|
mRootItem->setData(COLUMN_FEED_DATA, ROLE_FEED_NAME, name);
|
|
mRootItem->setData(COLUMN_FEED_DATA, ROLE_FEED_FOLDER, true);
|
|
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<int> 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, "");
|
|
|
|
/* 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);
|
|
|
|
ui->msgTreeWidget->installEventFilter(this);
|
|
}
|
|
|
|
FeedReaderDialog::~FeedReaderDialog()
|
|
{
|
|
/* save settings */
|
|
processSettings(false);
|
|
|
|
delete(mFeedCompareRole);
|
|
delete(mMsgCompareRole);
|
|
delete(ui);
|
|
|
|
mFeedReader->setNotify(NULL);
|
|
delete(mNotify);
|
|
}
|
|
|
|
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->filterColumnComboBox->setCurrentIndex(filterColumnToComboBox(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());
|
|
} 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());
|
|
}
|
|
|
|
Settings->endGroup();
|
|
mProcessSettings = false;
|
|
}
|
|
|
|
void FeedReaderDialog::showEvent(QShowEvent */*event*/)
|
|
{
|
|
updateFeeds("", mRootItem);
|
|
}
|
|
|
|
bool FeedReaderDialog::eventFilter(QObject *obj, QEvent *event)
|
|
{
|
|
if (obj == ui->msgTreeWidget) {
|
|
if (event->type() == QEvent::KeyPress) {
|
|
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(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<QKeyEvent*>(event);
|
|
if (keyEvent) {
|
|
if (keyEvent->key() == Qt::Key_Delete) {
|
|
/* Delete pressed */
|
|
removeFeed();
|
|
return true; // eat event
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* pass the event on to the parent class */
|
|
return MainPage::eventFilter(obj, event);
|
|
}
|
|
|
|
std::string FeedReaderDialog::currentFeedId()
|
|
{
|
|
QTreeWidgetItem *item = ui->feedTreeWidget->currentItem();
|
|
if (!item) {
|
|
return "";
|
|
}
|
|
|
|
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);
|
|
|
|
bool folder = false;
|
|
QTreeWidgetItem *item = ui->feedTreeWidget->currentItem();
|
|
if (item) {
|
|
folder = item->data(COLUMN_FEED_DATA, ROLE_FEED_FOLDER).toBool();
|
|
}
|
|
|
|
QMenu *menu = contextMnu.addMenu(QIcon(""), tr("New"));
|
|
QAction *action = menu->addAction(QIcon(":/images/FeedAdd.png"), tr("Feed"), this, SLOT(newFeed()));
|
|
if (!item || !folder) {
|
|
action->setEnabled(false);
|
|
}
|
|
action = menu->addAction(QIcon(":/images/FolderAdd.png"), tr("Folder"), this, SLOT(newFolder()));
|
|
if (!item || !folder) {
|
|
action->setEnabled(false);
|
|
}
|
|
|
|
contextMnu.addSeparator();
|
|
|
|
action = contextMnu.addAction(QIcon(":/images/edit_16.png"), tr("Edit"), this, SLOT(editFeed()));
|
|
if (!item || item == mRootItem) {
|
|
action->setEnabled(false);
|
|
}
|
|
|
|
action = contextMnu.addAction(QIcon(":/images/delete.png"), tr("Delete"), this, SLOT(removeFeed()));
|
|
if (!item || item == mRootItem) {
|
|
action->setEnabled(false);
|
|
}
|
|
|
|
contextMnu.addSeparator();
|
|
|
|
bool deactivated = false;
|
|
if (!folder && item) {
|
|
deactivated = item->data(COLUMN_FEED_DATA, ROLE_FEED_DEACTIVATED).toBool();
|
|
}
|
|
|
|
action = contextMnu.addAction(QIcon(":/images/Update.png"), tr("Update"), this, SLOT(processFeed()));
|
|
action->setEnabled(!deactivated);
|
|
|
|
action = contextMnu.addAction(QIcon(""), deactivated ? tr("Activate") : tr("Deactivate"), this, SLOT(activateFeed()));
|
|
if (!item || item == mRootItem || folder) {
|
|
action->setEnabled(false);
|
|
}
|
|
|
|
contextMnu.exec(QCursor::pos());
|
|
}
|
|
|
|
void FeedReaderDialog::msgTreeCustomPopupMenu(QPoint /*point*/)
|
|
{
|
|
QMenu contextMnu(this);
|
|
|
|
QList<QTreeWidgetItem*> 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(copyLinkMsg()));
|
|
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) {
|
|
return;
|
|
}
|
|
|
|
/* get feed infos */
|
|
std::list<FeedInfo> feedInfos;
|
|
mFeedReader->getFeedList(parentId, feedInfos);
|
|
|
|
int index = 0;
|
|
QTreeWidgetItem *item;
|
|
std::list<FeedInfo>::iterator feedIt;
|
|
|
|
/* update existing and delete not existing feeds */
|
|
while (index < parentItem->childCount()) {
|
|
item = parentItem->child(index);
|
|
std::string feedId = item->data(COLUMN_FEED_DATA, ROLE_FEED_ID).toString().toStdString();
|
|
|
|
/* search existing feed */
|
|
int found = -1;
|
|
for (feedIt = feedInfos.begin (); feedIt != feedInfos.end (); ++feedIt) {
|
|
if (feedIt->feedId == feedId) {
|
|
/* found it, update it */
|
|
updateFeedItem(item, *feedIt);
|
|
|
|
if (feedIt->flag.folder) {
|
|
/* process child feeds */
|
|
updateFeeds(feedIt->feedId, item);
|
|
}
|
|
|
|
feedInfos.erase(feedIt);
|
|
found = index;
|
|
break;
|
|
}
|
|
}
|
|
if (found >= 0) {
|
|
++index;
|
|
} else {
|
|
delete(parentItem->takeChild(index));
|
|
}
|
|
}
|
|
|
|
/* add new feeds */
|
|
for (feedIt = feedInfos.begin (); feedIt != feedInfos.end (); ++feedIt) {
|
|
item = new RSTreeWidgetItem(mFeedCompareRole);
|
|
parentItem->addChild(item);
|
|
updateFeedItem(item, *feedIt);
|
|
|
|
if (feedIt->flag.folder) {
|
|
/* process child feeds */
|
|
updateFeeds(feedIt->feedId, item);
|
|
}
|
|
}
|
|
calculateFeedItems();
|
|
}
|
|
|
|
void FeedReaderDialog::calculateFeedItem(QTreeWidgetItem *item, uint32_t &unreadCount, bool &loading)
|
|
{
|
|
uint32_t unreadCountItem = 0;
|
|
bool loadingItem = false;
|
|
|
|
if (item->data(COLUMN_FEED_DATA, ROLE_FEED_FOLDER).toBool()) {
|
|
int childCount = item->childCount();
|
|
for (int index = 0; index < childCount; ++index) {
|
|
calculateFeedItem(item->child(index), unreadCountItem, loadingItem);
|
|
}
|
|
} else {
|
|
unreadCountItem = item->data(COLUMN_FEED_DATA, ROLE_FEED_UNREAD).toUInt();
|
|
loadingItem = item->data(COLUMN_FEED_DATA, ROLE_FEED_LOADING).toBool();
|
|
}
|
|
|
|
unreadCount += unreadCountItem;
|
|
loading = loading || loadingItem;
|
|
|
|
QString name = item->data(COLUMN_FEED_DATA, ROLE_FEED_NAME).toString();
|
|
QString workState = item->data(COLUMN_FEED_DATA, ROLE_FEED_WORKSTATE).toString();
|
|
|
|
if (unreadCountItem) {
|
|
name += QString(" (%1)").arg(unreadCountItem);
|
|
}
|
|
if (!workState.isEmpty()) {
|
|
name += QString(" (%1)").arg(workState);
|
|
}
|
|
|
|
item->setText(COLUMN_FEED_NAME, name);
|
|
|
|
bool deactivated = item->data(COLUMN_FEED_DATA, ROLE_FEED_DEACTIVATED).toBool();
|
|
|
|
QColor colorActivated;
|
|
QColor colorDeactivated = QColor(Qt::gray);
|
|
for (int i = 0; i < COLUMN_FEED_COUNT; i++) {
|
|
QFont font = item->font(i);
|
|
font.setBold(unreadCountItem != 0);
|
|
item->setFont(i, font);
|
|
|
|
item->setTextColor(COLUMN_FEED_NAME, deactivated ? colorDeactivated : colorActivated);
|
|
}
|
|
|
|
QIcon icon = item->data(COLUMN_FEED_DATA, ROLE_FEED_ICON).value<QIcon>();
|
|
|
|
if (deactivated) {
|
|
/* create disabled icon */
|
|
icon = icon.pixmap(QSize(16, 16), QIcon::Disabled);
|
|
}
|
|
|
|
QImage overlayIcon;
|
|
if (loadingItem) {
|
|
/* overlaying icon */
|
|
overlayIcon = QImage(":/images/FeedProcessOverlay.png");
|
|
} else if (item->data(COLUMN_FEED_DATA, ROLE_FEED_ERROR).toBool()) {
|
|
overlayIcon = QImage(":/images/FeedErrorOverlay.png");
|
|
}
|
|
if (!overlayIcon.isNull()) {
|
|
if (icon.isNull()) {
|
|
icon = QPixmap::fromImage(overlayIcon);
|
|
} else {
|
|
QPixmap pixmap = icon.pixmap(QSize(16, 16));
|
|
QPainter painter(&pixmap);
|
|
painter.drawImage(0, 0, overlayIcon.scaled(pixmap.size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
|
painter.end();
|
|
icon = pixmap;
|
|
}
|
|
}
|
|
|
|
item->setIcon(COLUMN_FEED_NAME, icon);
|
|
}
|
|
|
|
void FeedReaderDialog::calculateFeedItems()
|
|
{
|
|
uint32_t unreadCount = 0;
|
|
bool loading = false;
|
|
|
|
calculateFeedItem(mRootItem, unreadCount, loading);
|
|
ui->feedTreeWidget->sortItems(COLUMN_FEED_NAME, Qt::AscendingOrder);
|
|
}
|
|
|
|
void FeedReaderDialog::updateFeedItem(QTreeWidgetItem *item, FeedInfo &info)
|
|
{
|
|
QString workState;
|
|
|
|
QIcon icon;
|
|
if (info.flag.folder) {
|
|
/* use folder icon */
|
|
icon = QIcon(":/images/Folder.png");
|
|
} else {
|
|
long todo; // show icon from feed
|
|
// if (info.icon.empty()) {
|
|
/* use standard icon */
|
|
icon = QIcon(":/images/Feed.png");
|
|
// } else {
|
|
// /* use icon from feed */
|
|
// icon = QIcon(QPixmap::fromImage(QImage((uchar*) QByteArray::fromBase64(info.icon.c_str()).constData(), 16, 16, QImage::Format_RGB32)));
|
|
// }
|
|
}
|
|
|
|
item->setData(COLUMN_FEED_DATA, ROLE_FEED_ICON, icon);
|
|
|
|
switch (info.workstate) {
|
|
case FeedInfo::WAITING:
|
|
break;
|
|
case FeedInfo::WAITING_TO_DOWNLOAD:
|
|
case FeedInfo::DOWNLOADING:
|
|
workState = tr("loading");
|
|
break;
|
|
case FeedInfo::WAITING_TO_PROCESS:
|
|
case FeedInfo::PROCESSING:
|
|
workState = tr("processing");
|
|
break;
|
|
}
|
|
|
|
QString name = QString::fromUtf8(info.name.c_str());
|
|
item->setData(COLUMN_FEED_DATA, ROLE_FEED_NAME, name.isEmpty() ? tr("No name") : name);
|
|
item->setData(COLUMN_FEED_DATA, ROLE_FEED_WORKSTATE, workState);
|
|
|
|
uint32_t unreadCount;
|
|
mFeedReader->getMessageCount(info.feedId, NULL, NULL, &unreadCount);
|
|
|
|
item->setData(COLUMN_FEED_NAME, ROLE_FEED_SORT, QString("%1_%2").arg(QString(info.flag.folder ? "0" : "1"), name));
|
|
item->setData(COLUMN_FEED_DATA, ROLE_FEED_UNREAD, unreadCount);
|
|
item->setData(COLUMN_FEED_DATA, ROLE_FEED_LOADING, info.workstate != FeedInfo::WAITING);
|
|
item->setData(COLUMN_FEED_DATA, ROLE_FEED_ID, QString::fromStdString(info.feedId));
|
|
item->setData(COLUMN_FEED_DATA, ROLE_FEED_FOLDER, info.flag.folder);
|
|
item->setData(COLUMN_FEED_DATA, ROLE_FEED_DEACTIVATED, info.flag.deactivated);
|
|
item->setData(COLUMN_FEED_DATA, ROLE_FEED_ERROR, info.error);
|
|
item->setToolTip(COLUMN_FEED_NAME, info.error ? QString::fromUtf8(info.errorString.c_str()) : "");
|
|
}
|
|
|
|
void FeedReaderDialog::updateMsgs(const std::string &feedId)
|
|
{
|
|
std::list<FeedMsgInfo> msgInfos;
|
|
if (!mFeedReader->getFeedMsgList(feedId, msgInfos)) {
|
|
ui->msgTreeWidget->clear();
|
|
return;
|
|
}
|
|
|
|
int index = 0;
|
|
QTreeWidgetItem *item;
|
|
std::list<FeedMsgInfo>::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()) {
|
|
/* complete update in showEvent */
|
|
return;
|
|
}
|
|
|
|
if (feedId.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
FeedInfo feedInfo;
|
|
if (type != NOTIFY_TYPE_DEL) {
|
|
if (!mFeedReader->getFeedInfo(feedId.toStdString(), feedInfo)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (type == NOTIFY_TYPE_MOD || type == NOTIFY_TYPE_DEL) {
|
|
QTreeWidgetItemIterator it(ui->feedTreeWidget);
|
|
QTreeWidgetItem *item;
|
|
while ((item = *it) != NULL) {
|
|
if (item->data(COLUMN_FEED_DATA, ROLE_FEED_ID).toString() == feedId) {
|
|
if (type == NOTIFY_TYPE_MOD) {
|
|
updateFeedItem(item, feedInfo);
|
|
} else {
|
|
delete(item);
|
|
}
|
|
break;
|
|
}
|
|
++it;
|
|
}
|
|
}
|
|
|
|
if (type == NOTIFY_TYPE_ADD) {
|
|
QString id = QString::fromStdString(feedInfo.parentId);
|
|
|
|
QTreeWidgetItemIterator it(ui->feedTreeWidget);
|
|
QTreeWidgetItem *itemParent;
|
|
while ((itemParent = *it) != NULL) {
|
|
if (itemParent->data(COLUMN_FEED_DATA, ROLE_FEED_ID).toString() == id) {
|
|
QTreeWidgetItem *item = new RSTreeWidgetItem(mFeedCompareRole);
|
|
itemParent->addChild(item);
|
|
updateFeedItem(item, feedInfo);
|
|
break;
|
|
}
|
|
++it;
|
|
}
|
|
}
|
|
calculateFeedItems();
|
|
}
|
|
|
|
void FeedReaderDialog::msgChanged(const QString &feedId, const QString &msgId, int type)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
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 FeedReaderDialog::feedItemChanged(QTreeWidgetItem *item)
|
|
{
|
|
if (!item) {
|
|
ui->msgTreeWidget->clear();
|
|
return;
|
|
}
|
|
|
|
std::string feedId = item->data(COLUMN_FEED_DATA, ROLE_FEED_ID).toString().toStdString();
|
|
updateMsgs(feedId);
|
|
}
|
|
|
|
void FeedReaderDialog::msgItemClicked(QTreeWidgetItem *item, int column)
|
|
{
|
|
if (item == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (column == COLUMN_MSG_READ) {
|
|
QList<QTreeWidgetItem*> 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();
|
|
return;
|
|
}
|
|
|
|
QTreeWidgetItem *item = ui->msgTreeWidget->currentItem();
|
|
if (!item) {
|
|
/* there is something wrong */
|
|
ui->msgTitle->clear();
|
|
// ui->msgLink->clear();
|
|
ui->msgText->clear();
|
|
return;
|
|
}
|
|
|
|
/* get msg */
|
|
FeedMsgInfo msgInfo;
|
|
if (!mFeedReader->getMsgInfo(feedId, msgId, msgInfo)) {
|
|
ui->msgTitle->clear();
|
|
// ui->msgLink->clear();
|
|
ui->msgText->clear();
|
|
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<QTreeWidgetItem*> 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->msgLink->setHtml(RsHtml().formatText(NULL, QString::fromUtf8(msgInfo.link.c_str()), RSHTML_FORMATTEXT_EMBED_LINKS));
|
|
}
|
|
|
|
void FeedReaderDialog::setMsgAsReadUnread(QList<QTreeWidgetItem *> &rows, bool read)
|
|
{
|
|
QList<QTreeWidgetItem*>::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()
|
|
{
|
|
if (mProcessSettings) {
|
|
return;
|
|
}
|
|
|
|
filterItems(ui->filterLineEdit->text());
|
|
|
|
// save index
|
|
int filterColumn = filterColumnFromComboBox(ui->filterColumnComboBox->currentIndex());
|
|
Settings->setValueToGroup("FeedReaderDialog", "filterColumn", filterColumn);
|
|
}
|
|
|
|
void FeedReaderDialog::filterItems(const QString& text)
|
|
{
|
|
int filterColumn = filterColumnFromComboBox(ui->filterColumnComboBox->currentIndex());
|
|
|
|
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(), filterColumnFromComboBox(ui->filterColumnComboBox->currentIndex()));
|
|
}
|
|
|
|
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"));
|
|
}
|
|
}
|
|
|
|
void FeedReaderDialog::newFolder()
|
|
{
|
|
QInputDialog dialog;
|
|
dialog.setWindowTitle(tr("Add new folder"));
|
|
dialog.setLabelText(tr("Please enter a name for the folder"));
|
|
dialog.setWindowIcon(QIcon(":/images/FeedReader.png"));
|
|
|
|
if (dialog.exec() == QDialog::Accepted && !dialog.textValue().isEmpty()) {
|
|
std::string feedId;
|
|
RsFeedAddResult result = mFeedReader->addFolder(currentFeedId(), dialog.textValue().toUtf8().constData(), feedId);
|
|
AddFeedDialog::showError(this, result, tr("Create folder"), tr("Cannot create folder."));
|
|
}
|
|
}
|
|
|
|
void FeedReaderDialog::newFeed()
|
|
{
|
|
AddFeedDialog dialog(mFeedReader, this);
|
|
dialog.setParent(currentFeedId());
|
|
dialog.exec();
|
|
}
|
|
|
|
void FeedReaderDialog::removeFeed()
|
|
{
|
|
std::string feedId = currentFeedId();
|
|
if (feedId.empty()) {
|
|
return;
|
|
}
|
|
|
|
QTreeWidgetItem *item = ui->feedTreeWidget->currentItem();
|
|
if (!item) {
|
|
return;
|
|
}
|
|
|
|
bool folder = item->data(COLUMN_FEED_DATA, ROLE_FEED_FOLDER).toBool();
|
|
QString name = item->data(COLUMN_FEED_DATA, ROLE_FEED_NAME).toString();
|
|
|
|
if (QMessageBox::question(this, folder ? tr("Remove folder") : tr("Remove feed"), folder ? tr("Do you want to remove the folder %1?").arg(name) : tr("Do you want to remove the feed %1?").arg(name), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) {
|
|
mFeedReader->removeFeed(feedId);
|
|
}
|
|
}
|
|
|
|
void FeedReaderDialog::editFeed()
|
|
{
|
|
std::string feedId = currentFeedId();
|
|
if (feedId.empty()) {
|
|
return;
|
|
}
|
|
|
|
QTreeWidgetItem *item = ui->feedTreeWidget->currentItem();
|
|
if (!item) {
|
|
return;
|
|
}
|
|
|
|
bool folder = item->data(COLUMN_FEED_DATA, ROLE_FEED_FOLDER).toBool();
|
|
if (folder) {
|
|
QInputDialog dialog;
|
|
dialog.setWindowTitle(tr("Edit folder"));
|
|
dialog.setLabelText(tr("Please enter a new name for the folder"));
|
|
dialog.setWindowIcon(QIcon(":/images/FeedReader.png"));
|
|
dialog.setTextValue(item->data(COLUMN_FEED_DATA, ROLE_FEED_NAME).toString());
|
|
|
|
if (dialog.exec() == QDialog::Accepted && !dialog.textValue().isEmpty()) {
|
|
RsFeedAddResult result = mFeedReader->setFolder(feedId, dialog.textValue().toUtf8().constData());
|
|
AddFeedDialog::showError(this, result, tr("Create folder"), tr("Cannot create folder."));
|
|
}
|
|
} else {
|
|
AddFeedDialog dialog(mFeedReader, this);
|
|
if (!dialog.fillFeed(feedId)) {
|
|
return;
|
|
}
|
|
dialog.exec();
|
|
|
|
}
|
|
}
|
|
|
|
void FeedReaderDialog::activateFeed()
|
|
{
|
|
std::string feedId = currentFeedId();
|
|
if (feedId.empty()) {
|
|
return;
|
|
}
|
|
|
|
FeedInfo feedInfo;
|
|
if (!mFeedReader->getFeedInfo(feedId, feedInfo)) {
|
|
return;
|
|
}
|
|
|
|
if (feedInfo.flag.folder) {
|
|
return;
|
|
}
|
|
|
|
feedInfo.flag.deactivated = !feedInfo.flag.deactivated;
|
|
|
|
mFeedReader->setFeed(feedId, feedInfo);
|
|
}
|
|
|
|
void FeedReaderDialog::processFeed()
|
|
{
|
|
std::string feedId = currentFeedId();
|
|
/* empty feed id process all feeds */
|
|
|
|
mFeedReader->processFeed(feedId);
|
|
}
|
|
|
|
void FeedReaderDialog::markAsReadMsg()
|
|
{
|
|
QList<QTreeWidgetItem*> selectedItems = ui->msgTreeWidget->selectedItems();
|
|
setMsgAsReadUnread(selectedItems, true);
|
|
}
|
|
|
|
void FeedReaderDialog::markAsUnreadMsg()
|
|
{
|
|
QList<QTreeWidgetItem*> selectedItems = ui->msgTreeWidget->selectedItems();
|
|
setMsgAsReadUnread(selectedItems, false);
|
|
}
|
|
|
|
void FeedReaderDialog::markAllAsReadMsg()
|
|
{
|
|
QList<QTreeWidgetItem*> items;
|
|
|
|
QTreeWidgetItemIterator it(ui->msgTreeWidget);
|
|
QTreeWidgetItem *item;
|
|
while ((item = *it) != NULL) {
|
|
if (!item->isHidden()) {
|
|
items.push_back(item);
|
|
}
|
|
++it;
|
|
}
|
|
setMsgAsReadUnread(items, true);
|
|
}
|
|
|
|
void FeedReaderDialog::copyLinkMsg()
|
|
{
|
|
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<QTreeWidgetItem*> selectedItems = ui->msgTreeWidget->selectedItems();
|
|
QList<QTreeWidgetItem*>::iterator it;
|
|
std::list<std::string> 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);
|
|
}
|