source: smplayer/trunk/src/actionseditor.cpp@ 178

Last change on this file since 178 was 176, checked in by Silvan Scherrer, 9 years ago

smplayer: update trunk to version 16.4

  • Property svn:eol-style set to LF
File size: 18.5 KB
Line 
1/* smplayer, GUI front-end for mplayer.
2 Copyright (C) 2006-2016 Ricardo Villalba <rvm@users.sourceforge.net>
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17*/
18
19/* This is based on qq14-actioneditor-code.zip from Qt */
20
21
22#include "actionseditor.h"
23
24#include <QTableWidget>
25#include <QHeaderView>
26
27#include <QLayout>
28#include <QObject>
29#include <QPushButton>
30#include <QString>
31#include <QSettings>
32#include <QFile>
33#include <QTextStream>
34#include <QMessageBox>
35#include <QFileInfo>
36#include <QRegExp>
37#include <QApplication>
38#include <QAction>
39#include <QDebug>
40
41#include "images.h"
42#include "filedialog.h"
43#include "paths.h"
44
45#include "shortcutgetter.h"
46
47
48/*
49#include <QLineEdit>
50#include <QItemDelegate>
51
52class MyDelegate : public QItemDelegate
53{
54public:
55 MyDelegate(QObject *parent = 0);
56
57 QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
58 const QModelIndex &index) const;
59 virtual void setModelData(QWidget * editor, QAbstractItemModel * model,
60 const QModelIndex & index ) const;
61};
62
63MyDelegate::MyDelegate(QObject *parent) : QItemDelegate(parent)
64{
65}
66
67static QString old_accel_text;
68
69QWidget * MyDelegate::createEditor(QWidget *parent,
70 const QStyleOptionViewItem & option,
71 const QModelIndex & index) const
72{
73 qDebug("MyDelegate::createEditor");
74
75 old_accel_text = index.model()->data(index, Qt::DisplayRole).toString();
76 //qDebug( "text: %s", old_accel_text.toUtf8().data());
77
78 return QItemDelegate::createEditor(parent, option, index);
79}
80
81void MyDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
82 const QModelIndex &index) const
83{
84 QLineEdit *line_edit = static_cast<QLineEdit*>(editor);
85
86 QString accelText = QKeySequence(line_edit->text()).toString();
87 if (accelText.isEmpty() && !line_edit->text().isEmpty()) {
88 model->setData(index, old_accel_text);
89 }
90 else {
91 model->setData(index, accelText);
92 }
93}
94*/
95
96
97#if USE_MULTIPLE_SHORTCUTS
98QString ActionsEditor::shortcutsToString(QList <QKeySequence> shortcuts_list) {
99 QString accelText = "";
100
101 for (int n=0; n < shortcuts_list.count(); n++) {
102 accelText += shortcuts_list[n].toString(QKeySequence::PortableText);
103 if (n < (shortcuts_list.count()-1)) accelText += ", ";
104 }
105
106 //qDebug("ActionsEditor::shortcutsToString: accelText: '%s'", accelText.toUtf8().constData());
107
108 return accelText;
109}
110
111QList <QKeySequence> ActionsEditor::stringToShortcuts(QString shortcuts) {
112 QList <QKeySequence> shortcuts_list;
113
114 QStringList l = shortcuts.split(", ");
115
116 for (int n=0; n < l.count(); n++) {
117 //qDebug("%s", l[n].toUtf8().data());
118#if QT_VERSION >= 0x040300
119 // Qt 4.3 and 4.4 (at least on linux) seems to have a problem when using Traditional Chinese
120 // QKeysequence deletes the arrow key names from the shortcut
121 // so this is a work-around.
122 QString s = l[n].simplified();
123#else
124 QString s = QKeySequence( l[n].simplified() );
125#endif
126
127 //Work-around for Simplified-Chinese
128 s.replace( QString::fromUtf8("å·Š"), "Left");
129 s.replace( QString::fromUtf8("例"), "Down");
130 s.replace( QString::fromUtf8("右"), "Right");
131 s.replace( QString::fromUtf8("侊"), "Up");
132
133 shortcuts_list.append( s );
134 //qDebug("ActionsEditor::stringToShortcuts: shortcut %d: '%s'", n, s.toUtf8().data());
135 }
136
137 return shortcuts_list;
138}
139#endif
140
141
142#define COL_CONFLICTS 0
143#define COL_SHORTCUT 1
144#define COL_DESC 2
145#define COL_NAME 3
146
147ActionsEditor::ActionsEditor(QWidget * parent, Qt::WindowFlags f)
148 : QWidget(parent, f)
149{
150 latest_dir = Paths::shortcutsPath();
151
152 actionsTable = new QTableWidget(0, COL_NAME +1, this);
153 actionsTable->setSelectionMode( QAbstractItemView::SingleSelection );
154 actionsTable->verticalHeader()->hide();
155
156#if QT_VERSION >= 0x050000
157 actionsTable->horizontalHeader()->setSectionResizeMode(COL_DESC, QHeaderView::Stretch);
158 actionsTable->horizontalHeader()->setSectionResizeMode(COL_NAME, QHeaderView::Stretch);
159 actionsTable->horizontalHeader()->setSectionResizeMode(COL_CONFLICTS, QHeaderView::ResizeToContents);
160#else
161 actionsTable->horizontalHeader()->setResizeMode(COL_DESC, QHeaderView::Stretch);
162 actionsTable->horizontalHeader()->setResizeMode(COL_NAME, QHeaderView::Stretch);
163 actionsTable->horizontalHeader()->setResizeMode(COL_CONFLICTS, QHeaderView::ResizeToContents);
164#endif
165
166 actionsTable->setAlternatingRowColors(true);
167#if USE_SHORTCUTGETTER
168 actionsTable->setSelectionBehavior(QAbstractItemView::SelectRows);
169 actionsTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
170#endif
171 //actionsTable->setItemDelegateForColumn( COL_SHORTCUT, new MyDelegate(actionsTable) );
172
173#if !USE_SHORTCUTGETTER
174 connect(actionsTable, SIGNAL(currentItemChanged(QTableWidgetItem *,QTableWidgetItem *)),
175 this, SLOT(recordAction(QTableWidgetItem *)) );
176 connect(actionsTable, SIGNAL(itemChanged(QTableWidgetItem *)),
177 this, SLOT(validateAction(QTableWidgetItem *)) );
178#else
179 connect(actionsTable, SIGNAL(itemActivated(QTableWidgetItem *)),
180 this, SLOT(editShortcut()) );
181#endif
182
183 saveButton = new QPushButton(this);
184 loadButton = new QPushButton(this);
185
186 connect(saveButton, SIGNAL(clicked()), this, SLOT(saveActionsTable()));
187 connect(loadButton, SIGNAL(clicked()), this, SLOT(loadActionsTable()));
188
189#if USE_SHORTCUTGETTER
190 editButton = new QPushButton(this);
191 connect( editButton, SIGNAL(clicked()), this, SLOT(editShortcut()) );
192#endif
193
194 QHBoxLayout *buttonLayout = new QHBoxLayout;
195 buttonLayout->setSpacing(8);
196#if USE_SHORTCUTGETTER
197 buttonLayout->addWidget(editButton);
198#endif
199 buttonLayout->addStretch(1);
200 buttonLayout->addWidget(loadButton);
201 buttonLayout->addWidget(saveButton);
202
203 QVBoxLayout *mainLayout = new QVBoxLayout(this);
204 mainLayout->setMargin(8);
205 mainLayout->setSpacing(8);
206 mainLayout->addWidget(actionsTable);
207 mainLayout->addLayout(buttonLayout);
208
209 retranslateStrings();
210}
211
212ActionsEditor::~ActionsEditor() {
213}
214
215void ActionsEditor::retranslateStrings() {
216 actionsTable->setHorizontalHeaderLabels( QStringList() << "" <<
217 tr("Shortcut") << tr("Description") << tr("Name") );
218
219 saveButton->setText(tr("&Save"));
220 saveButton->setIcon(Images::icon("save"));
221
222 loadButton->setText(tr("&Load"));
223 loadButton->setIcon(Images::icon("open"));
224
225#if USE_SHORTCUTGETTER
226 editButton->setText(tr("&Change shortcut..."));
227#endif
228
229 //updateView(); // The actions are translated later, so it's useless
230}
231
232bool ActionsEditor::isEmpty() {
233 return actionsList.isEmpty();
234}
235
236void ActionsEditor::clear() {
237 actionsList.clear();
238}
239
240void ActionsEditor::addActions(QWidget *widget) {
241 QAction *action;
242
243 QList<QAction *> actions = widget->findChildren<QAction *>();
244 for (int n=0; n < actions.count(); n++) {
245 action = static_cast<QAction*> (actions[n]);
246 /*
247 if (!action->objectName().isEmpty()) {
248 qDebug("ActionsEditor::addActions: action # %d: '%s' menu: %d", n, action->objectName().toUtf8().constData(), action->menu()!=0);
249 }
250 */
251 if (!action->objectName().isEmpty() && !action->inherits("QWidgetAction") && (action->menu()==0) )
252 actionsList.append(action);
253 }
254
255 updateView();
256}
257
258void ActionsEditor::updateView() {
259 actionsTable->setRowCount( actionsList.count() );
260
261 QAction *action;
262 QString accelText;
263
264#if !USE_SHORTCUTGETTER
265 dont_validate = true;
266#endif
267 //actionsTable->setSortingEnabled(false);
268
269 for (int n=0; n < actionsList.count(); n++) {
270 action = static_cast<QAction*> (actionsList[n]);
271
272#if USE_MULTIPLE_SHORTCUTS
273 accelText = shortcutsToString( action->shortcuts() );
274#else
275 accelText = action->shortcut().toString();
276#endif
277
278 // Conflict column
279 QTableWidgetItem * i_conf = new QTableWidgetItem();
280
281 // Name column
282 QTableWidgetItem * i_name = new QTableWidgetItem(action->objectName());
283
284 // Desc column
285 QTableWidgetItem * i_desc = new QTableWidgetItem(action->text().replace("&",""));
286 i_desc->setIcon( action->icon() );
287
288 // Shortcut column
289 QTableWidgetItem * i_shortcut = new QTableWidgetItem(accelText);
290 int column_height = i_shortcut->sizeHint().height();
291 i_shortcut->setSizeHint(QSize(150, column_height));
292
293 // Set flags
294#if !USE_SHORTCUTGETTER
295 i_conf->setFlags(Qt::ItemIsEnabled);
296 i_name->setFlags(Qt::ItemIsEnabled);
297 i_desc->setFlags(Qt::ItemIsEnabled);
298#else
299 i_conf->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
300 i_name->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
301 i_desc->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
302 i_shortcut->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
303#endif
304
305 // Add items to table
306 actionsTable->setItem(n, COL_CONFLICTS, i_conf );
307 actionsTable->setItem(n, COL_NAME, i_name );
308 actionsTable->setItem(n, COL_DESC, i_desc );
309 actionsTable->setItem(n, COL_SHORTCUT, i_shortcut );
310
311 }
312 hasConflicts(); // Check for conflicts
313
314 actionsTable->resizeColumnsToContents();
315 actionsTable->setCurrentCell(0, COL_SHORTCUT);
316
317#if !USE_SHORTCUTGETTER
318 dont_validate = false;
319#endif
320 //actionsTable->setSortingEnabled(true);
321}
322
323
324void ActionsEditor::applyChanges() {
325 qDebug("ActionsEditor::applyChanges");
326
327 for (int row = 0; row < (int)actionsList.size(); ++row) {
328 QAction *action = actionsList[row];
329 QTableWidgetItem *i = actionsTable->item(row, COL_SHORTCUT);
330
331#if USE_MULTIPLE_SHORTCUTS
332 action->setShortcuts( stringToShortcuts(i->text()) );
333#else
334 action->setShortcut( QKeySequence(i->text()) );
335#endif
336 }
337}
338
339#if !USE_SHORTCUTGETTER
340void ActionsEditor::recordAction(QTableWidgetItem * i) {
341 //qDebug("ActionsEditor::recordAction");
342
343 //QTableWidgetItem * i = actionsTable->currentItem();
344 if (i->column() == COL_SHORTCUT) {
345 //qDebug("ActionsEditor::recordAction: %d %d %s", i->row(), i->column(), i->text().toUtf8().data());
346 oldAccelText = i->text();
347 }
348}
349
350void ActionsEditor::validateAction(QTableWidgetItem * i) {
351 //qDebug("ActionsEditor::validateAction");
352 if (dont_validate) return;
353
354 if (i->column() == COL_SHORTCUT) {
355 QString accelText = QKeySequence(i->text()).toString();
356
357 if (accelText.isEmpty() && !i->text().isEmpty()) {
358 /*
359 QAction * action = static_cast<QAction*> (actionsList[i->row()]);
360 QString oldAccelText= action->accel().toString();
361 */
362 i->setText(oldAccelText);
363 }
364 else {
365 i->setText(accelText);
366 }
367
368 if (hasConflicts()) qApp->beep();
369 }
370}
371
372#else
373
374void ActionsEditor::editShortcut() {
375 QTableWidgetItem * i = actionsTable->item( actionsTable->currentRow(), COL_SHORTCUT );
376 if (i) {
377 ShortcutGetter d(this);
378 QString result = d.exec( i->text() );
379
380 if (!result.isNull()) {
381 //qDebug("ActionsEditor::editShortcut: result: '%s'", result.toUtf8().constData());
382 QString accelText = QKeySequence(result).toString(QKeySequence::PortableText);
383 i->setText(accelText);
384 if (hasConflicts()) qApp->beep();
385 }
386 }
387}
388#endif
389
390int ActionsEditor::findActionName(const QString & name) {
391 for (int row=0; row < actionsTable->rowCount(); row++) {
392 if (actionsTable->item(row, COL_NAME)->text() == name) return row;
393 }
394 return -1;
395}
396
397bool ActionsEditor::containsShortcut(const QString & accel, const QString & shortcut) {
398 QStringList shortcut_list = accel.split(", ");
399 QString s;
400 foreach(s, shortcut_list) {
401 s = s.trimmed();
402 //qDebug("ActionsEditor::containsShortcut: comparing '%s' with '%s'", s.toUtf8().constData(), shortcut.toUtf8().constData());
403 if (s == shortcut) return true;
404 }
405 return false;
406}
407
408int ActionsEditor::findActionAccel(const QString & accel, int ignoreRow) {
409 QStringList shortcuts = accel.split(", ");
410 QString shortcut;
411
412 for (int row = 0; row < actionsTable->rowCount(); row++) {
413 QTableWidgetItem * i = actionsTable->item(row, COL_SHORTCUT);
414 if (i && row != ignoreRow) {
415 if (!i->text().isEmpty()) {
416 foreach(shortcut, shortcuts) {
417 if (containsShortcut(i->text(), shortcut.trimmed())) {
418 return row;
419 }
420 }
421 }
422 }
423 }
424 return -1;
425}
426
427bool ActionsEditor::hasConflicts() {
428 int found;
429 bool conflict = false;
430
431 QString accelText;
432 QTableWidgetItem *i;
433
434 for (int n = 0; n < actionsTable->rowCount(); n++) {
435 //actionsTable->setText( n, COL_CONFLICTS, " ");
436 i = actionsTable->item( n, COL_CONFLICTS );
437 if (i) i->setIcon( QPixmap() );
438
439 i = actionsTable->item(n, COL_SHORTCUT );
440 if (i) {
441 accelText = i->text();
442 if (!accelText.isEmpty()) {
443 found = findActionAccel( accelText, n );
444 if ( (found != -1) /*&& (found != n)*/ ) {
445 conflict = true;
446 //actionsTable->setText( n, COL_CONFLICTS, "!");
447 actionsTable->item( n, COL_CONFLICTS )->setIcon( Images::icon("conflict") );
448 }
449 }
450 }
451 }
452 //if (conflict) qApp->beep();
453 return conflict;
454}
455
456
457void ActionsEditor::saveActionsTable() {
458 QString s = MyFileDialog::getSaveFileName(
459 this, tr("Choose a filename"),
460 latest_dir,
461 tr("Key files") +" (*.keys)" );
462
463 if (!s.isEmpty()) {
464 // If filename has no extension, add it
465 if (QFileInfo(s).suffix().isEmpty()) {
466 s = s + ".keys";
467 }
468 if (QFileInfo(s).exists()) {
469 int res = QMessageBox::question( this,
470 tr("Confirm overwrite?"),
471 tr("The file %1 already exists.\n"
472 "Do you want to overwrite?").arg(s),
473 QMessageBox::Yes,
474 QMessageBox::No,
475 Qt::NoButton);
476 if (res == QMessageBox::No ) {
477 return;
478 }
479 }
480 latest_dir = QFileInfo(s).absolutePath();
481 bool r = saveActionsTable(s);
482 if (!r) {
483 QMessageBox::warning(this, tr("Error"),
484 tr("The file couldn't be saved"),
485 QMessageBox::Ok, Qt::NoButton);
486 }
487 }
488}
489
490bool ActionsEditor::saveActionsTable(const QString & filename) {
491 qDebug("ActionsEditor::saveActions: '%s'", filename.toUtf8().data());
492
493 QFile f( filename );
494 if ( f.open( QIODevice::WriteOnly ) ) {
495 QTextStream stream( &f );
496 stream.setCodec("UTF-8");
497
498 for (int row=0; row < actionsTable->rowCount(); row++) {
499 stream << actionsTable->item(row, COL_NAME)->text() << "\t"
500 << actionsTable->item(row, COL_SHORTCUT)->text() << "\n";
501 }
502 f.close();
503 return true;
504 }
505 return false;
506}
507
508void ActionsEditor::loadActionsTable() {
509 QString s = MyFileDialog::getOpenFileName(
510 this, tr("Choose a file"),
511 latest_dir, tr("Key files") +" (*.keys)" );
512
513 if (!s.isEmpty()) {
514 latest_dir = QFileInfo(s).absolutePath();
515 bool r = loadActionsTable(s);
516 if (!r) {
517 QMessageBox::warning(this, tr("Error"),
518 tr("The file couldn't be loaded"),
519 QMessageBox::Ok, Qt::NoButton);
520 }
521 }
522}
523
524bool ActionsEditor::loadActionsTable(const QString & filename) {
525 qDebug() << "ActionsEditor::loadActions:" << filename;
526
527 int row;
528
529 QFile f( filename );
530 if ( f.open( QIODevice::ReadOnly ) ) {
531
532#if !USE_SHORTCUTGETTER
533 dont_validate = true;
534#endif
535
536 QTextStream stream( &f );
537 stream.setCodec("UTF-8");
538
539 QString line;
540 while ( !stream.atEnd() ) {
541 line = stream.readLine().trimmed();
542 qDebug() << "ActionsEditor::loadActions: line:" << line;
543 QString name;
544 QString accelText;
545 int pos = line.indexOf(QRegExp("\\t|\\s"));
546 //qDebug() << "ActionsEditor::loadActions: pos:" << pos;
547 if (pos == -1) {
548 name = line;
549 } else {
550 name = line.left(pos);
551 accelText = line.mid(pos+1).trimmed();
552 }
553 qDebug() << "ActionsEditor::loadActions: name:" << name << "accel:" << accelText;
554 if (!name.isEmpty()) {
555 row = findActionName(name);
556 if (row > -1) {
557 qDebug() << "ActionsEditor::loadActions: action found!";
558 actionsTable->item(row, COL_SHORTCUT)->setText(accelText);
559 }
560 } else {
561 qDebug() << "ActionsEditor::loadActions: error in line";
562 }
563 }
564 f.close();
565 hasConflicts(); // Check for conflicts
566
567#if !USE_SHORTCUTGETTER
568 dont_validate = false;
569#endif
570
571 return true;
572 } else {
573 return false;
574 }
575}
576
577
578// Static functions
579
580void ActionsEditor::saveToConfig(QObject *o, QSettings *set) {
581 qDebug("ActionsEditor::saveToConfig");
582
583 set->beginGroup("actions");
584
585 QAction *action;
586 QList<QAction *> actions = o->findChildren<QAction *>();
587 for (int n=0; n < actions.count(); n++) {
588 action = static_cast<QAction*> (actions[n]);
589 if (!action->objectName().isEmpty() && !action->inherits("QWidgetAction")) {
590#if USE_MULTIPLE_SHORTCUTS
591 QString accelText = shortcutsToString(action->shortcuts());
592#else
593 QString accelText = action->shortcut().toString();
594#endif
595 set->setValue(action->objectName(), accelText);
596 }
597 }
598
599 set->endGroup();
600}
601
602
603void ActionsEditor::loadFromConfig(QObject *o, QSettings *set) {
604 qDebug("ActionsEditor::loadFromConfig");
605
606 set->beginGroup("actions");
607
608 QAction *action;
609 QString accelText;
610
611 QList<QAction *> actions = o->findChildren<QAction *>();
612 for (int n=0; n < actions.count(); n++) {
613 action = static_cast<QAction*> (actions[n]);
614 if (!action->objectName().isEmpty() && !action->inherits("QWidgetAction")) {
615#if USE_MULTIPLE_SHORTCUTS
616 QString current = shortcutsToString(action->shortcuts());
617 accelText = set->value(action->objectName(), current).toString();
618 action->setShortcuts( stringToShortcuts( accelText ) );
619#else
620 accelText = set->value(action->objectName(), action->shortcut().toString()).toString();
621 action->setShortcut(QKeySequence(accelText));
622#endif
623 }
624 }
625
626 set->endGroup();
627}
628
629QAction * ActionsEditor::findAction(QObject *o, const QString & name) {
630 QAction *action;
631
632 QList<QAction *> actions = o->findChildren<QAction *>();
633 for (int n=0; n < actions.count(); n++) {
634 action = static_cast<QAction*> (actions[n]);
635 /* qDebug("ActionsEditor::findAction: %s", action->objectName().toLatin1().constData()); */
636 if (name == action->objectName()) return action;
637 }
638
639 return 0;
640}
641
642QStringList ActionsEditor::actionsNames(QObject *o) {
643 QStringList l;
644
645 QAction *action;
646
647 QList<QAction *> actions = o->findChildren<QAction *>();
648 for (int n=0; n < actions.count(); n++) {
649 action = static_cast<QAction*> (actions[n]);
650 //qDebug("action name: '%s'", action->objectName().toUtf8().data());
651 //qDebug("action name: '%s'", action->text().toUtf8().data());
652 if (!action->objectName().isEmpty())
653 l.append( action->objectName() );
654 }
655
656 return l;
657}
658
659
660// Language change stuff
661void ActionsEditor::changeEvent(QEvent *e) {
662 if (e->type() == QEvent::LanguageChange) {
663 retranslateStrings();
664 } else {
665 QWidget::changeEvent(e);
666 }
667}
668
669#include "moc_actionseditor.cpp"
Note: See TracBrowser for help on using the repository browser.