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 }