From 0c49fc05cf3f7e38bcc8b69ea47984efcfe7d38b Mon Sep 17 00:00:00 2001
From: Lukas Holecek <hluk@email.cz>
Date: Sun, 11 Apr 2021 06:58:25 +0200
Subject: [PATCH] Index rows from one by default

Fixes #1085
---
 CHANGES.md                         |  4 ++++
 docs/scripting-api.rst             |  3 +++
 src/app/clipboardserver.cpp        |  1 +
 src/common/appconfig.h             |  5 +++++
 src/gui/clipboardbrowser.cpp       |  5 ++++-
 src/gui/clipboardbrowsershared.h   |  1 +
 src/gui/configurationmanager.cpp   |  2 ++
 src/gui/mainwindow.cpp             |  3 +++
 src/gui/traymenu.cpp               |  5 +++--
 src/gui/traymenu.h                 |  5 +++++
 src/item/itemdelegate.cpp          |  2 +-
 src/scriptable/scriptableproxy.cpp |  1 +
 src/tests/tests.cpp                | 24 ++++++++++++++++++++++++
 13 files changed, 57 insertions(+), 4 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 3340b4a07..9abb343c1 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -12,6 +12,10 @@
 
 - System notification popups are now used instead of own implementation.
 
+- Item rows in main window and tray menu are now indexed from one instead of
+  zero by default (#1085). This can be reverted to the old behavior using
+  command `copyq row_index_from_one false`.
+
 - A tag can be marked as "locked" in configuration. Items with such tags cannot
   be removed until the tag is removed or "unlocked".
 
diff --git a/docs/scripting-api.rst b/docs/scripting-api.rst
index 07185a425..42ba1ca26 100644
--- a/docs/scripting-api.rst
+++ b/docs/scripting-api.rst
@@ -73,6 +73,9 @@ Functions
 Argument list parts ``...`` and ``[...]`` are optional and can be
 omitted.
 
+Item **row** values in scripts always **start from 0** (like array index),
+unlike in GUI, where row numbers start from 1 by default.
+
 .. js:function:: String version()
 
    Returns version string.
diff --git a/src/app/clipboardserver.cpp b/src/app/clipboardserver.cpp
index e6fde8891..c94bade1d 100644
--- a/src/app/clipboardserver.cpp
+++ b/src/app/clipboardserver.cpp
@@ -688,6 +688,7 @@ void ClipboardServer::loadSettings()
     m_sharedData->saveDelayMsOnItemRemoved = appConfig.option<Config::save_delay_ms_on_item_removed>();
     m_sharedData->saveDelayMsOnItemMoved = appConfig.option<Config::save_delay_ms_on_item_moved>();
     m_sharedData->saveDelayMsOnItemEdited = appConfig.option<Config::save_delay_ms_on_item_edited>();
+    m_sharedData->rowIndexFromOne = appConfig.option<Config::row_index_from_one>();
 
     m_wnd->loadSettings(settings, appConfig);
 
diff --git a/src/common/appconfig.h b/src/common/appconfig.h
index 94c0ec4b0..61f49c8c8 100644
--- a/src/common/appconfig.h
+++ b/src/common/appconfig.h
@@ -403,6 +403,11 @@ struct window_wait_for_modifiers_released_ms : Config<int> {
     static Value defaultValue() { return 50; }
 };
 
+struct row_index_from_one : Config<bool> {
+    static QString name() { return "row_index_from_one"; }
+    static Value defaultValue() { return true; }
+};
+
 struct style : Config<QString> {
     static QString name() { return "style"; }
 };
diff --git a/src/gui/clipboardbrowser.cpp b/src/gui/clipboardbrowser.cpp
index b16f2f52f..edcdd27a1 100644
--- a/src/gui/clipboardbrowser.cpp
+++ b/src/gui/clipboardbrowser.cpp
@@ -1332,8 +1332,11 @@ void ClipboardBrowser::filterItems(const ItemFilterPtr &filter)
 
     // If search string is a number, highlight item in that row.
     bool filterByRowNumber = !m_sharedData->numberSearch;
-    if (filterByRowNumber)
+    if (filterByRowNumber) {
         m_filterRow = newSearch.toInt(&filterByRowNumber);
+        if (m_filterRow > 0 && m_sharedData->rowIndexFromOne)
+            --m_filterRow;
+    }
     if (!filterByRowNumber)
         m_filterRow = -1;
 
diff --git a/src/gui/clipboardbrowsershared.h b/src/gui/clipboardbrowsershared.h
index e1ee67f41..89e33cd94 100644
--- a/src/gui/clipboardbrowsershared.h
+++ b/src/gui/clipboardbrowsershared.h
@@ -44,6 +44,7 @@ struct ClipboardBrowserShared {
     int saveDelayMsOnItemRemoved = 0;
     int saveDelayMsOnItemMoved = 0;
     int saveDelayMsOnItemEdited = 0;
+    bool rowIndexFromOne = true;
     ItemFactory *itemFactory = nullptr;
     ActionHandler *actions = nullptr;
     NotificationDaemon *notifications = nullptr;
diff --git a/src/gui/configurationmanager.cpp b/src/gui/configurationmanager.cpp
index ca9a90244..9fb574e0e 100644
--- a/src/gui/configurationmanager.cpp
+++ b/src/gui/configurationmanager.cpp
@@ -346,6 +346,8 @@ void ConfigurationManager::initOptions()
     bind<Config::window_wait_for_modifiers_released_ms>();
 
     bind<Config::style>();
+
+    bind<Config::row_index_from_one>();
 }
 
 template <typename Config, typename Widget>
diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp
index d0789bc46..486a7c2a2 100644
--- a/src/gui/mainwindow.cpp
+++ b/src/gui/mainwindow.cpp
@@ -2509,6 +2509,9 @@ void MainWindow::loadSettings(QSettings &settings, AppConfig &appConfig)
     m_trayMenu->setNumberSearchEnabled(m_sharedData->numberSearch);
     m_menu->setNumberSearchEnabled(m_sharedData->numberSearch);
 
+    m_trayMenu->setRowIndexFromOne(m_sharedData->rowIndexFromOne);
+    m_menu->setRowIndexFromOne(m_sharedData->rowIndexFromOne);
+
     m_options.transparency = appConfig.option<Config::transparency>();
     m_options.transparencyFocused = appConfig.option<Config::transparency_focused>();
     updateWindowTransparency();
diff --git a/src/gui/traymenu.cpp b/src/gui/traymenu.cpp
index a248e7eae..0b52b6da2 100644
--- a/src/gui/traymenu.cpp
+++ b/src/gui/traymenu.cpp
@@ -101,10 +101,11 @@ void TrayMenu::addClipboardItemAction(const QVariantMap &data, bool showImages)
     QString format;
 
     // Add number key hint.
-    if (m_clipboardItemActionCount < 10) {
+    const int rowNumber = m_clipboardItemActionCount + static_cast<int>(m_rowIndexFromOne);
+    if (rowNumber < 10) {
         format = tr("&%1. %2",
                     "Key hint (number shortcut) for items in tray menu (%1 is number, %2 is item label)")
-                .arg(m_clipboardItemActionCount);
+                .arg(rowNumber);
     }
 
     m_clipboardItemActionCount++;
diff --git a/src/gui/traymenu.h b/src/gui/traymenu.h
index 6f58701b5..2560eb436 100644
--- a/src/gui/traymenu.h
+++ b/src/gui/traymenu.h
@@ -61,6 +61,9 @@ public:
 
     void markItemInClipboard(const QVariantMap &clipboardData);
 
+    /** Row numbers start from one instead of zero? */
+    void setRowIndexFromOne(bool rowIndexFromOne) { m_rowIndexFromOne = rowIndexFromOne; }
+
 signals:
     /** Emitted if numbered action triggered. */
     void clipboardItemActionTriggered(const QVariantMap &itemData, bool omitPaste);
@@ -100,6 +103,8 @@ private:
     QString m_searchText;
 
     QTimer m_timerUpdateActiveAction;
+
+    bool m_rowIndexFromOne = true;
 };
 
 #endif // TRAYMENU_H
diff --git a/src/item/itemdelegate.cpp b/src/item/itemdelegate.cpp
index cda64cd2a..0f52b8e49 100644
--- a/src/item/itemdelegate.cpp
+++ b/src/item/itemdelegate.cpp
@@ -482,7 +482,7 @@ void ItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
 
     // Render number.
     if ( m_sharedData->theme.showRowNumber() ) {
-        const QString num = QString::number(row);
+        const QString num = QString::number(row + static_cast<int>(m_sharedData->rowIndexFromOne));
         QPalette::ColorRole role = isSelected ? QPalette::HighlightedText : QPalette::Text;
         painter->save();
         painter->setFont( m_sharedData->theme.rowNumberFont() );
diff --git a/src/scriptable/scriptableproxy.cpp b/src/scriptable/scriptableproxy.cpp
index 48ebaf1d2..7f77b00f4 100644
--- a/src/scriptable/scriptableproxy.cpp
+++ b/src/scriptable/scriptableproxy.cpp
@@ -1398,6 +1398,7 @@ int ScriptableProxy::menuItems(const QVector<QVariantMap> &items)
 
     TrayMenu menu;
     menu.setObjectName("CustomMenu");
+    menu.setRowIndexFromOne( AppConfig().option<Config::row_index_from_one>() );
 
     const auto addMenuItems = [&](const QString &searchText) {
         menu.clearClipboardItems();
diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp
index 6d373f480..13b253284 100644
--- a/src/tests/tests.cpp
+++ b/src/tests/tests.cpp
@@ -2288,6 +2288,13 @@ void Tests::searchItemsAndSelect()
 void Tests::searchRowNumber()
 {
     RUN("add" << "d2" << "c" << "b2" << "a", "");
+
+    RUN("keys" << ":2" << "TAB", "");
+    RUN("testSelected", QString(clipboardTabName) + " 1 1\n");
+    RUN("keys" << "CTRL+A", "");
+    RUN("testSelected", QString(clipboardTabName) + " 1 1 3\n");
+
+    RUN("config" << "row_index_from_one" << "false", "false\n");
     RUN("keys" << ":2" << "TAB", "");
     RUN("testSelected", QString(clipboardTabName) + " 2 2\n");
     RUN("keys" << "CTRL+A", "");
@@ -2869,6 +2876,23 @@ void Tests::menu()
     RUN("menu" << tab << "2", "");
     RUN("keys" << menuId << "END", "");
     ACTIVATE_MENU_ITEM(menuId, clipboardBrowserId, "B");
+
+#ifdef Q_OS_MAC
+    SKIP("Number keys don't seem to work in the tray menu on macOS.");
+#endif
+
+    // Select item by row number.
+    RUN("tab" << tab << "add(3,2,1,0)", "");
+    RUN("menu" << tab, "");
+    RUN("keys" << menuId << "3" << clipboardBrowserId, "");
+    WAIT_FOR_CLIPBOARD("2");
+
+    // Select item by index.
+    RUN("config" << "row_index_from_one" << "false", "false\n");
+    RUN("tab" << tab << "add(3,2,1,0)", "");
+    RUN("menu" << tab, "");
+    RUN("keys" << menuId << "3" << clipboardBrowserId, "");
+    WAIT_FOR_CLIPBOARD("3");
 }
 
 void Tests::traySearch()
-- 
GitLab