From 20ec52ea1a3ef3d41ffc8db8f157c6080e89e5c4 Mon Sep 17 00:00:00 2001 From: Lukas Holecek Date: Sun, 26 May 2019 10:14:00 +0200 Subject: [PATCH] Add forceUnload() script function Fixes updating filtered items and menus after refresh button is clicked. Signed-off-by: Lukas Holecek --- docs/scripting-api.rst | 12 +++ src/gui/clipboardbrowserplaceholder.cpp | 8 +- src/gui/clipboardbrowserplaceholder.h | 9 +- src/gui/commandcompleterdocumentation.h | 1 + src/gui/mainwindow.cpp | 108 +++++++++++++++++------- src/gui/mainwindow.h | 18 ++-- src/scriptable/scriptable.cpp | 6 ++ src/scriptable/scriptable.h | 1 + src/scriptable/scriptableproxy.cpp | 11 ++- src/scriptable/scriptableproxy.h | 1 + src/tests/tests.cpp | 22 +++++ src/tests/tests.h | 1 + 12 files changed, 147 insertions(+), 51 deletions(-) diff --git a/docs/scripting-api.rst b/docs/scripting-api.rst index 020012f12..f49dbc4f8 100644 --- a/docs/scripting-api.rst +++ b/docs/scripting-api.rst @@ -278,6 +278,18 @@ omitted. Returns list of successfully unloaded tabs. +.. js:function:: forceUnload([tabNames...]) + + Force-unload tabs (i.e. items from memory). + + If no tabs are specified, unloads all tabs. + + Refresh button needs to be clicked to show the content of a force-unloaded + tab. + + If a tab has an editor open, the editor will be closed first even if it has + unsaved changes. + .. js:function:: count(), length(), size() Returns amount of items in current tab. diff --git a/src/gui/clipboardbrowserplaceholder.cpp b/src/gui/clipboardbrowserplaceholder.cpp index 32b523f53..8e0e37fc7 100644 --- a/src/gui/clipboardbrowserplaceholder.cpp +++ b/src/gui/clipboardbrowserplaceholder.cpp @@ -54,7 +54,6 @@ ClipboardBrowser *ClipboardBrowserPlaceholder::createBrowser() return nullptr; std::unique_ptr c( new ClipboardBrowser(m_tabName, m_sharedData, this) ); - emit browserCreated(c.get()); if ( !c->loadItems() ) { createLoadButton(); @@ -73,6 +72,7 @@ ClipboardBrowser *ClipboardBrowserPlaceholder::createBrowser() restartExpiring(); + emit browserCreated(m_browser); return m_browser; } @@ -162,6 +162,8 @@ void ClipboardBrowserPlaceholder::setActiveWidget(QWidget *widget) layout()->addWidget(widget); setFocusProxy(widget); widget->show(); + if (isVisible()) + widget->setFocus(); } void ClipboardBrowserPlaceholder::createLoadButton() @@ -170,6 +172,8 @@ void ClipboardBrowserPlaceholder::createLoadButton() return; m_loadButton = new QPushButton(this); + m_loadButton->setObjectName("ClipboardBrowserRefreshButton"); + m_loadButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); m_loadButton->setFlat(true); const QIcon icon( getIcon("", IconRedo) ); @@ -190,6 +194,8 @@ void ClipboardBrowserPlaceholder::unloadBrowser() m_browser->saveUnsavedItems(); m_browser->deleteLater(); m_browser = nullptr; + + emit browserDestroyed(); } bool ClipboardBrowserPlaceholder::canExpire() const diff --git a/src/gui/clipboardbrowserplaceholder.h b/src/gui/clipboardbrowserplaceholder.h index ed2854faf..0757dd1a2 100644 --- a/src/gui/clipboardbrowserplaceholder.h +++ b/src/gui/clipboardbrowserplaceholder.h @@ -65,8 +65,13 @@ class ClipboardBrowserPlaceholder final : public QWidget /// Unload browser and data. bool expire(); + void unloadBrowser(); + + void createLoadButton(); + signals: void browserCreated(ClipboardBrowser *browser); + void browserDestroyed(); protected: void showEvent(QShowEvent *event) override; @@ -75,10 +80,6 @@ class ClipboardBrowserPlaceholder final : public QWidget private: void setActiveWidget(QWidget *widget); - void createLoadButton(); - - void unloadBrowser(); - bool canExpire() const; void restartExpiring(); diff --git a/src/gui/commandcompleterdocumentation.h b/src/gui/commandcompleterdocumentation.h index b1b48e208..04f957bf7 100644 --- a/src/gui/commandcompleterdocumentation.h +++ b/src/gui/commandcompleterdocumentation.h @@ -40,6 +40,7 @@ void addDocumentation(AddDocumentationCallback addDocumentation) addDocumentation("tabIcon", "String tabIcon(tabName)", "Returns path to icon for tab."); addDocumentation("tabIcon", "tabIcon(tabName, iconPath)", "Sets icon for tab."); addDocumentation("unload", "String[] unload([tabNames...])", "Unload tabs (i.e. items from memory)."); + addDocumentation("forceUnload", "forceUnload([tabNames...])", "Force-unload tabs (i.e. items from memory)."); addDocumentation("count", "count(), length(), size()", "Returns amount of items in current tab."); addDocumentation("select", "select(row)", "Copies item in the row to clipboard."); addDocumentation("next", "next()", "Copies next item from current tab to clipboard."); diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index 5439e0e83..7e3d12fc1 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -1007,6 +1007,25 @@ void MainWindow::onBrowserCreated(ClipboardBrowser *browser) ui->searchBar, &Utils::FilterLineEdit::hide ); connect( browser, &ClipboardBrowser::itemWidgetCreated, this, &MainWindow::onItemWidgetCreated ); + + const ClipboardBrowserPlaceholder *currentPlaceholder = getPlaceholder(); + if (currentPlaceholder && currentPlaceholder->browser() == browser) { + const int index = ui->tabWidget->currentIndex(); + tabChanged(index, index); + } + + const ClipboardBrowserPlaceholder *placeholderForTrayMenu = getPlaceholderForTrayMenu(); + if (placeholderForTrayMenu && placeholderForTrayMenu->browser() == browser) + updateTrayMenu(); +} + +void MainWindow::onBrowserDestroyed(ClipboardBrowserPlaceholder *placeholder) +{ + if (placeholder == getPlaceholder()) + updateContextMenu(0); + + if (placeholder == getPlaceholderForTrayMenu()) + updateTrayMenu(); } void MainWindow::onItemSelectionChanged(const ClipboardBrowser *browser) @@ -1019,7 +1038,9 @@ void MainWindow::onItemsChanged(const ClipboardBrowser *browser) { if (browser == this->browser()) updateContextMenu(contextMenuUpdateIntervalMsec); - if (browser == getTabForTrayMenu()) + + const ClipboardBrowserPlaceholder *placeholder = getPlaceholderForTrayMenu(); + if (placeholder && placeholder->browser() == browser) updateTrayMenu(); } @@ -1224,7 +1245,8 @@ bool MainWindow::closeMinimizes() const ClipboardBrowserPlaceholder *MainWindow::createTab( const QString &name, TabNameMatching nameMatch) { - Q_ASSERT( !name.isEmpty() ); + if ( name.isEmpty() ) + return nullptr; const int i = nameMatch == MatchExactTabName ? findTabIndexExactMatch(name) @@ -1236,6 +1258,8 @@ ClipboardBrowserPlaceholder *MainWindow::createTab( auto placeholder = new ClipboardBrowserPlaceholder(name, m_sharedData, this); connect( placeholder, &ClipboardBrowserPlaceholder::browserCreated, this, &MainWindow::onBrowserCreated ); + connect( placeholder, &ClipboardBrowserPlaceholder::browserDestroyed, + this, [this, placeholder]() { onBrowserDestroyed(placeholder); } ); ui->tabWidget->addTab(placeholder, name); saveTabPositions(); @@ -1322,12 +1346,13 @@ void MainWindow::addCommandsToTrayMenu(const QVariantMap &clipboardData) if ( m_menuCommands.isEmpty() ) return; - auto c = getTabForTrayMenu(); - if (!c) { - c = getPlaceholder()->createBrowser(); - if (!c) - return; - } + ClipboardBrowserPlaceholder *placeholder = getPlaceholderForTrayMenu(); + if (!placeholder) + return; + + ClipboardBrowser *c = placeholder->createBrowser(); + if (!c) + return; // Pass current window title to commands in tray menu. auto data = clipboardData; @@ -1525,11 +1550,15 @@ QAction *MainWindow::actionForMenuItem(int id, QWidget *parent, Qt::ShortcutCont return action; } -void MainWindow::addMenuItems(TrayMenu *menu, ClipboardBrowser *c, int maxItemCount, const QString &searchText) +void MainWindow::addMenuItems(TrayMenu *menu, ClipboardBrowserPlaceholder *placeholder, int maxItemCount, const QString &searchText) { WidgetSizeGuard sizeGuard(menu); menu->clearClipboardItems(); + if (!placeholder) + return; + + const ClipboardBrowser *c = placeholder->createBrowser(); if (!c) return; @@ -1547,11 +1576,15 @@ void MainWindow::addMenuItems(TrayMenu *menu, ClipboardBrowser *c, int maxItemCo } } -void MainWindow::activateMenuItem(ClipboardBrowser *c, const QVariantMap &data, bool omitPaste) +void MainWindow::activateMenuItem(ClipboardBrowserPlaceholder *placeholder, const QVariantMap &data, bool omitPaste) { if ( m_sharedData->moveItemOnReturnKey ) { const auto itemHash = ::hash(data); - c->moveToTop(itemHash); + if (placeholder) { + ClipboardBrowser *c = placeholder->createBrowser(); + if (c) + c->moveToTop(itemHash); + } } if ( QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier) ) @@ -2346,16 +2379,11 @@ bool MainWindow::toggleVisible() return true; } -void MainWindow::setCurrentTab(const ClipboardBrowser *browser) +void MainWindow::showBrowser(const ClipboardBrowser *browser) { int i = 0; for( ; i < ui->tabWidget->count() && getPlaceholder(i)->browser() != browser; ++i ) {} setCurrentTab(i); -} - -void MainWindow::showBrowser(const ClipboardBrowser *browser) -{ - setCurrentTab(browser); showWindow(); } @@ -2370,12 +2398,12 @@ bool MainWindow::setCurrentTab(int index) void MainWindow::onMenuActionTriggered(const QVariantMap &data, bool omitPaste) { - activateMenuItem( getTabForMenu(), data, omitPaste ); + activateMenuItem( getPlaceholderForMenu(), data, omitPaste ); } void MainWindow::onTrayActionTriggered(const QVariantMap &data, bool omitPaste) { - activateMenuItem( getTabForTrayMenu(), data, omitPaste ); + activateMenuItem( getPlaceholderForTrayMenu(), data, omitPaste ); } void MainWindow::trayActivated(QSystemTrayIcon::ActivationReason reason) @@ -2816,22 +2844,22 @@ QStringList MainWindow::tabs() const return ui->tabWidget->tabs(); } -ClipboardBrowser *MainWindow::getTabForMenu() +ClipboardBrowserPlaceholder *MainWindow::getPlaceholderForMenu() { const auto i = findTabIndex(m_menuTabName); - return i != -1 ? browser(i) : nullptr; + return i != -1 ? getPlaceholder(i) : nullptr; } -ClipboardBrowser *MainWindow::getTabForTrayMenu() +ClipboardBrowserPlaceholder *MainWindow::getPlaceholderForTrayMenu() { if (m_options.trayCurrentTab) - return browser(); + return getPlaceholder(); if ( m_options.trayTabName.isEmpty() ) - return m_options.clipboardTab.isEmpty() ? nullptr : tab(m_options.clipboardTab); + return m_options.clipboardTab.isEmpty() ? nullptr : getPlaceholder(m_options.clipboardTab); int i = findTabIndex(m_options.trayTabName); - return i != -1 ? browser(i) : nullptr; + return i != -1 ? getPlaceholder(i) : nullptr; } void MainWindow::onFilterChanged(const QRegExp &re) @@ -2987,12 +3015,12 @@ void MainWindow::updateTrayMenuTimeout() void MainWindow::filterMenuItems(const QString &searchText) { - addMenuItems(m_menu, getTabForMenu(), m_menuMaxItemCount, searchText); + addMenuItems(m_menu, getPlaceholderForMenu(), m_menuMaxItemCount, searchText); } void MainWindow::filterTrayMenuItems(const QString &searchText) { - addMenuItems(m_trayMenu, getTabForTrayMenu(), m_options.trayItems, searchText); + addMenuItems(m_trayMenu, getPlaceholderForTrayMenu(), m_options.trayItems, searchText); m_trayMenu->markItemInClipboard(m_clipboardData); } @@ -3132,11 +3160,16 @@ ClipboardBrowser *MainWindow::browserForItem(const QModelIndex &index) return nullptr; } -void MainWindow::addTab(const QString &name) +void MainWindow::addAndFocusTab(const QString &name) { - createTab(name, MatchExactTabName); - auto w = ui->tabWidget; - w->setCurrentIndex( w->count()-1 ); + auto placeholder = createTab(name, MatchExactTabName); + if (!placeholder) + return; + + int i = 0; + for( ; i < ui->tabWidget->count() && getPlaceholder(i) != placeholder; ++i ) {} + setCurrentTab(i); + saveTabPositions(); } @@ -3392,7 +3425,7 @@ void MainWindow::openNewTabDialog(const QString &name) d->setTabName(name); connect( d, &TabDialog::newTabNameAccepted, - this, &MainWindow::addTab ); + this, &MainWindow::addAndFocusTab ); d->open(); } @@ -3564,6 +3597,17 @@ bool MainWindow::unloadTab(const QString &tabName) return !placeholder || placeholder->expire(); } +void MainWindow::forceUnloadTab(const QString &tabName) +{ + ClipboardBrowserPlaceholder *placeholder = getPlaceholder(tabName); + if (!placeholder) + return; + + placeholder->unloadBrowser(); + + placeholder->createLoadButton(); +} + MainWindow::~MainWindow() { disconnect(); diff --git a/src/gui/mainwindow.h b/src/gui/mainwindow.h index f9708dd89..5cf323628 100644 --- a/src/gui/mainwindow.h +++ b/src/gui/mainwindow.h @@ -207,6 +207,7 @@ class MainWindow final : public QMainWindow void setTabIcon(const QString &tabName, const QString &icon); bool unloadTab(const QString &tabName); + void forceUnloadTab(const QString &tabName); /** * Save all items in tab to file. @@ -333,9 +334,6 @@ class MainWindow final : public QMainWindow /** Activate current item. */ void activateCurrentItem(); - /** Set current tab. */ - void setCurrentTab(const ClipboardBrowser *browser); - /** Show window and given tab and give focus to the tab. */ void showBrowser(const ClipboardBrowser *browser); @@ -382,10 +380,7 @@ class MainWindow final : public QMainWindow /** Rename current tab to given name (if possible). */ void renameTab(const QString &name, int tabIndex); - /** - * Add tab with given name if doesn't exist and focus the tab. - */ - void addTab(const QString &name); + void addAndFocusTab(const QString &name); /** Toggle monitoring (i.e. adding new clipboard content to the first tab). */ void toggleClipboardStoring(); @@ -436,8 +431,8 @@ class MainWindow final : public QMainWindow bool nativeEvent(const QByteArray &eventType, void *message, long *result) override; private: - ClipboardBrowser *getTabForMenu(); - ClipboardBrowser *getTabForTrayMenu(); + ClipboardBrowserPlaceholder *getPlaceholderForMenu(); + ClipboardBrowserPlaceholder *getPlaceholderForTrayMenu(); void filterMenuItems(const QString &searchText); void filterTrayMenuItems(const QString &searchText); void trayActivated(QSystemTrayIcon::ActivationReason reason); @@ -491,6 +486,7 @@ class MainWindow final : public QMainWindow void moveToBottom(); void onBrowserCreated(ClipboardBrowser *browser); + void onBrowserDestroyed(ClipboardBrowserPlaceholder *placeholder); void onItemSelectionChanged(const ClipboardBrowser *browser); void onItemsChanged(const ClipboardBrowser *browser); @@ -610,8 +606,8 @@ class MainWindow final : public QMainWindow QAction *actionForMenuItem(int id, QWidget *parent, Qt::ShortcutContext context); - void addMenuItems(TrayMenu *menu, ClipboardBrowser *c, int maxItemCount, const QString &searchText); - void activateMenuItem(ClipboardBrowser *c, const QVariantMap &data, bool omitPaste); + void addMenuItems(TrayMenu *menu, ClipboardBrowserPlaceholder *placeholder, int maxItemCount, const QString &searchText); + void activateMenuItem(ClipboardBrowserPlaceholder *placeholder, const QVariantMap &data, bool omitPaste); bool toggleMenu(TrayMenu *menu, QPoint pos); bool toggleMenu(TrayMenu *menu); diff --git a/src/scriptable/scriptable.cpp b/src/scriptable/scriptable.cpp index 2caf96b50..9aa69cfb2 100644 --- a/src/scriptable/scriptable.cpp +++ b/src/scriptable/scriptable.cpp @@ -1073,6 +1073,12 @@ QScriptValue Scriptable::unload() return toScriptValue(unloaded, this); } +void Scriptable::forceUnload() +{ + const auto tabs = arguments(); + m_proxy->forceUnloadTabs(tabs.isEmpty() ? m_proxy->tabs() : tabs); +} + QScriptValue Scriptable::length() { m_skipArguments = 0; diff --git a/src/scriptable/scriptable.h b/src/scriptable/scriptable.h index f7ee0cff0..5972be4e1 100644 --- a/src/scriptable/scriptable.h +++ b/src/scriptable/scriptable.h @@ -196,6 +196,7 @@ public slots: QScriptValue tabIcon(); QScriptValue tabicon() { return tabIcon(); } QScriptValue unload(); + void forceUnload(); QScriptValue length(); QScriptValue size() { return length(); } diff --git a/src/scriptable/scriptableproxy.cpp b/src/scriptable/scriptableproxy.cpp index 687f8fbcc..8d69238ef 100644 --- a/src/scriptable/scriptableproxy.cpp +++ b/src/scriptable/scriptableproxy.cpp @@ -1132,6 +1132,13 @@ QStringList ScriptableProxy::unloadTabs(const QStringList &tabs) return unloaded; } +void ScriptableProxy::forceUnloadTabs(const QStringList &tabs) +{ + INVOKE2(forceUnloadTabs, (tabs)); + for (const auto &tab : tabs) + m_wnd->forceUnloadTab(tab); +} + bool ScriptableProxy::showBrowser(const QString &tabName) { INVOKE(showBrowser, (tabName)); @@ -1432,9 +1439,7 @@ QVariantMap ScriptableProxy::browserItemData(const QString &tabName, int arg1) void ScriptableProxy::setCurrentTab(const QString &tabName) { INVOKE2(setCurrentTab, (tabName)); - ClipboardBrowser *c = fetchBrowser(tabName); - if (c) - m_wnd->setCurrentTab(c); + m_wnd->addAndFocusTab(tabName); } QString ScriptableProxy::tab(const QString &tabName) diff --git a/src/scriptable/scriptableproxy.h b/src/scriptable/scriptableproxy.h index a2587a96d..5201a8955 100644 --- a/src/scriptable/scriptableproxy.h +++ b/src/scriptable/scriptableproxy.h @@ -117,6 +117,7 @@ public slots: void setTabIcon(const QString &tabName, const QString &icon); QStringList unloadTabs(const QStringList &tabs); + void forceUnloadTabs(const QStringList &tabs); bool showBrowser(const QString &tabName); bool showBrowserAt(const QString &tabName, QRect rect); diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp index 7779d7a75..aea360fd6 100644 --- a/src/tests/tests.cpp +++ b/src/tests/tests.cpp @@ -71,6 +71,7 @@ const auto defaultSessionColor = "#ff8800"; const auto defaultTagColor = "#000000"; const auto clipboardBrowserId = "focus:ClipboardBrowser"; +const auto clipboardBrowserRefreshButtonId = "focus:ClipboardBrowserRefreshButton"; const auto filterEditId = "focus:Utils::FilterLineEdit"; const auto trayMenuId = "focus:TrayMenu"; const auto menuId = "focus:Menu"; @@ -1490,6 +1491,27 @@ void Tests::commandUnload() RUN("unload" << "missing-tab", "missing-tab\n"); } +void Tests::commandForceUnload() +{ + RUN("forceUnload", ""); + RUN_EXPECT_ERROR_WITH_STDERR("add" << "A", CommandException, "ScriptError: Invalid tab"); + + RUN("keys" << clipboardBrowserRefreshButtonId << "Space", ""); + RUN("add" << "A", ""); + + const auto tab = testTab(1); + RUN("tab" << tab << "add" << "A", ""); + + RUN("forceUnload" << tab, ""); + + RUN("setCurrentTab" << tab, ""); + RUN_EXPECT_ERROR_WITH_STDERR( + "tab" << tab << "add" << "B", CommandException, "ScriptError: Invalid tab"); + + RUN("keys" << clipboardBrowserRefreshButtonId << "Space", ""); + RUN("add" << "B", ""); +} + void Tests::classByteArray() { RUN("ByteArray('test')", "test"); diff --git a/src/tests/tests.h b/src/tests/tests.h index 7f113cb7b..7620fca4e 100644 --- a/src/tests/tests.h +++ b/src/tests/tests.h @@ -135,6 +135,7 @@ private slots: void commandMimeTypes(); void commandUnload(); + void commandForceUnload(); void classByteArray(); void classFile();