[kid3] /: Add keyboard shortcuts to the sections for editing without the mouse

Urs Fleisch null at kde.org
Mon Mar 30 14:56:33 BST 2020


Git commit 3d7a068e5f6626ae069c890687da188dedc16c0f by Urs Fleisch.
Committed on 30/03/2020 at 13:55.
Pushed by ufleisch into branch 'master'.

Add keyboard shortcuts to the sections for editing without the mouse

The standard keyboard shortcuts for Back and Forward can be used to navigate
to other sections, Delete to delete the current frame, Copy and Paste to
copy and paste the section data. Shift-Delete removes the section,
Shift-Ctrl-V pastes data from another seciton, and F2 can be used to edit
the current frame.

M  +39   -0    doc/en/index.docbook
M  +2    -0    src/gui/CMakeLists.txt
M  +125  -1    src/gui/forms/kid3form.cpp
M  +12   -0    src/gui/forms/kid3form.h
A  +95   -0    src/gui/forms/sectionactions.cpp     [License: GPL (v2+)]
A  +117  -0    src/gui/forms/sectionactions.h     [License: GPL (v2+)]

https://invent.kde.org/kde/kid3/commit/3d7a068e5f6626ae069c890687da188dedc16c0f

diff --git a/doc/en/index.docbook b/doc/en/index.docbook
index 1e94f193..cf3b0727 100644
--- a/doc/en/index.docbook
+++ b/doc/en/index.docbook
@@ -330,6 +330,45 @@ and folder listboxes, the right side contains the <guilabel>File</guilabel>,
 </guilabel> sections.
 </para>
 
+<para>
+To navigate between the different sections using the keyboard, several keyboard
+shortcuts are supported. In the tag sections, the shortcuts are active while
+not editing text or when being in the first column.
+
+<itemizedlist>
+<listitem><para>
+<keycombo>&Alt;<keycap>Left</keycap></keycombo>: Go to previous section
+(<keycombo><keycap>Command</keycap><keycap>[</keycap></keycombo> on &macOS;)
+</para></listitem>
+<listitem><para>
+<keycombo>&Alt;<keycap>Right</keycap></keycombo>: Go to next section
+(<keycombo><keycap>Command</keycap><keycap>]</keycap></keycombo> on &macOS;)
+</para></listitem>
+<listitem><para>
+<keycombo>&Ctrl;<keycap>Shift</keycap><keycap>V</keycap></keycombo>: From
+other tag
+</para></listitem>
+<listitem><para>
+<keycombo>&Ctrl;<keycap>C</keycap></keycombo>: Copy
+</para></listitem>
+<listitem><para>
+<keycombo>&Ctrl;<keycap>V</keycap></keycombo>: Paste
+</para></listitem>
+<listitem><para>
+<keycombo><keycap>Shift</keycap><keycap>Delete</keycap></keycombo>: Remove
+</para></listitem>
+<listitem><para>
+<keycombo><keycap>F2</keycap></keycombo>: Edit
+</para></listitem>
+<listitem><para>
+<keycombo><keycap>Insert</keycap></keycombo>: Add
+</para></listitem>
+<listitem><para>
+<keycombo><keycap>Delete</keycap></keycombo>: Delete
+</para></listitem>
+</itemizedlist>
+</para>
+
 <sect2 id="file-list">
 <title>File List</title>
 
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt
index 091eb65f..5974a28d 100644
--- a/src/gui/CMakeLists.txt
+++ b/src/gui/CMakeLists.txt
@@ -42,6 +42,7 @@ qt5_wrap_cpp(gui_GEN_MOC_SRCS
   forms/playlistview.h
   forms/audioplayer.h
   forms/mprisinterface.h
+  forms/sectionactions.h
   TARGET kid3-gui
 )
 
@@ -100,6 +101,7 @@ add_library(kid3-gui
   forms/audioplayer.cpp
   forms/mprisinterface.cpp
   forms/guiplatformtools.cpp
+  forms/sectionactions.cpp
   ${gui_GEN_MOC_SRCS}
 )
 
diff --git a/src/gui/forms/kid3form.cpp b/src/gui/forms/kid3form.cpp
index 6f6a8aee..0d20bcff 100644
--- a/src/gui/forms/kid3form.cpp
+++ b/src/gui/forms/kid3form.cpp
@@ -71,6 +71,7 @@
 #include "coretaggedfileiconprovider.h"
 #include "icoreplatformtools.h"
 #include "kid3application.h"
+#include "sectionactions.h"
 #ifdef Q_OS_MAC
 #include <CoreFoundation/CFURL.h>
 #endif
@@ -582,6 +583,76 @@ Kid3Form::Kid3Form(Kid3Application* app, BaseMainWindowImpl* mainWin,
     setTabOrder(tabWidget, m_frameTable[tagNr]);
     tabWidget = m_frameTable[tagNr];
   }
+
+  auto sectionActions = new SectionActions(SectionActions::Navigation,
+                                           m_fileListBox);
+  connect(sectionActions->previousSectionAction(), &QAction::triggered,
+          this, [this]() { setFocusPreviousTag(Frame::Tag_NumValues); });
+  connect(sectionActions->nextSectionAction(), &QAction::triggered,
+          this, &Kid3Form::setFocusDirList);
+
+  sectionActions = new SectionActions(SectionActions::Navigation, m_dirListBox);
+  connect(sectionActions->previousSectionAction(), &QAction::triggered,
+          this, &Kid3Form::setFocusFileList);
+  connect(sectionActions->nextSectionAction(), &QAction::triggered,
+          this, &Kid3Form::setFocusFilename);
+
+  sectionActions = new SectionActions(SectionActions::Navigation |
+                                      SectionActions::Transfer,
+                                      m_fileWidget);
+  connect(sectionActions->previousSectionAction(), &QAction::triggered,
+          this, &Kid3Form::setFocusDirList);
+  connect(sectionActions->nextSectionAction(), &QAction::triggered,
+          this, [this]() { setFocusNextTag(Frame::Tag_NumValues); });
+  connect(sectionActions->transferAction(), &QAction::triggered,
+    m_app->tag(Frame::Tag_2), &Kid3ApplicationTagContext::getFilenameFromTags);
+
+  FOR_ALL_TAGS(tagNr) {
+    sectionActions = new SectionActions(
+          tagNr == Frame::Tag_1 ? SectionActions::Navigation |
+                                  SectionActions::Transfer |
+                                  SectionActions::EditSection
+                                : SectionActions::Navigation |
+                                  SectionActions::Transfer |
+                                  SectionActions::EditSection |
+                                  SectionActions::EditElement,
+          m_frameTable[tagNr]);
+    connect(sectionActions->previousSectionAction(), &QAction::triggered,
+            this, [this, tagNr]() {
+      setFocusPreviousTag(tagNr);
+    });
+    connect(sectionActions->nextSectionAction(), &QAction::triggered,
+            this, [this, tagNr]() {
+      setFocusNextTag(tagNr);
+    });
+
+    connect(sectionActions->copyAction(), &QAction::triggered,
+            m_app->tag(tagNr), &Kid3ApplicationTagContext::copyTags);
+    connect(sectionActions->pasteAction(), &QAction::triggered,
+            m_app->tag(tagNr), &Kid3ApplicationTagContext::pasteTags);
+    connect(sectionActions->removeAction(), &QAction::triggered,
+            this, [this, tagNr]() {
+      m_app->tag(tagNr)->removeTags();
+      setFocusTag(tagNr);
+    });
+
+    QByteArray ba;
+    ba.append(static_cast<char>(tagNr == Frame::Tag_2 ? Frame::Tag_1
+                                                      : Frame::Tag_2));
+    ba.append(static_cast<char>(tagNr));
+    sectionActions->transferAction()->setData(ba);
+    connect(sectionActions->transferAction(), &QAction::triggered,
+            this, &Kid3Form::copyTagsActionData);
+
+    if (tagNr != Frame::Tag_1) {
+      connect(sectionActions->editAction(), &QAction::triggered,
+              m_app->tag(tagNr), &Kid3ApplicationTagContext::editFrame);
+      connect(sectionActions->addAction(), &QAction::triggered,
+              m_app->tag(tagNr), &Kid3ApplicationTagContext::addFrame);
+      connect(sectionActions->deleteAction(), &QAction::triggered,
+              m_app->tag(tagNr), &Kid3ApplicationTagContext::deleteFrame);
+    }
+  }
 }
 
 /**
@@ -901,7 +972,14 @@ void Kid3Form::hidePicture(bool hide)
  */
 void Kid3Form::setFocusFilename()
 {
-  m_nameLineEdit->setFocus();
+  if (m_fileWidget->isHidden()) {
+    hideFile(false);
+  }
+  if (isFilenameEditEnabled()) {
+    m_nameLineEdit->setFocus();
+  } else {
+    m_formatComboBox->setFocus();
+  }
 }
 
 /**
@@ -910,9 +988,55 @@ void Kid3Form::setFocusFilename()
  */
 void Kid3Form::setFocusTag(Frame::TagNumber tagNr)
 {
+  if (m_tagWidget[tagNr]->isHidden()) {
+    hideTag(tagNr, false);
+  }
   m_frameTable[tagNr]->setFocus();
 }
 
+/**
+ * Set focus on next tag controls.
+ * @param tagNr current tag, Frame::Tag_NumValues if not on tag
+ */
+void Kid3Form::setFocusNextTag(Frame::TagNumber tagNr)
+{
+  for (int i = tagNr == Frame::Tag_NumValues ? Frame::Tag_1
+                                             : tagNr + 1; ; ++i) {
+    if (i >= Frame::Tag_NumValues) {
+      setFocusFileList();
+      break;
+    } else if (i >= Frame::Tag_1) {
+      if (m_tagWidget[i]->isEnabled()) {
+        setFocusTag(static_cast<Frame::TagNumber>(i));
+        break;
+      }
+    } else {
+      break;
+    }
+  }
+}
+
+/**
+ * Set focus on previous tag controls.
+ * @param tagNr current tag, Frame::Tag_NumValues if not on tag
+ */
+void Kid3Form::setFocusPreviousTag(Frame::TagNumber tagNr)
+{
+  for (int i = tagNr - 1; ; --i) {
+    if (i < Frame::Tag_1) {
+      setFocusFilename();
+      break;
+    } else if (i < Frame::Tag_NumValues) {
+      if (m_tagWidget[i]->isEnabled()) {
+        setFocusTag(static_cast<Frame::TagNumber>(i));
+        break;
+      }
+    } else {
+      break;
+    }
+  }
+}
+
 /**
  * Set focus on file list.
  */
diff --git a/src/gui/forms/kid3form.h b/src/gui/forms/kid3form.h
index 8a1ea3c9..672cbda0 100644
--- a/src/gui/forms/kid3form.h
+++ b/src/gui/forms/kid3form.h
@@ -248,6 +248,18 @@ public slots:
    */
   void setFocusTag(Frame::TagNumber tagNr);
 
+  /**
+   * Set focus on next tag controls.
+   * @param tagNr current tag, Frame::Tag_NumValues if not on tag
+   */
+  void setFocusNextTag(Frame::TagNumber tagNr);
+
+  /**
+   * Set focus on previous tag controls.
+   * @param tagNr current tag, Frame::Tag_NumValues if not on tag
+   */
+  void setFocusPreviousTag(Frame::TagNumber tagNr);
+
   /**
    * Set focus on file list.
    */
diff --git a/src/gui/forms/sectionactions.cpp b/src/gui/forms/sectionactions.cpp
new file mode 100644
index 00000000..ad9fb2d0
--- /dev/null
+++ b/src/gui/forms/sectionactions.cpp
@@ -0,0 +1,95 @@
+/**
+ * \file sectionactions.cpp
+ * Actions for section shortcuts.
+ *
+ * \b Project: Kid3
+ * \author Urs Fleisch
+ * \date 22 Mar 2020
+ *
+ * Copyright (C) 2020  Urs Fleisch
+ *
+ * This file is part of Kid3.
+ *
+ * Kid3 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.
+ *
+ * Kid3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "sectionactions.h"
+#include <QAction>
+#include <QAbstractItemView>
+
+/**
+ * Constructor.
+ * @param groups action groups to add
+ * @param widget widget to which actions are added
+ */
+SectionActions::SectionActions(ActionGroups groups, QWidget* widget)
+  : QObject(widget),
+    m_widget(widget),
+    m_previousSectionAction(nullptr), m_nextSectionAction(nullptr),
+    m_copyAction(nullptr), m_pasteAction(nullptr),
+    m_removeAction(nullptr), m_transferAction(nullptr),
+    m_editAction(nullptr), m_addAction(nullptr), m_deleteAction(nullptr)
+{
+  auto shortcutContext = qobject_cast<QAbstractItemView*>(m_widget)
+      ? Qt::WidgetShortcut : Qt::WidgetWithChildrenShortcut;
+  if (groups & Navigation) {
+    m_previousSectionAction = new QAction(m_widget);
+    m_previousSectionAction->setShortcut(QKeySequence::Back);
+    m_previousSectionAction->setShortcutContext(shortcutContext);
+    m_widget->addAction(m_previousSectionAction);
+
+    m_nextSectionAction = new QAction(m_widget);
+    m_nextSectionAction->setShortcut(QKeySequence::Forward);
+    m_nextSectionAction->setShortcutContext(shortcutContext);
+    m_widget->addAction(m_nextSectionAction);
+  }
+  if (groups & Transfer) {
+    m_transferAction = new QAction(m_widget);
+    m_transferAction->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_V);
+    m_transferAction->setShortcutContext(shortcutContext);
+    m_widget->addAction(m_transferAction);
+  }
+  if (groups & EditSection) {
+    m_copyAction = new QAction(m_widget);
+    m_copyAction->setShortcut(QKeySequence::Copy);
+    m_copyAction->setShortcutContext(shortcutContext);
+    m_widget->addAction(m_copyAction);
+
+    m_pasteAction = new QAction(m_widget);
+    m_pasteAction->setShortcut(QKeySequence::Paste);
+    m_pasteAction->setShortcutContext(shortcutContext);
+    m_widget->addAction(m_pasteAction);
+
+    m_removeAction = new QAction(m_widget);
+    m_removeAction->setShortcut(Qt::SHIFT + Qt::Key_Delete);
+    m_removeAction->setShortcutContext(shortcutContext);
+    m_widget->addAction(m_removeAction);
+  }
+  if (groups & EditElement) {
+    m_editAction = new QAction(m_widget);
+    m_editAction->setShortcut(Qt::Key_F2);
+    m_editAction->setShortcutContext(shortcutContext);
+    m_widget->addAction(m_editAction);
+
+    m_addAction = new QAction(m_widget);
+    m_addAction->setShortcut(Qt::Key_Insert);
+    m_addAction->setShortcutContext(shortcutContext);
+    m_widget->addAction(m_addAction);
+
+    m_deleteAction = new QAction(m_widget);
+    m_deleteAction->setShortcut(QKeySequence::Delete);
+    m_deleteAction->setShortcutContext(shortcutContext);
+    m_widget->addAction(m_deleteAction);
+  }
+}
diff --git a/src/gui/forms/sectionactions.h b/src/gui/forms/sectionactions.h
new file mode 100644
index 00000000..33c9a5ad
--- /dev/null
+++ b/src/gui/forms/sectionactions.h
@@ -0,0 +1,117 @@
+/**
+ * \file sectionactions.h
+ * Actions for section shortcuts.
+ *
+ * \b Project: Kid3
+ * \author Urs Fleisch
+ * \date 22 Mar 2020
+ *
+ * Copyright (C) 2020  Urs Fleisch
+ *
+ * This file is part of Kid3.
+ *
+ * Kid3 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.
+ *
+ * Kid3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <QObject>
+
+class QWidget;
+class QAction;
+
+/**
+ * Actions for section shortcuts.
+ * Can be used to add actions to a section to navigate to other sections
+ * and edit. The keyboard shortcuts are only active when the section has the
+ * focus.
+ */
+class SectionActions : public QObject {
+  Q_OBJECT
+public:
+  /** Which actions to include */
+  enum ActionGroup {
+    Navigation  = 1 << 0, /**< previous section, next section */
+    Transfer    = 1 << 1, /**< transfer (from other tag) */
+    EditSection = 1 << 2, /**< copy, paste, remove */
+    EditElement = 1 << 3  /**< edit, add, delete */
+  };
+  Q_DECLARE_FLAGS(ActionGroups, ActionGroup)
+
+  /**
+   * Constructor.
+   * @param groups action groups to add
+   * @param widget widget to which actions are added
+   */
+  SectionActions(ActionGroups groups, QWidget* widget);
+
+  /** Get action for previous section. */
+  QAction* previousSectionAction() const {
+    return m_previousSectionAction;
+  }
+
+  /** Get action for next section. */
+  QAction* nextSectionAction() const {
+    return m_nextSectionAction;
+  }
+
+  /** Get action for copy. */
+  QAction* copyAction() const {
+    return m_copyAction;
+  }
+
+  /** Get action for paste. */
+  QAction* pasteAction() const {
+    return m_pasteAction;
+  }
+
+  /** Get action for remove. */
+  QAction* removeAction() const {
+    return m_removeAction;
+  }
+
+  /** Get action for transfer to other tag. */
+  QAction* transferAction() const {
+    return m_transferAction;
+  }
+
+  /** Get action for edit. */
+  QAction* editAction() const {
+    return m_editAction;
+  }
+
+  /** Get action for add. */
+  QAction* addAction() const {
+    return m_addAction;
+  }
+
+  /** Get action for delete. */
+  QAction* deleteAction() const {
+    return m_deleteAction;
+  }
+
+private:
+  QWidget* m_widget;
+  QAction* m_previousSectionAction;
+  QAction* m_nextSectionAction;
+  QAction* m_copyAction;
+  QAction* m_pasteAction;
+  QAction* m_removeAction;
+  QAction* m_transferAction;
+  QAction* m_editAction;
+  QAction* m_addAction;
+  QAction* m_deleteAction;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(SectionActions::ActionGroups)


More information about the kde-doc-english mailing list