Fixed #132, fixed #6.

This commit is contained in:
Martin Rotter 2017-09-10 10:40:33 +02:00
parent dbdddb8729
commit 0a8aea5558
9 changed files with 165 additions and 34 deletions

View File

@ -3,6 +3,8 @@
Added:
▪ Standard account is now automatically added if RSS Guard is started with empty database.
▪ Menu action "Select next unread message" in "Messages" menu now works across all feeds, so user can navigate through all unread messages with a sigle keyboard shortcut. (#132, #6)
▪ Added two bindable menu actions (in menu "Web browser & tabs") which allow to cycle among tabs. (#6)
Fixed:
▪ Fixed build on some Unit-like operating systems.

View File

@ -65,24 +65,30 @@ FormMain::FormMain(QWidget* parent, Qt::WindowFlags f)
: QMainWindow(parent, f), m_ui(new Ui::FormMain) {
m_ui->setupUi(this);
qApp->setMainForm(this);
#if defined (USE_WEBENGINE)
m_ui->m_menuWebBrowserTabs->addAction(AdBlockManager::instance()->adBlockIcon());
m_ui->m_menuWebBrowserTabs->addAction(qApp->web()->engineSettingsAction());
#endif
// Add these actions to the list of actions of the main window.
// This allows to use actions via shortcuts
// even if main menu is not visible.
addActions(qApp->userActions());
setStatusBar(m_statusBar = new StatusBar(this));
// Prepare main window and tabs.
prepareMenus();
// Prepare tabs.
tabWidget()->feedMessageViewer()->feedsToolBar()->loadSavedActions();
tabWidget()->feedMessageViewer()->messagesToolBar()->loadSavedActions();
// Establish connections.
createConnections();
updateMessageButtonsAvailability();
updateFeedButtonsAvailability();
// Setup some appearance of the window.
setupIcons();
loadSize();
@ -107,9 +113,11 @@ StatusBar* FormMain::statusBar() const {
void FormMain::showDbCleanupAssistant() {
if (qApp->feedUpdateLock()->tryLock()) {
QScopedPointer<FormDatabaseCleanup> form_pointer(new FormDatabaseCleanup(this));
form_pointer.data()->setCleaner(qApp->feedReader()->databaseCleaner());
form_pointer.data()->exec();
FormDatabaseCleanup form(new FormDatabaseCleanup(this));
form.setCleaner(qApp->feedReader()->databaseCleaner());
form.exec();
// Reload needed stuff.
qApp->feedUpdateLock()->unlock();
tabWidget()->feedMessageViewer()->messagesView()->reloadSelections();
qApp->feedReader()->feedsModel()->reloadCountsOfWholeModel();
@ -130,20 +138,25 @@ QList<QAction*> FormMain::allActions() const {
actions << m_ui->m_actionBackupDatabaseSettings;
actions << m_ui->m_actionRestart;
actions << m_ui->m_actionQuit;
#if !defined(Q_OS_MAC)
actions << m_ui->m_actionFullscreen;
#endif
actions << m_ui->m_actionAboutGuard;
actions << m_ui->m_actionSwitchFeedsList;
actions << m_ui->m_actionSwitchMainWindow;
#if !defined(Q_OS_MAC)
actions << m_ui->m_actionSwitchMainMenu;
#endif
actions << m_ui->m_actionSwitchToolBars;
actions << m_ui->m_actionSwitchListHeaders;
actions << m_ui->m_actionSwitchStatusBar;
actions << m_ui->m_actionSwitchMessageListOrientation;
// Add feeds/messages actions.
actions << m_ui->m_actionTabsNext;
actions << m_ui->m_actionTabsPrevious;
actions << m_ui->m_actionOpenSelectedSourceArticlesExternally;
actions << m_ui->m_actionOpenSelectedMessagesInternally;
actions << m_ui->m_actionMarkAllItemsRead;
@ -174,9 +187,11 @@ QList<QAction*> FormMain::allActions() const {
actions << m_ui->m_actionSelectPreviousMessage;
actions << m_ui->m_actionSelectNextUnreadMessage;
actions << m_ui->m_actionExpandCollapseItem;
#if defined(USE_WEBENGINE)
actions << m_ui->m_actionTabNewWebBrowser;
#endif
actions << m_ui->m_actionTabsCloseAll;
actions << m_ui->m_actionTabsCloseAllExceptCurrent;
return actions;
@ -434,6 +449,7 @@ void FormMain::display() {
void FormMain::setupIcons() {
IconFactory* icon_theme_factory = qApp->icons();
// Setup icons of this main window.
m_ui->m_actionDownloadManager->setIcon(icon_theme_factory->fromTheme(QSL("emblem-downloads")));
m_ui->m_actionSettings->setIcon(icon_theme_factory->fromTheme(QSL("document-properties")));
@ -447,6 +463,7 @@ void FormMain::setupIcons() {
m_ui->m_actionRestoreDatabaseSettings->setIcon(icon_theme_factory->fromTheme(QSL("document-import")));
m_ui->m_actionDonate->setIcon(icon_theme_factory->fromTheme(QSL("applications-office")));
m_ui->m_actionDisplayWiki->setIcon(icon_theme_factory->fromTheme(QSL("applications-science")));
// View.
m_ui->m_actionSwitchMainWindow->setIcon(icon_theme_factory->fromTheme(QSL("window-close")));
m_ui->m_actionFullscreen->setIcon(icon_theme_factory->fromTheme(QSL("view-fullscreen")));
@ -457,6 +474,7 @@ void FormMain::setupIcons() {
m_ui->m_actionSwitchStatusBar->setIcon(icon_theme_factory->fromTheme(QSL("dialog-information")));
m_ui->m_actionSwitchMessageListOrientation->setIcon(icon_theme_factory->fromTheme(QSL("view-restore")));
m_ui->m_menuShowHide->setIcon(icon_theme_factory->fromTheme(QSL("view-restore")));
// Feeds/messages.
m_ui->m_menuAddItem->setIcon(icon_theme_factory->fromTheme(QSL("list-add")));
m_ui->m_actionStopRunningItemsUpdate->setIcon(icon_theme_factory->fromTheme(QSL("process-stop")));
@ -492,10 +510,14 @@ void FormMain::setupIcons() {
m_ui->m_actionServiceDelete->setIcon(icon_theme_factory->fromTheme(QSL("list-remove")));
m_ui->m_actionAddFeedIntoSelectedAccount->setIcon(icon_theme_factory->fromTheme(QSL("application-rss+xml")));
m_ui->m_actionAddCategoryIntoSelectedAccount->setIcon(icon_theme_factory->fromTheme(QSL("folder")));
// Tabs & web browser.
m_ui->m_actionTabNewWebBrowser->setIcon(icon_theme_factory->fromTheme(QSL("tab-new")));
m_ui->m_actionTabsCloseAll->setIcon(icon_theme_factory->fromTheme(QSL("window-close")));
m_ui->m_actionTabsCloseAllExceptCurrent->setIcon(icon_theme_factory->fromTheme(QSL("window-close")));
m_ui->m_actionTabsNext->setIcon(icon_theme_factory->fromTheme(QSL("go-next")));
m_ui->m_actionTabsPrevious->setIcon(icon_theme_factory->fromTheme(QSL("go-previous")));
// Setup icons on TabWidget too.
m_ui->m_tabWidget->setupIcons();
}
@ -566,23 +588,27 @@ void FormMain::createConnections() {
connect(m_ui->m_menuAccounts, &QMenu::aboutToShow, this, &FormMain::updateAccountsMenu);
connect(m_ui->m_actionServiceDelete, &QAction::triggered, m_ui->m_actionDeleteSelectedItem, &QAction::triggered);
connect(m_ui->m_actionServiceEdit, &QAction::triggered, m_ui->m_actionEditSelectedItem, &QAction::triggered);
// Menu "File" connections.
connect(m_ui->m_actionBackupDatabaseSettings, &QAction::triggered, this, &FormMain::backupDatabaseSettings);
connect(m_ui->m_actionRestoreDatabaseSettings, &QAction::triggered, this, &FormMain::restoreDatabaseSettings);
connect(m_ui->m_actionQuit, &QAction::triggered, qApp, &Application::quit);
connect(m_ui->m_actionServiceAdd, &QAction::triggered, this, &FormMain::showAddAccountDialog);
connect(m_ui->m_actionRestart, &QAction::triggered, qApp, &Application::restart);
// Menu "View" connections.
connect(m_ui->m_actionFullscreen, &QAction::toggled, this, &FormMain::switchFullscreenMode);
connect(m_ui->m_actionSwitchMainMenu, &QAction::toggled, m_ui->m_menuBar, &QMenuBar::setVisible);
connect(m_ui->m_actionSwitchMainWindow, &QAction::triggered, this, &FormMain::switchVisibility);
connect(m_ui->m_actionSwitchStatusBar, &QAction::toggled, statusBar(), &StatusBar::setVisible);
// Menu "Tools" connections.
connect(m_ui->m_actionSettings, &QAction::triggered, [this]() {
FormSettings(*this).exec();
});
connect(m_ui->m_actionDownloadManager, &QAction::triggered, m_ui->m_tabWidget, &TabWidget::showDownloadManager);
connect(m_ui->m_actionCleanupDatabase, &QAction::triggered, this, &FormMain::showDbCleanupAssistant);
// Menu "Help" connections.
connect(m_ui->m_actionAboutGuard, &QAction::triggered, [this]() {
FormAbout(this).exec();
@ -593,7 +619,10 @@ void FormMain::createConnections() {
connect(m_ui->m_actionReportBug, &QAction::triggered, this, &FormMain::reportABug);
connect(m_ui->m_actionDonate, &QAction::triggered, this, &FormMain::donate);
connect(m_ui->m_actionDisplayWiki, &QAction::triggered, this, &FormMain::showWiki);
// Tab widget connections.
connect(m_ui->m_actionTabsNext, &QAction::triggered, m_ui->m_tabWidget, &TabWidget::gotoNextTab);
connect(m_ui->m_actionTabsPrevious, &QAction::triggered, m_ui->m_tabWidget, &TabWidget::gotoPreviousTab);
connect(m_ui->m_actionTabsCloseAllExceptCurrent, &QAction::triggered, m_ui->m_tabWidget, &TabWidget::closeAllTabsExceptCurrent);
connect(m_ui->m_actionTabsCloseAll, &QAction::triggered, m_ui->m_tabWidget, &TabWidget::closeAllTabs);
connect(m_ui->m_actionTabNewWebBrowser, &QAction::triggered, m_ui->m_tabWidget, &TabWidget::addEmptyBrowser);
@ -607,6 +636,7 @@ void FormMain::createConnections() {
connect(qApp->feedReader(), &FeedReader::feedUpdatesStarted, this, &FormMain::onFeedUpdatesStarted);
connect(qApp->feedReader(), &FeedReader::feedUpdatesProgress, this, &FormMain::onFeedUpdatesProgress);
connect(qApp->feedReader(), &FeedReader::feedUpdatesFinished, this, &FormMain::onFeedUpdatesFinished);
// Toolbar forwardings.
connect(m_ui->m_actionAddFeedIntoSelectedAccount, &QAction::triggered,
tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::addFeedIntoSelectedAccount);
@ -663,7 +693,7 @@ void FormMain::createConnections() {
connect(m_ui->m_actionSelectNextMessage,
&QAction::triggered, tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::selectNextItem);
connect(m_ui->m_actionSelectNextUnreadMessage,
&QAction::triggered, tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::selectNextUnreadItem);
&QAction::triggered, tabWidget()->feedMessageViewer()->feedsView(), &FeedsView::selectNextUnreadItem);
connect(m_ui->m_actionSelectPreviousMessage,
&QAction::triggered, tabWidget()->feedMessageViewer()->messagesView(), &MessagesView::selectPreviousItem);
connect(m_ui->m_actionSwitchMessageListOrientation, &QAction::triggered,

View File

@ -165,8 +165,12 @@
<string>Web browser &amp;&amp; tabs</string>
</property>
<addaction name="m_actionTabNewWebBrowser"/>
<addaction name="separator"/>
<addaction name="m_actionTabsCloseAll"/>
<addaction name="m_actionTabsCloseAllExceptCurrent"/>
<addaction name="separator"/>
<addaction name="m_actionTabsNext"/>
<addaction name="m_actionTabsPrevious"/>
</widget>
<addaction name="m_menuFile"/>
<addaction name="m_menuView"/>
@ -721,6 +725,16 @@
<string>Close all tabs except current</string>
</property>
</action>
<action name="m_actionTabsNext">
<property name="text">
<string>Go to &amp;next tab</string>
</property>
</action>
<action name="m_actionTabsPrevious">
<property name="text">
<string>Go to &amp;previous tab</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>

View File

@ -206,6 +206,8 @@ void FeedMessageViewer::createConnections() {
#endif
// If user selects feeds, load their messages.
connect(m_feedsView, &FeedsView::itemSelected, m_messagesView, &MessagesView::loadItem);
connect(m_feedsView, &FeedsView::requestViewNextUnreadMessage, m_messagesView, &MessagesView::selectNextUnreadItem);
// State of many messages is changed, then we need
// to reload selections.
connect(m_feedsView->sourceModel(), &FeedsModel::reloadMessageListRequested,

View File

@ -49,15 +49,18 @@ FeedsView::FeedsView(QWidget* parent)
m_contextMenuEmptySpace(nullptr),
m_contextMenuOtherItems(nullptr) {
setObjectName(QSL("FeedsView"));
// Allocate models.
m_sourceModel = qApp->feedReader()->feedsModel();
m_proxyModel = qApp->feedReader()->feedsProxyModel();
// Connections.
connect(m_sourceModel, &FeedsModel::requireItemValidationAfterDragDrop, this, &FeedsView::validateItemAfterDragDrop);
connect(m_sourceModel, &FeedsModel::itemExpandRequested, this, &FeedsView::onItemExpandRequested);
connect(m_sourceModel, &FeedsModel::itemExpandStateSaveRequested, this, &FeedsView::onItemExpandStateSaveRequested);
connect(header(), &QHeaderView::sortIndicatorChanged, this, &FeedsView::saveSortState);
connect(m_proxyModel, &FeedsProxyModel::expandAfterFilterIn, this, &FeedsView::expandItemDelayed);
setModel(m_proxyModel);
setupAppearance();
}
@ -349,13 +352,75 @@ void FeedsView::selectPreviousItem() {
}
}
void FeedsView::selectNextUnreadItem() {
QModelIndex next_unread_row;
if (currentIndex().isValid()) {
next_unread_row = nextPreviousUnreadItem(currentIndex());
}
else {
next_unread_row = nextPreviousUnreadItem(m_proxyModel->index(0, MSG_DB_READ_INDEX));
}
if (next_unread_row.isValid()) {
setCurrentIndex(next_unread_row);
emit requestViewNextUnreadMessage();
}
}
QModelIndex FeedsView::nextPreviousUnreadItem(QModelIndex default_row) {
const bool started_from_zero = default_row.row() == 0 && !default_row.parent().isValid();
QModelIndex next_index = nextUnreadItem(default_row);
// There is no next message, check previous.
if (!next_index.isValid() && !started_from_zero) {
next_index = nextUnreadItem(m_proxyModel->index(0, 0));
}
return next_index;
}
QModelIndex FeedsView::nextUnreadItem(QModelIndex default_row) {
default_row = m_proxyModel->index(default_row.row(), 0, default_row.parent());
const QModelIndex starting_row = default_row;
while (true) {
bool has_unread = m_sourceModel->itemForIndex(m_proxyModel->mapToSource(default_row))->countOfUnreadMessages() > 0;
if (has_unread) {
if (m_proxyModel->hasChildren(default_row)) {
// Current index has unread items, but is expandable, go to first child.
expand(default_row);
default_row = indexBelow(default_row);
continue;
}
else {
// We found unread feed, return it.
return default_row;
}
}
else {
QModelIndex next_row = indexBelow(default_row);
if (next_row == default_row || !next_row.isValid() || starting_row == next_row) {
// We came to last row probably.
break;
}
else {
default_row = next_row;
}
}
}
return QModelIndex();
}
void FeedsView::switchVisibility() {
setVisible(!isVisible());
}
void FeedsView::expandItemDelayed(const QModelIndex& idx) {
QTimer::singleShot(100, this, [ = ] {
// TODO: Z nastavení.
setExpanded(m_proxyModel->mapFromSource(idx), true);
});
}
@ -445,6 +510,9 @@ void FeedsView::setupAppearance() {
// Setup column resize strategies.
header()->setSectionResizeMode(FDS_MODEL_TITLE_INDEX, QHeaderView::Stretch);
header()->setSectionResizeMode(FDS_MODEL_COUNTS_INDEX, QHeaderView::ResizeToContents);
header()->setStretchLastSection(false);
header()->setSortIndicatorShown(false);
setUniformRowHeights(true);
setAnimated(true);
setSortingEnabled(true);
@ -460,8 +528,6 @@ void FeedsView::setupAppearance() {
setRootIsDecorated(false);
setSelectionMode(QAbstractItemView::SingleSelection);
setItemDelegate(new StyledItemDelegateWithoutFocus(this));
header()->setStretchLastSection(false);
header()->setSortIndicatorShown(false);
}
void FeedsView::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) {
@ -541,7 +607,6 @@ void FeedsView::onItemExpandRequested(const QList<RootItem*>& items, bool exp) {
foreach (const RootItem* item, items) {
QModelIndex source_index = m_sourceModel->indexForItem(item);
QModelIndex proxy_index = m_proxyModel->mapFromSource(source_index);
//setExpanded(proxy_index, !exp);
setExpanded(proxy_index, exp);
}
}

View File

@ -90,26 +90,20 @@ class FeedsView : public QTreeView {
void selectNextItem();
void selectPreviousItem();
void selectNextUnreadItem();
// Switches visibility of the widget.
void switchVisibility();
signals:
// Emitted if user selects new feeds.
void itemSelected(RootItem* item);
// Requests opening of given messages in newspaper mode.
void requestViewNextUnreadMessage();
void openMessagesInNewspaperView(RootItem* root, const QList<Message>& messages);
protected:
// Handle selections.
void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
// React on "Del" key.
void keyPressEvent(QKeyEvent* event);
// Show custom context menu.
void contextMenuEvent(QContextMenuEvent* event);
void mouseDoubleClickEvent(QMouseEvent* event);
private slots:
@ -123,6 +117,9 @@ class FeedsView : public QTreeView {
void onItemExpandStateSaveRequested(RootItem* item);
private:
QModelIndex nextPreviousUnreadItem(QModelIndex default_row);
QModelIndex nextUnreadItem(QModelIndex default_row);
// Initializes context menus.
QMenu* initializeContextMenuCategories(RootItem* clicked_item);
QMenu* initializeContextMenuFeeds(RootItem* clicked_item);

View File

@ -269,6 +269,24 @@ int TabWidget::addBrowser(bool move_after_current, bool make_active, const QUrl&
#endif
}
void TabWidget::gotoNextTab() {
if (currentIndex() == count() - 1) {
setCurrentIndex(0);
}
else {
setCurrentIndex(currentIndex() + 1);
}
}
void TabWidget::gotoPreviousTab() {
if (currentIndex() == 0) {
setCurrentIndex(count() - 1);
}
else {
setCurrentIndex(currentIndex() - 1);
}
}
void TabWidget::indentTabText(int index) {
#if defined (Q_OS_MACOS)

View File

@ -113,6 +113,9 @@ class TabWidget : public QTabWidget {
// General method for adding WebBrowsers.
int addBrowser(bool move_after_current, bool make_active, const QUrl& initial_url = QUrl());
void gotoNextTab();
void gotoPreviousTab();
private slots:
// Fixes tabs indexes.
void fixContentsAfterMove(int from, int to);