parent
dbdddb8729
commit
0a8aea5558
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -165,8 +165,12 @@
|
||||
<string>Web browser && 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 &next tab</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="m_actionTabsPrevious">
|
||||
<property name="text">
|
||||
<string>Go to &previous tab</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user