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