Skip to content

Commit

Permalink
keyboard shortcuts widget now checks for duplicate shortcuts everytim…
Browse files Browse the repository at this point in the history
…e shortcut is changed and offers solutions, fixes #1605
  • Loading branch information
martinrotter committed Jan 31, 2025
1 parent 08388cb commit 68060c7
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 15 deletions.
46 changes: 46 additions & 0 deletions resources/scripts/scrapers/youtube-limit-length.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Use this as RSS Guard post-processing script.
#
# Example command line usage:
# curl 'PATH_TO_SOME_YOUTUBE_CHANNEL_RSS_FEED' | python 'youtube-limit-length.py'

import sys
import xml.etree.ElementTree as ET
import requests
import json
import isodate

sys.stdin.reconfigure(encoding="utf-8")
input_data = sys.stdin.read()

api_key = "YOUR_API_KEY_HERE"
min_required_length = 90 # In seconds.

youtube_query = (
"https://www.googleapis.com/youtube/v3/videos?part=contentDetails&id=VIDEO-ID&key="
+ api_key
)
ns = {
"atom": "http://www.w3.org/2005/Atom",
"yt": "http://www.youtube.com/xml/schemas/2015",
}

tree = ET.fromstring(input_data)
entries = tree.findall("atom:entry", ns)

for entry in entries:
video_id = entry.find("yt:videoId", ns).text
youtube_query_specific = youtube_query.replace("VIDEO-ID", video_id)
api_response = requests.get(youtube_query_specific).text
response_json = json.loads(api_response)

try:
length_encoded = str(response_json["items"][0]["contentDetails"]["duration"])
except:
continue

length = isodate.parse_duration(length_encoded)

if length.seconds < min_required_length:
tree.remove(entry)

sys.stdout.buffer.write(ET.tostring(tree, encoding='utf-8', method='xml'))
72 changes: 60 additions & 12 deletions src/librssguard/dynamic-shortcuts/dynamicshortcutswidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "definitions/definitions.h"
#include "dynamic-shortcuts/shortcutcatcher.h"
#include "gui/messagebox.h"

#include <QAction>
#include <QGridLayout>
Expand All @@ -25,24 +26,24 @@ bool DynamicShortcutsWidget::areShortcutsUnique() const {
QList<QKeySequence> all_shortcuts;

// Obtain all shortcuts.
for (const ActionBinding& binding : m_actionBindings) {
const QKeySequence new_shortcut = binding.second->shortcut();
for (ShortcutCatcher* catcher : m_actionBindings) {
const QKeySequence new_shortcut = catcher->shortcut();

if (!new_shortcut.isEmpty() && all_shortcuts.contains(new_shortcut)) {
// Problem, two identical non-empty shortcuts found.
return false;
}
else {
all_shortcuts.append(binding.second->shortcut());
all_shortcuts.append(catcher->shortcut());
}
}

return true;
}

void DynamicShortcutsWidget::updateShortcuts() {
for (const ActionBinding& binding : std::as_const(m_actionBindings)) {
binding.first->setShortcut(binding.second->shortcut());
for (ShortcutCatcher* catcher : std::as_const(m_actionBindings)) {
catcher->action()->setShortcut(catcher->shortcut());
}
}

Expand All @@ -58,15 +59,14 @@ void DynamicShortcutsWidget::populate(QList<QAction*> actions) {
// Create shortcut catcher for this action and set default shortcut.
auto* catcher = new ShortcutCatcher(this);

catcher->setAction(action);
catcher->setDefaultShortcut(action->shortcut());

// Store information for re-initialization of shortcuts
// of actions when widget gets "confirmed".
QPair<QAction*, ShortcutCatcher*> new_binding;
if (!action->shortcut().isEmpty()) {
m_assignedShortcuts.insert(action->shortcut(), catcher);
}

new_binding.first = action;
new_binding.second = catcher;
m_actionBindings << new_binding;
m_actionBindings.append(catcher);

// Add new catcher to our control.
auto* action_label = new QLabel(this);
Expand All @@ -89,15 +89,63 @@ void DynamicShortcutsWidget::populate(QList<QAction*> actions) {

action_icon->setPixmap(action->icon().pixmap(ICON_SIZE_SETTINGS, ICON_SIZE_SETTINGS));
action_icon->setToolTip(action->toolTip());

m_layout->addWidget(action_icon, row_id, 0);
m_layout->addWidget(action_label, row_id, 1);
m_layout->addWidget(catcher, row_id, 2);

row_id++;
connect(catcher, &ShortcutCatcher::shortcutChanged, this, &DynamicShortcutsWidget::setupChanged);
connect(catcher, &ShortcutCatcher::shortcutChanged, this, &DynamicShortcutsWidget::onShortcutChanged);
}

// Make sure that "spacer" is added.
m_layout->setRowStretch(row_id, 1);
m_layout->setColumnStretch(1, 1);
}

void DynamicShortcutsWidget::onShortcutChanged(const QKeySequence& sequence) {
ShortcutCatcher* catcher = qobject_cast<ShortcutCatcher*>(sender());
QKeySequence assigned_sequence = m_assignedShortcuts.key(catcher);

qDebugNN << catcher->action()->text();

// We remove currently assigned sequence to this catcher.
m_assignedShortcuts.remove(assigned_sequence);

if (!sequence.isEmpty()) {
// Now we check if any other catcher has assigned the same sequence.
ShortcutCatcher* conflicting_catcher = m_assignedShortcuts.value(sequence);

if (conflicting_catcher != nullptr) {
// We found conflicting catcher.
catcher->blockSignals(true);

QMessageBox::StandardButton decision =
MsgBox::show(this,
QMessageBox::Icon::Critical,
tr("Duplicate shortcut"),
tr("There is another action which has the same shortcut assigned."),
tr("Do you want to keep the new shortcut assignment and clear the previous shortcut?"),
conflicting_catcher->action()->text().remove(QSL("&")),
QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No,
QMessageBox::StandardButton::Yes);

catcher->blockSignals(false);

if (decision == QMessageBox::StandardButton::Yes) {
// We keep the new shortcut and reset old conflicting action.
m_assignedShortcuts.insert(sequence, catcher);
conflicting_catcher->clearShortcut();
}
else {
// We discard new shortcut.
catcher->clearShortcut();
}
}
else {
m_assignedShortcuts.insert(sequence, catcher);
}
}

emit setupChanged();
}
8 changes: 5 additions & 3 deletions src/librssguard/dynamic-shortcuts/dynamicshortcutswidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
class QGridLayout;
class ShortcutCatcher;

typedef QPair<QAction*, ShortcutCatcher*> ActionBinding;

class DynamicShortcutsWidget : public QWidget {
Q_OBJECT

Expand All @@ -33,12 +31,16 @@ class DynamicShortcutsWidget : public QWidget {
// assigned to actions before calling this method.
void populate(QList<QAction*> actions);

private slots:
void onShortcutChanged(const QKeySequence& sequence);

signals:
void setupChanged();

private:
QGridLayout* m_layout;
QList<ActionBinding> m_actionBindings;
QList<ShortcutCatcher*> m_actionBindings;
QHash<QKeySequence, ShortcutCatcher*> m_assignedShortcuts;
};

#endif // DYNAMICSHORTCUTSOVERVIEW_H
8 changes: 8 additions & 0 deletions src/librssguard/dynamic-shortcuts/shortcutcatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,11 @@ void ShortcutCatcher::resetShortcut() {
void ShortcutCatcher::clearShortcut() {
setShortcut(QKeySequence());
}

QAction* ShortcutCatcher::action() const {
return m_action;
}

void ShortcutCatcher::setAction(QAction* act) {
m_action = act;
}
4 changes: 4 additions & 0 deletions src/librssguard/dynamic-shortcuts/shortcutcatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ class ShortcutCatcher : public QWidget {
void setDefaultShortcut(const QKeySequence& key);
void setShortcut(const QKeySequence& key);

QAction* action() const;
void setAction(QAction* act);

public slots:
void resetShortcut();
void clearShortcut();
Expand All @@ -27,6 +30,7 @@ class ShortcutCatcher : public QWidget {
void shortcutChanged(const QKeySequence& seguence);

private:
QAction* m_action;
PlainToolButton* m_btnReset;
PlainToolButton* m_btnClear;
QKeySequenceEdit* m_shortcutBox;
Expand Down

0 comments on commit 68060c7

Please sign in to comment.