1 #include "commandbarpopup.h"
2 
3 // Tnz includes
4 #include "tapp.h"
5 #include "menubar.h"
6 #include "shortcutpopup.h"
7 
8 // TnzQt includes
9 #include "toonzqt/gutil.h"
10 
11 // TnzLib includes
12 #include "toonz/toonzfolders.h"
13 
14 // TnzCore includes
15 #include "tsystem.h"
16 
17 // Qt includes
18 #include <QMainWindow>
19 #include <QPushButton>
20 #include <QVBoxLayout>
21 #include <QHBoxLayout>
22 #include <QGridLayout>
23 #include <QHeaderView>
24 #include <QtDebug>
25 #include <QXmlStreamReader>
26 #include <QXmlStreamWriter>
27 #include <QDataStream>
28 #include <QMimeData>
29 #include <QDrag>
30 #include <QMouseEvent>
31 #include <QPainter>
32 #include <QApplication>
33 #include <QLabel>
34 
35 //=============================================================================
36 // CommandBarCommandItem
37 //-----------------------------------------------------------------------------
38 
39 class CommandBarCommandItem final : public QTreeWidgetItem {
40   QAction* m_action;
41 
42 public:
CommandBarCommandItem(QTreeWidgetItem * parent,QAction * action)43   CommandBarCommandItem(QTreeWidgetItem* parent, QAction* action)
44       : QTreeWidgetItem(parent, UserType), m_action(action) {
45     setFlags(Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled |
46              Qt::ItemNeverHasChildren);
47     QString tempText = m_action->text();
48     // removing accelerator key indicator
49     tempText = tempText.replace(QRegExp("&([^& ])"), "\\1");
50     // removing doubled &s
51     tempText = tempText.replace("&&", "&");
52     setText(0, tempText);
53     setToolTip(0, QObject::tr("[Drag] to move position"));
54   }
getAction() const55   QAction* getAction() const { return m_action; }
56 };
57 
58 //=============================================================================
59 // CommandBarSeparatorItem
60 //-----------------------------------------------------------------------------
61 
62 class CommandBarSeparatorItem final : public QTreeWidgetItem {
63 public:
CommandBarSeparatorItem(QTreeWidgetItem * parent)64   CommandBarSeparatorItem(QTreeWidgetItem* parent)
65       : QTreeWidgetItem(parent, UserType) {
66     setFlags(Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled |
67              Qt::ItemNeverHasChildren);
68     setText(0, QObject::tr("----Separator----"));
69     setToolTip(0, QObject::tr("[Drag] to move position"));
70   }
71 };
72 
73 //=============================================================================
74 // CommandBarTree
75 //-----------------------------------------------------------------------------
76 
CommandBarTree(TFilePath & path,QWidget * parent)77 CommandBarTree::CommandBarTree(TFilePath& path, QWidget* parent)
78     : QTreeWidget(parent) {
79   setObjectName("SolidLineFrame");
80   setAlternatingRowColors(true);
81   setDragEnabled(true);
82   setDropIndicatorShown(true);
83   setDefaultDropAction(Qt::MoveAction);
84   setDragDropMode(QAbstractItemView::DragDrop);
85   setIconSize(QSize(21, 17));
86 
87   setColumnCount(1);
88   header()->close();
89 
90   /*- Load path if it does exist. If not, then load from the template. -*/
91   TFilePath fp;
92   if (TFileStatus(path).isWritable())
93     fp = path;
94   else {
95     if (path.getName() == "xsheettoolbar") {
96       fp = ToonzFolder::getTemplateModuleDir() + TFilePath("xsheettoolbar.xml");
97     } else {
98       fp = ToonzFolder::getTemplateModuleDir() + TFilePath("commandbar.xml");
99     }
100   }
101 
102   loadMenuTree(fp);
103 }
104 
105 //-----------------------------------------------------------------------------
106 
loadMenuTree(const TFilePath & fp)107 void CommandBarTree::loadMenuTree(const TFilePath& fp) {
108   QFile file(toQString(fp));
109   if (!file.open(QFile::ReadOnly | QFile::Text)) {
110     qDebug() << "Cannot read file" << file.errorString();
111     return;
112   }
113 
114   QXmlStreamReader reader(&file);
115 
116   if (reader.readNextStartElement()) {
117     if (reader.name() == "commandbar") {
118       while (reader.readNextStartElement()) {
119         if (reader.name() == "command") {
120           QString cmdName = reader.readElementText();
121 
122           QAction* action = CommandManager::instance()->getAction(
123               cmdName.toStdString().c_str());
124           if (action) {
125             CommandBarCommandItem* item = new CommandBarCommandItem(0, action);
126             addTopLevelItem(item);
127           }
128         } else if (reader.name() == "separator") {
129           CommandBarSeparatorItem* sep = new CommandBarSeparatorItem(0);
130           addTopLevelItem(sep);
131           reader.skipCurrentElement();
132         } else
133           reader.skipCurrentElement();
134       }
135     } else
136       reader.raiseError(QObject::tr("Incorrect file"));
137   }
138 
139   if (reader.hasError()) {
140     qDebug() << "Cannot read menubar xml";
141   }
142 }
143 
144 //-----------------------------------------------------------------------------
145 
loadMenuRecursive(QXmlStreamReader & reader,QTreeWidgetItem * parentItem)146 void CommandBarTree::loadMenuRecursive(QXmlStreamReader& reader,
147                                        QTreeWidgetItem* parentItem) {
148   while (reader.readNextStartElement()) {
149     if (reader.name() == "command") {
150       QString cmdName = reader.readElementText();
151       QAction* action =
152           CommandManager::instance()->getAction(cmdName.toStdString().c_str());
153       if (action)
154         CommandBarCommandItem* item =
155             new CommandBarCommandItem(parentItem, action);
156     } else if (reader.name() == "command_debug") {
157 #ifndef NDEBUG
158       QString cmdName = reader.readElementText();
159       QAction* action =
160           CommandManager::instance()->getAction(cmdName.toStdString().c_str());
161       if (action)
162         CommandBarCommandItem* item =
163             new CommandBarCommandItem(parentItem, action);
164 #else
165       reader.skipCurrentElement();
166 #endif
167     } else if (reader.name() == "separator") {
168       CommandBarSeparatorItem* sep = new CommandBarSeparatorItem(parentItem);
169       reader.skipCurrentElement();
170     } else
171       reader.skipCurrentElement();
172   }
173 }
174 
175 //-----------------------------------------------------------------------------
176 
saveMenuTree(TFilePath & path)177 void CommandBarTree::saveMenuTree(TFilePath& path) {
178   QFile file(toQString(path));
179   if (!file.open(QFile::WriteOnly | QFile::Text)) {
180     qDebug() << "Cannot read file" << file.errorString();
181     return;
182   }
183 
184   QXmlStreamWriter writer(&file);
185   writer.setAutoFormatting(true);
186   writer.writeStartDocument();
187 
188   writer.writeStartElement("commandbar");
189   { saveMenuRecursive(writer, invisibleRootItem()); }
190   writer.writeEndElement();
191 
192   writer.writeEndDocument();
193 }
194 
195 //-----------------------------------------------------------------------------
196 
saveMenuRecursive(QXmlStreamWriter & writer,QTreeWidgetItem * parentItem)197 void CommandBarTree::saveMenuRecursive(QXmlStreamWriter& writer,
198                                        QTreeWidgetItem* parentItem) {
199   for (int c = 0; c < parentItem->childCount(); c++) {
200     CommandBarCommandItem* command =
201         dynamic_cast<CommandBarCommandItem*>(parentItem->child(c));
202     CommandBarSeparatorItem* sep =
203         dynamic_cast<CommandBarSeparatorItem*>(parentItem->child(c));
204 
205     if (command)
206       writer.writeTextElement(
207           "command",
208           QString::fromStdString(CommandManager::instance()->getIdFromAction(
209               command->getAction())));
210     else if (sep)
211       writer.writeEmptyElement("separator");
212   }
213 }
214 
215 //-----------------------------------------------------------------------------
216 
dropMimeData(QTreeWidgetItem * parent,int index,const QMimeData * data,Qt::DropAction action)217 bool CommandBarTree::dropMimeData(QTreeWidgetItem* parent, int index,
218                                   const QMimeData* data,
219                                   Qt::DropAction action) {
220   if (data->hasText()) {
221     QString txt = data->text();
222     QTreeWidgetItem* item;
223     if (txt == "separator")
224       item = new CommandBarSeparatorItem(0);
225     else {
226       QAction* act =
227           CommandManager::instance()->getAction(txt.toStdString().c_str());
228       if (!act) return false;
229       item = new CommandBarCommandItem(0, act);
230     }
231 
232     if (parent)
233       parent->insertChild(index, item);
234     else
235       insertTopLevelItem(index, item);
236 
237     return true;
238   }
239 
240   return false;
241 }
242 
243 //-----------------------------------------------------------------------------
244 
mimeTypes() const245 QStringList CommandBarTree::mimeTypes() const {
246   QStringList qstrList;
247   qstrList.append("text/plain");
248   return qstrList;
249 }
250 
251 //-----------------------------------------------------------------------------
252 
contextMenuEvent(QContextMenuEvent * event)253 void CommandBarTree::contextMenuEvent(QContextMenuEvent* event) {
254   QTreeWidgetItem* item = itemAt(event->pos());
255   if (item != currentItem()) setCurrentItem(item);
256   QMenu* menu = new QMenu(this);
257   QAction* action;
258 
259   if (item) {
260     action = menu->addAction(tr("Remove \"%1\"").arg(item->text(0)));
261     connect(action, SIGNAL(triggered()), this, SLOT(removeItem()));
262   }
263 
264   menu->exec(event->globalPos());
265   delete menu;
266 }
267 
268 //-----------------------------------------------------------------------------
269 
removeItem()270 void CommandBarTree::removeItem() {
271   QTreeWidgetItem* item = currentItem();
272   if (!item) return;
273 
274   if (indexOfTopLevelItem(item) >= 0)
275     takeTopLevelItem(indexOfTopLevelItem(item));
276   else
277     item->parent()->removeChild(item);
278 
279   delete item;
280 }
281 
282 //=============================================================================
283 // CommandListTree
284 //-----------------------------------------------------------------------------
285 
CommandBarListTree(QWidget * parent)286 CommandBarListTree::CommandBarListTree(QWidget* parent) : QTreeWidget(parent) {
287   setObjectName("SolidLineFrame");
288   setAlternatingRowColors(true);
289   setDragEnabled(true);
290   setDragDropMode(QAbstractItemView::DragOnly);
291   setColumnCount(1);
292   setIconSize(QSize(21, 18));
293   header()->close();
294 
295   QIcon menuFolderIcon(createQIcon("folder_project", true));
296   invisibleRootItem()->setIcon(0, menuFolderIcon);
297 
298   QTreeWidgetItem* menuCommandFolder = new QTreeWidgetItem(this);
299   menuCommandFolder->setFlags(Qt::ItemIsEnabled);
300   menuCommandFolder->setText(0, ShortcutTree::tr("Menu Commands"));
301   menuCommandFolder->setExpanded(true);
302   menuCommandFolder->setIcon(0, invisibleRootItem()->icon(0));
303 
304   addFolder(ShortcutTree::tr("File"), MenuFileCommandType, menuCommandFolder);
305   addFolder(ShortcutTree::tr("Edit"), MenuEditCommandType, menuCommandFolder);
306   addFolder(ShortcutTree::tr("Scan & Cleanup"), MenuScanCleanupCommandType,
307             menuCommandFolder);
308   addFolder(ShortcutTree::tr("Level"), MenuLevelCommandType, menuCommandFolder);
309   addFolder(ShortcutTree::tr("Xsheet"), MenuXsheetCommandType,
310             menuCommandFolder);
311   addFolder(ShortcutTree::tr("Cells"), MenuCellsCommandType, menuCommandFolder);
312   addFolder(ShortcutTree::tr("Play"), MenuPlayCommandType, menuCommandFolder);
313   addFolder(ShortcutTree::tr("Render"), MenuRenderCommandType,
314             menuCommandFolder);
315   addFolder(ShortcutTree::tr("View"), MenuViewCommandType, menuCommandFolder);
316   addFolder(ShortcutTree::tr("Windows"), MenuWindowsCommandType,
317             menuCommandFolder);
318   addFolder(ShortcutTree::tr("Help"), MenuHelpCommandType, menuCommandFolder);
319 
320   addFolder(ShortcutTree::tr("Tools"), ToolCommandType);
321   addFolder(ShortcutTree::tr("Fill"), FillCommandType);
322   addFolder(ShortcutTree::tr("Right-click Menu Commands"),
323             RightClickMenuCommandType);
324   addFolder(ShortcutTree::tr("Tool Modifiers"), ToolModifierCommandType);
325   addFolder(ShortcutTree::tr("Visualization"), VisualizationButtonCommandType);
326   addFolder(ShortcutTree::tr("Misc"), MiscCommandType);
327   addFolder(ShortcutTree::tr("RGBA Channels"), RGBACommandType);
328 
329   sortItems(0, Qt::AscendingOrder);
330 
331   CommandBarSeparatorItem* sep = new CommandBarSeparatorItem(0);
332   sep->setToolTip(0, QObject::tr("[Drag&Drop] to copy separator to menu bar"));
333   addTopLevelItem(sep);
334 }
335 
336 //-----------------------------------------------------------------------------
337 
addFolder(const QString & title,int commandType,QTreeWidgetItem * parentFolder)338 void CommandBarListTree::addFolder(const QString& title, int commandType,
339                                    QTreeWidgetItem* parentFolder) {
340   QTreeWidgetItem* folder;
341   if (!parentFolder)
342     folder = new QTreeWidgetItem(this);
343   else
344     folder = new QTreeWidgetItem(parentFolder);
345   assert(folder);
346   folder->setText(0, title);
347   folder->setIcon(0, invisibleRootItem()->icon(0));
348 
349   std::vector<QAction*> actions;
350   CommandManager::instance()->getActions((CommandType)commandType, actions);
351   for (int i = 0; i < (int)actions.size(); i++) {
352     CommandBarCommandItem* item = new CommandBarCommandItem(folder, actions[i]);
353     item->setToolTip(0, QObject::tr("[Drag&Drop] to copy command to menu bar"));
354   }
355 }
356 
357 //-----------------------------------------------------------------------------
358 
mousePressEvent(QMouseEvent * event)359 void CommandBarListTree::mousePressEvent(QMouseEvent* event) {
360   setCurrentItem(itemAt(event->pos()));
361   CommandBarCommandItem* commandItem =
362       dynamic_cast<CommandBarCommandItem*>(itemAt(event->pos()));
363   CommandBarSeparatorItem* separatorItem =
364       dynamic_cast<CommandBarSeparatorItem*>(itemAt(event->pos()));
365 
366   if (commandItem || separatorItem) {
367     std::string dragStr;
368     QString dragPixmapTxt;
369     if (commandItem) {
370       dragStr =
371           CommandManager::instance()->getIdFromAction(commandItem->getAction());
372       dragPixmapTxt = commandItem->getAction()->text();
373       dragPixmapTxt.remove("&");
374     } else {
375       dragStr       = "separator";
376       dragPixmapTxt = tr("----Separator----");
377     }
378 
379     QMimeData* mimeData = new QMimeData;
380     mimeData->setText(QString::fromStdString(dragStr));
381 
382     QFontMetrics fm(QApplication::font());
383     QPixmap pix(fm.boundingRect(dragPixmapTxt).adjusted(-2, -2, 2, 2).size());
384     QPainter painter(&pix);
385     painter.fillRect(pix.rect(), Qt::white);
386     painter.setPen(Qt::black);
387     painter.drawText(pix.rect(), Qt::AlignCenter, dragPixmapTxt);
388 
389     QDrag* drag = new QDrag(this);
390     drag->setMimeData(mimeData);
391     drag->setPixmap(pix);
392 
393     drag->exec(Qt::CopyAction);
394   }
395 
396   QTreeWidget::mousePressEvent(event);
397 }
398 
399 //=============================================================================
400 // CommandBarPopup
401 //-----------------------------------------------------------------------------
402 
CommandBarPopup(bool isXsheetToolbar)403 CommandBarPopup::CommandBarPopup(bool isXsheetToolbar)
404     : Dialog(TApp::instance()->getMainWindow(), true, false,
405              "CustomizeCommandBar") {
406   QLabel* commandBarLabel;
407   if (isXsheetToolbar) {
408     m_path = ToonzFolder::getMyModuleDir() + TFilePath("xsheettoolbar.xml");
409     commandBarLabel = new QLabel(tr("XSheet Toolbar"));
410     setWindowTitle(tr("Customize XSheet Toolbar"));
411   } else {
412     m_path = ToonzFolder::getMyModuleDir() + TFilePath("commandbar.xml");
413     commandBarLabel = new QLabel(tr("Command Bar"));
414     setWindowTitle(tr("Customize Command Bar"));
415   }
416 
417   m_commandListTree = new CommandBarListTree(this);
418   m_menuBarTree     = new CommandBarTree(m_path, this);
419 
420   QPushButton* okBtn     = new QPushButton(tr("OK"), this);
421   QPushButton* cancelBtn = new QPushButton(tr("Cancel"), this);
422 
423   okBtn->setFocusPolicy(Qt::NoFocus);
424   cancelBtn->setFocusPolicy(Qt::NoFocus);
425 
426   QLabel* commandItemListLabel = new QLabel(tr("Toolbar Items"), this);
427 
428   QFont f("Arial", 15, QFont::Bold);
429   commandBarLabel->setFont(f);
430   commandItemListLabel->setFont(f);
431 
432   QLabel* noticeLabel =
433       new QLabel(tr("Duplicated commands will be ignored. Only "
434                     "the last one will appear in the menu bar."),
435                  this);
436   QFont nf("Arial", 9, QFont::Normal);
437   nf.setItalic(true);
438   noticeLabel->setFont(nf);
439 
440   //--- layout
441   QVBoxLayout* mainLay = new QVBoxLayout();
442   m_topLayout->setMargin(0);
443   m_topLayout->setSpacing(0);
444   {
445     QGridLayout* mainUILay = new QGridLayout();
446     mainUILay->setMargin(5);
447     mainUILay->setHorizontalSpacing(8);
448     mainUILay->setVerticalSpacing(5);
449     {
450       mainUILay->addWidget(commandBarLabel, 0, 0);
451       mainUILay->addWidget(commandItemListLabel, 0, 1);
452       mainUILay->addWidget(m_menuBarTree, 1, 0);
453       mainUILay->addWidget(m_commandListTree, 1, 1);
454 
455       mainUILay->addWidget(noticeLabel, 2, 0, 1, 2);
456     }
457     mainUILay->setRowStretch(0, 0);
458     mainUILay->setRowStretch(1, 1);
459     mainUILay->setRowStretch(2, 0);
460     mainUILay->setColumnStretch(0, 1);
461     mainUILay->setColumnStretch(1, 1);
462 
463     m_topLayout->addLayout(mainUILay, 1);
464   }
465 
466   m_buttonLayout->setMargin(0);
467   m_buttonLayout->setSpacing(30);
468   {
469     m_buttonLayout->addStretch(1);
470     m_buttonLayout->addWidget(okBtn, 0);
471     m_buttonLayout->addWidget(cancelBtn, 0);
472     m_buttonLayout->addStretch(1);
473   }
474 
475   //--- signal/slot connections
476 
477   bool ret = connect(okBtn, SIGNAL(clicked()), this, SLOT(onOkPressed()));
478   ret      = ret && connect(cancelBtn, SIGNAL(clicked()), this, SLOT(reject()));
479   assert(ret);
480 }
481 
482 //-----------------------------------------------------------------------------
483 
onOkPressed()484 void CommandBarPopup::onOkPressed() {
485   m_menuBarTree->saveMenuTree(m_path);
486 
487   accept();
488 }