1
2
3 #include "toonzqt/menubarcommand.h"
4 //#include "menubarcommandids.h"
5 #include "toonzqt/dvdialog.h"
6 #include "toonzqt/gutil.h"
7 #include "toonz/toonzfolders.h"
8 #include "tsystem.h"
9 #include <assert.h>
10 #include <QObject>
11 #include <QAction>
12 #include <QSettings>
13 #include <QKeySequence>
14 #include <QApplication>
15
16 #include <sys/types.h>
17
18 using namespace DVGui;
19
20 //---------------------------------------------------------
21
22 namespace {
23
updateToolTip(QAction * action)24 void updateToolTip(QAction *action) {
25 QString tooltip = action->text();
26 QString shortcut = action->shortcut().toString();
27 if (shortcut != "") tooltip += " (" + shortcut + ")";
28 action->setToolTip(tooltip);
29 }
30
31 } // namespace
32
33 //=========================================================
34
AuxActionsCreator()35 AuxActionsCreator::AuxActionsCreator() {
36 AuxActionsCreatorManager::instance()->addAuxActionsCreator(this);
37 }
38 //-----------------------------------------------------------------------------
39
AuxActionsCreatorManager()40 AuxActionsCreatorManager::AuxActionsCreatorManager()
41 : m_auxActionsCreated(false) {}
42
instance()43 AuxActionsCreatorManager *AuxActionsCreatorManager::instance() {
44 static AuxActionsCreatorManager _instance;
45 return &_instance;
46 }
47
addAuxActionsCreator(AuxActionsCreator * auxActionsCreator)48 void AuxActionsCreatorManager::addAuxActionsCreator(
49 AuxActionsCreator *auxActionsCreator) {
50 m_auxActionsCreators.push_back(auxActionsCreator);
51 }
52
createAuxActions(QObject * parent)53 void AuxActionsCreatorManager::createAuxActions(QObject *parent) {
54 if (m_auxActionsCreated) return;
55 m_auxActionsCreated = true;
56 for (int i = 0; i < (int)m_auxActionsCreators.size(); i++)
57 m_auxActionsCreators[i]->createActions(parent);
58 }
59
60 //=========================================================
61
CommandManager()62 CommandManager::CommandManager() {}
63
64 //---------------------------------------------------------
65
~CommandManager()66 CommandManager::~CommandManager() {
67 std::map<std::string, Node *>::iterator it;
68 for (it = m_idTable.begin(); it != m_idTable.end(); ++it) delete it->second;
69 }
70
71 //---------------------------------------------------------
72
instance()73 CommandManager *CommandManager::instance() {
74 static CommandManager _instance;
75 return &_instance;
76 }
77
78 //---------------------------------------------------------
79
80 //
81 // command id => command
82 //
getNode(CommandId id,bool createIfNeeded)83 CommandManager::Node *CommandManager::getNode(CommandId id,
84 bool createIfNeeded) {
85 AuxActionsCreatorManager::instance()->createAuxActions(qApp);
86 std::map<std::string, Node *>::iterator it = m_idTable.find(id);
87 if (it != m_idTable.end()) return it->second;
88 if (createIfNeeded) {
89 Node *node = new Node(id);
90 m_idTable[id] = node;
91 return node;
92 } else
93 return 0;
94 }
95
96 //---------------------------------------------------------
97
setShortcut(CommandId id,QAction * action,std::string shortcutString)98 void CommandManager::setShortcut(CommandId id, QAction *action,
99 std::string shortcutString) {
100 if (shortcutString != "")
101 action->setShortcut(QKeySequence(QString::fromStdString(shortcutString)));
102 else
103 action->setShortcut(QKeySequence());
104 TFilePath fp = ToonzFolder::getMyModuleDir() + TFilePath("shortcuts.ini");
105 QSettings settings(toQString(fp), QSettings::IniFormat);
106 settings.beginGroup("shortcuts");
107 settings.setValue(QString(id), QString::fromStdString(shortcutString));
108 settings.endGroup();
109 }
110
111 //---------------------------------------------------------
112
define(CommandId id,CommandType type,std::string defaultShortcutString,QAction * qaction)113 void CommandManager::define(CommandId id, CommandType type,
114 std::string defaultShortcutString,
115 QAction *qaction) {
116 assert(type != UndefinedCommandType);
117 assert(qaction != 0);
118 assert(m_qactionTable.count(qaction) == 0);
119
120 Node *node = getNode(id);
121 if (node->m_type != UndefinedCommandType) {
122 assert(!"Duplicate command id");
123 }
124 node->m_type = type;
125 node->m_qaction = qaction;
126 node->m_qaction->setEnabled(
127 (node->m_enabled &&
128 (node->m_handler || node->m_qaction->actionGroup() != 0)) ||
129 node->m_type == MiscCommandType ||
130 node->m_type == ToolModifierCommandType);
131
132 m_qactionTable[qaction] = node;
133 qaction->setShortcutContext(Qt::ApplicationShortcut);
134 // user defined shortcuts will be loaded afterwards in loadShortcuts()
135 QString defaultShortcutQString =
136 QString::fromStdString(defaultShortcutString);
137 if (!defaultShortcutQString.isEmpty()) {
138 qaction->setShortcut(QKeySequence(defaultShortcutQString));
139 m_shortcutTable[defaultShortcutString] = node;
140 }
141
142 if (type == ToolCommandType) updateToolTip(qaction);
143 }
144
145 //---------------------------------------------------------
146
147 //
148 // set handler (id, handler)
149 // possibly changes enable/disable qaction state
150 //
setHandler(CommandId id,CommandHandlerInterface * handler)151 void CommandManager::setHandler(CommandId id,
152 CommandHandlerInterface *handler) {
153 Node *node = getNode(id);
154 if (node->m_handler != handler) {
155 delete node->m_handler;
156 node->m_handler = handler;
157 }
158 if (node->m_qaction) {
159 node->m_qaction->setEnabled(
160 node->m_enabled &&
161 (!!node->m_handler || node->m_qaction->actionGroup() != 0));
162 }
163 }
164
165 //---------------------------------------------------------
166 //
167 // qaction -> command; execute
168 //
169
execute(QAction * qaction)170 void CommandManager::execute(QAction *qaction) {
171 assert(qaction);
172 std::map<QAction *, Node *>::iterator it = m_qactionTable.find(qaction);
173 assert(it != m_qactionTable.end());
174 if (it != m_qactionTable.end() && it->second->m_handler) {
175 it->second->m_handler->execute();
176 }
177 }
178
179 //---------------------------------------------------------
180
execute(QAction * action,QAction * menuAction)181 void CommandManager::execute(QAction *action, QAction *menuAction) {
182 std::map<QAction *, Node *>::iterator it = m_qactionTable.find(action);
183 if (it != m_qactionTable.end())
184 execute(action);
185 else
186 execute(menuAction);
187 }
188
189 //---------------------------------------------------------
190
execute(CommandId id)191 void CommandManager::execute(CommandId id) {
192 Node *node = getNode(id, false);
193 if (node && node->m_handler) {
194 QAction *action = node->m_qaction;
195 if (action && action->isCheckable()) {
196 // principalmente per i tool
197 action->setChecked(true);
198 }
199 node->m_handler->execute();
200 }
201 }
202
203 //---------------------------------------------------------
204
getActions(CommandType type,std::vector<QAction * > & actions)205 void CommandManager::getActions(CommandType type,
206 std::vector<QAction *> &actions) {
207 AuxActionsCreatorManager::instance()->createAuxActions(qApp);
208 std::map<QAction *, Node *>::iterator it;
209 for (it = m_qactionTable.begin(); it != m_qactionTable.end(); ++it)
210 if (it->second->m_type == type) actions.push_back(it->first);
211 }
212
213 //---------------------------------------------------------
214
getActionFromShortcut(std::string shortcutString)215 QAction *CommandManager::getActionFromShortcut(std::string shortcutString) {
216 std::map<std::string, Node *>::iterator it =
217 m_shortcutTable.find(shortcutString);
218 return it != m_shortcutTable.end() ? it->second->m_qaction : 0;
219 }
220
221 //---------------------------------------------------------
222
getShortcutFromAction(QAction * action)223 std::string CommandManager::getShortcutFromAction(QAction *action) {
224 std::map<std::string, Node *>::iterator it = m_shortcutTable.begin();
225 for (; it != m_shortcutTable.end(); ++it) {
226 if (it->second->m_qaction == action) return it->first;
227 }
228 return "";
229 }
230
231 //---------------------------------------------------------
232
getShortcutFromId(const char * id)233 std::string CommandManager::getShortcutFromId(const char *id) {
234 QAction *action = getAction(id);
235 assert(action);
236 if (!action) return "";
237 return getShortcutFromAction(action);
238 }
239
240 //---------------------------------------------------------
241
getKeyFromShortcut(const std::string & shortcut)242 int CommandManager::getKeyFromShortcut(const std::string &shortcut) {
243 QString qShortcut = QString::fromStdString(shortcut);
244 if (qShortcut == "") return 0;
245
246 QKeySequence ks(qShortcut);
247 assert(ks.count() == 1);
248 return ks[0];
249 }
250
251 //---------------------------------------------------------
252
getKeyFromId(const char * id)253 int CommandManager::getKeyFromId(const char *id) {
254 return getKeyFromShortcut(getShortcutFromId(id));
255 }
256
257 //---------------------------------------------------------
258
setShortcut(QAction * action,std::string shortcutString,bool keepDefault)259 void CommandManager::setShortcut(QAction *action, std::string shortcutString,
260 bool keepDefault) {
261 QString shortcut = QString::fromStdString(shortcutString);
262
263 std::string oldShortcutString = action->shortcut().toString().toStdString();
264 if (oldShortcutString == shortcutString) return;
265
266 // Cerco il nodo corrispondente ad action. Deve esistere
267 std::map<QAction *, Node *>::iterator it = m_qactionTable.find(action);
268 Node *node = it != m_qactionTable.end() ? it->second : 0;
269 assert(node);
270 assert(node->m_qaction == action);
271
272 QKeySequence ks(shortcut);
273 assert(ks.count() == 1 || ks.count() == 0 && shortcut == "");
274
275 if (node->m_type == ZoomCommandType && ks.count() > 1) {
276 DVGui::warning(
277 QObject::tr("It is not possible to assign a shortcut with modifiers to "
278 "the visualization commands."));
279 return;
280 }
281 // lo shortcut e' gia' assegnato?
282 QString oldActionId;
283 std::map<std::string, Node *>::iterator sit =
284 m_shortcutTable.find(shortcutString);
285 if (sit != m_shortcutTable.end()) {
286 // la vecchia azione non ha piu' shortcut
287 oldActionId = QString::fromStdString(sit->second->m_id);
288 sit->second->m_qaction->setShortcut(QKeySequence());
289 if (sit->second->m_type == ToolCommandType)
290 updateToolTip(sit->second->m_qaction);
291 }
292 // assegno lo shortcut all'azione
293 action->setShortcut(
294 QKeySequence::fromString(QString::fromStdString(shortcutString)));
295 if (node->m_type == ToolCommandType) updateToolTip(action);
296
297 // Aggiorno la tabella shortcut -> azioni
298 // Cancello il riferimento all'eventuale vecchio shortcut di action
299 if (oldShortcutString != "") m_shortcutTable.erase(oldShortcutString);
300 // e aggiungo il nuovo legame
301 m_shortcutTable[shortcutString] = node;
302
303 // registro il tutto
304 TFilePath fp = ToonzFolder::getMyModuleDir() + TFilePath("shortcuts.ini");
305 QSettings settings(toQString(fp), QSettings::IniFormat);
306 settings.beginGroup("shortcuts");
307 settings.setValue(QString::fromStdString(node->m_id),
308 QString::fromStdString(shortcutString));
309 if (keepDefault) {
310 if (oldActionId != "") settings.remove(oldActionId);
311 }
312 settings.endGroup();
313 }
314
315 //---------------------------------------------------------
316
enable(CommandId id,bool enabled)317 void CommandManager::enable(CommandId id, bool enabled) {
318 Node *node = getNode(id, false);
319 if (!node) return;
320 if (node->m_enabled == enabled) return;
321 node->m_enabled = enabled;
322 if (node->m_qaction)
323 node->m_qaction->setEnabled(
324 node->m_enabled &&
325 (!!node->m_handler || node->m_qaction->actionGroup() != 0));
326 }
327
328 //---------------------------------------------------------
329
setChecked(CommandId id,bool checked)330 void CommandManager::setChecked(CommandId id, bool checked) {
331 Node *node = getNode(id, false);
332 if (!node) return;
333 if (node->m_qaction) {
334 node->m_qaction->setChecked(checked);
335 if (node->m_handler) node->m_handler->execute();
336 }
337 }
338
339 //---------------------------------------------------------
340
getAction(CommandId id,bool createIfNeeded)341 QAction *CommandManager::getAction(CommandId id, bool createIfNeeded) {
342 Node *node = getNode(id, createIfNeeded);
343 if (node) {
344 return node->m_qaction;
345 } else {
346 return 0;
347 }
348 }
349
350 //---------------------------------------------------------
351
createAction(CommandId id,QObject * parent,bool state)352 QAction *CommandManager::createAction(CommandId id, QObject *parent,
353 bool state) {
354 Node *node = getNode(id, false);
355 if (!node) return 0;
356 QAction *refAction = node->m_qaction;
357 if (!refAction) return 0;
358 QString text = refAction->text();
359 if (node->m_onText != "" && node->m_offText != "")
360 text = state ? node->m_onText : node->m_offText;
361 QAction *action = new QAction(text, parent);
362 action->setShortcut(refAction->shortcut());
363 return action;
364 }
365
366 //---------------------------------------------------------
367
setToggleTexts(CommandId id,const QString & onText,const QString & offText)368 void CommandManager::setToggleTexts(CommandId id, const QString &onText,
369 const QString &offText) {
370 Node *node = getNode(id, false);
371 if (node) {
372 node->m_onText = onText;
373 node->m_offText = offText;
374 }
375 }
376
377 //---------------------------------------------------------
378
getIdFromAction(QAction * action)379 std::string CommandManager::getIdFromAction(QAction *action) {
380 std::map<QAction *, Node *>::iterator it = m_qactionTable.find(action);
381 if (it != m_qactionTable.end())
382 return it->second->m_id;
383 else
384 return "";
385 }
386
387 //---------------------------------------------------------
388 // load user defined shortcuts
389
loadShortcuts()390 void CommandManager::loadShortcuts() {
391 TFilePath fp = ToonzFolder::getMyModuleDir() + TFilePath("shortcuts.ini");
392 if (!TFileStatus(fp).doesExist()) {
393 // if user shortcut file does not exist, then try to load from template
394 TFilePath tmplFp =
395 ToonzFolder::getTemplateModuleDir() + TFilePath("shortcuts.ini");
396 if (TFileStatus(tmplFp).doesExist()) TSystem::copyFile(fp, tmplFp);
397 // if neither settings exist, do nothing and return
398 else
399 return;
400 }
401
402 QSettings settings(toQString(fp), QSettings::IniFormat);
403 settings.beginGroup("shortcuts");
404 QStringList ids = settings.allKeys();
405 for (int i = 0; i < ids.size(); i++) {
406 std::string id = ids.at(i).toStdString();
407 QString shortcut = settings.value(ids.at(i), "").toString();
408 QAction *action = getAction(&id[0], false);
409 if (action) {
410 QString oldShortcut = action->shortcut().toString();
411 if (oldShortcut == shortcut) continue;
412 if (!oldShortcut.isEmpty())
413 m_shortcutTable.erase(oldShortcut.toStdString());
414 if (!shortcut.isEmpty()) {
415 QAction *other = getActionFromShortcut(shortcut.toStdString());
416 if (other) other->setShortcut(QKeySequence());
417 m_shortcutTable[shortcut.toStdString()] = getNode(&id[0]);
418 }
419 action->setShortcut(QKeySequence(shortcut));
420 }
421 }
422 settings.endGroup();
423 }
424
425 /*
426 //---------------------------------------------------------
427
428 QString CommandManager::getFullText(CommandId id, bool state)
429 {
430 std::map<std::string, Node*>::iterator it;
431 it = m_idTable.find(id);
432 if(it == m_idTable.end()) return "";
433 Node *node = it->second;
434 QAction *action = it->second->m_qaction;
435 QString text = action->text();
436 if(node->m_onText != "" && node->m_offText != "")
437 text = state ? node->m_onText : node->m_offText;
438
439 QString shortcut = QString::fromStdString(getShortcutFromAction(action));
440 if(shortcut != "") text += " " + shortcut;
441 return text;
442 }
443 */
444
445 //---------------------------------------------------------
446
MenuItemHandler(const char * cmdId)447 MenuItemHandler::MenuItemHandler(const char *cmdId) {
448 CommandManager::instance()->setHandler(
449 cmdId, new CommandHandlerHelper<MenuItemHandler>(
450 this, &MenuItemHandler::execute));
451 }
452
453 //=============================================================================
454 // DVAction
455 //-----------------------------------------------------------------------------
456
DVAction(const QString & text,QObject * parent)457 DVAction::DVAction(const QString &text, QObject *parent)
458 : QAction(text, parent) {
459 connect(this, SIGNAL(triggered()), this, SLOT(onTriggered()));
460 }
461
462 //-----------------------------------------------------------------------------
463
DVAction(const QIcon & icon,const QString & text,QObject * parent)464 DVAction::DVAction(const QIcon &icon, const QString &text, QObject *parent)
465 : QAction(icon, text, parent) {
466 connect(this, SIGNAL(triggered()), this, SLOT(onTriggered()));
467 }
468
469 //-----------------------------------------------------------------------------
470
onTriggered()471 void DVAction::onTriggered() { CommandManager::instance()->execute(this); }
472
473 //=============================================================================
474 // DVMenuAction
475 //-----------------------------------------------------------------------------
476 /*! It is a menu' with action defined in \b actions.
477 Actions can contain CommandId or simple action name.
478 In first case action triggered is connected with action command
479 execute directly.
480 In second case action triggered is connected with menu action
481 command execute;
482 is helpful to use \b m_triggeredActionIndex to distingue action.
483 */
DVMenuAction(const QString & text,QWidget * parent,QList<QString> actions)484 DVMenuAction::DVMenuAction(const QString &text, QWidget *parent,
485 QList<QString> actions)
486 : QMenu(text, parent), m_triggeredActionIndex(-1) {
487 int i;
488 for (i = 0; i < actions.size(); i++) addAction(actions.at(i));
489 connect(this, SIGNAL(triggered(QAction *)), this,
490 SLOT(onTriggered(QAction *)));
491 }
492
493 //-----------------------------------------------------------------------------
494 /*! Set a new list of action to menu'.
495 NB. If action list is composed by action menaged and action to
496 create pay
497 attention to inserted order.*/
setActions(QList<QString> actions)498 void DVMenuAction::setActions(QList<QString> actions) {
499 if (m_triggeredActionIndex != -1) // Sta facendo l'onTriggered
500 return;
501 clear();
502 int i;
503 for (i = 0; i < actions.size(); i++) {
504 QString actionId = actions.at(i);
505 // Se l'azione e' definita da un CommandId la aggiungo.
506 QAction *action =
507 CommandManager::instance()->getAction(actionId.toStdString().c_str());
508 if (action) {
509 addAction(action);
510 continue;
511 }
512 // Altrimenti creo una nuova azione e la aggiungo.
513 action = addAction(actionId);
514 action->setData(QVariant(i));
515 }
516 }
517
518 //-----------------------------------------------------------------------------
519
520 namespace {
changeStringNumber(QString str,int index)521 QString changeStringNumber(QString str, int index) {
522 QString newStr = str;
523 int n = 3;
524 if (index >= 10) n = 4;
525 QString number;
526 newStr.replace(0, n, number.number(index + 1) + QString(". "));
527 return newStr;
528 }
529 }
530
531 //-----------------------------------------------------------------------------
532
onTriggered(QAction * action)533 void DVMenuAction::onTriggered(QAction *action) {
534 QVariant data = action->data();
535 if (data.isValid()) m_triggeredActionIndex = data.toInt();
536 CommandManager::instance()->execute(action, menuAction());
537 int oldIndex = m_triggeredActionIndex;
538 if (m_triggeredActionIndex != -1) m_triggeredActionIndex = -1;
539 QString str = data.toString();
540 QAction *tableAction =
541 CommandManager::instance()->getAction(str.toStdString().c_str());
542 if (tableAction || oldIndex == 0) return;
543 QList<QAction *> acts = actions();
544 removeAction(action);
545 insertAction(acts[0], action);
546
547 acts = actions();
548 int i;
549 for (i = 0; i <= oldIndex; i++) {
550 QAction *a = acts.at(i);
551 QString newTxt = changeStringNumber(a->text(), i);
552 a->setText(newTxt);
553 a->setData(QVariant(i));
554 }
555 m_triggeredActionIndex = -1;
556 }
557
558 //-----------------------------------------------------------------------------
559