1 
2 
3 #include "sceneviewer.h"
4 #include "sceneviewercontextmenu.h"
5 
6 // Toonz includes
7 #include "tapp.h"
8 #include "menubarcommandids.h"
9 #include "onionskinmaskgui.h"
10 
11 // TnzTools includes
12 #include "tools/toolhandle.h"
13 #include "tools/toolcommandids.h"
14 #include "tools/strokeselection.h"
15 
16 // TnzQt includes
17 #include "toonzqt/menubarcommand.h"
18 #include "toonzqt/viewcommandids.h"
19 #include "toonzqt/selection.h"
20 #include "toonzqt/imageutils.h"
21 
22 // TnzLib includes
23 #include "toonz/txsheethandle.h"
24 #include "toonz/tcolumnhandle.h"
25 #include "toonz/tframehandle.h"
26 #include "toonz/tobjecthandle.h"
27 #include "toonz/tstageobjecttree.h"
28 #include "toonz/tscenehandle.h"
29 #include "toonz/txshcolumn.h"
30 #include "toonz/tstageobjectspline.h"
31 #include "toonz/tstageobjectid.h"
32 #include "toonz/preferences.h"
33 
34 // TnzCore includes
35 #include "tvectorimage.h"
36 
37 // Qt includes
38 #include <QMenu>
39 #include <QContextMenuEvent>
40 #include <QSignalMapper>
41 
SceneViewerContextMenu(SceneViewer * parent)42 SceneViewerContextMenu::SceneViewerContextMenu(SceneViewer *parent)
43     : QMenu(parent), m_viewer(parent), m_groupIndexToBeEntered(-1) {
44   TApp *app                      = TApp::instance();
45   bool isEditingLevel            = app->getCurrentFrame()->isEditingLevel();
46   bool ret                       = true;
47   QAction *action                = 0;
48   CommandManager *commandManager = CommandManager::instance();
49 
50   /*- サブカメラの消去 -*/
51   if (parent->isEditPreviewSubcamera()) {
52     action = addAction(tr("Reset Subcamera"));
53     ret    = ret && parent->connect(action, SIGNAL(triggered()),
54                                  SLOT(doDeleteSubCamera()));
55     addSeparator();
56   }
57 
58   // tool
59   TTool *tool = app->getCurrentTool()->getTool();
60   if (tool && tool->isEnabled()) tool->addContextMenuItems(this);
61 
62   // fullscreen
63   if (ImageUtils::FullScreenWidget *fsWidget =
64           dynamic_cast<ImageUtils::FullScreenWidget *>(
65               m_viewer->parentWidget())) {
66     bool isFullScreen = (fsWidget->windowState() & Qt::WindowFullScreen) != 0;
67 
68     action =
69         commandManager->createAction(V_ShowHideFullScreen, this, !isFullScreen);
70     addAction(action);
71     ret = ret && parent->connect(action, SIGNAL(triggered()), fsWidget,
72                                  SLOT(toggleFullScreen()));
73   }
74 
75   // swap compared
76   if (parent->canSwapCompared()) {
77     action = addAction(tr("Swap Compared Images"));
78     ret    = ret &&
79           parent->connect(action, SIGNAL(triggered()), SLOT(swapCompared()));
80   }
81 
82   if (!isEditingLevel) {
83     // fit camera
84     action = commandManager->createAction(V_ZoomFit, this);
85     addAction(action);
86     ret = ret &&
87           parent->connect(action, SIGNAL(triggered()), SLOT(fitToCamera()));
88   }
89 
90   QMenu *flipViewMenu = addMenu(tr("Flip View"));
91 
92   // flip horizontally
93   action = commandManager->createAction(V_FlipX, this);
94   flipViewMenu->addAction(action);
95   ret = ret && parent->connect(action, SIGNAL(triggered()), SLOT(flipX()));
96 
97   // flip vertically
98   action = commandManager->createAction(V_FlipY, this);
99   flipViewMenu->addAction(action);
100   ret = ret && parent->connect(action, SIGNAL(triggered()), SLOT(flipY()));
101 
102   QMenu *resetViewMenu = addMenu(tr("Reset View"));
103 
104   // reset
105   action = commandManager->createAction(V_ViewReset, this);
106   resetViewMenu->addAction(action);
107   ret = ret &&
108         parent->connect(action, SIGNAL(triggered()), SLOT(resetSceneViewer()));
109 
110   // reset zoom
111   action = commandManager->createAction(V_ZoomReset, this);
112   resetViewMenu->addAction(action);
113   ret = ret && parent->connect(action, SIGNAL(triggered()), SLOT(resetZoom()));
114 
115   // reset rotation
116   action = commandManager->createAction(V_RotateReset, this);
117   resetViewMenu->addAction(action);
118   ret = ret &&
119         parent->connect(action, SIGNAL(triggered()), SLOT(resetRotation()));
120 
121   // reset position
122   action = commandManager->createAction(V_PositionReset, this);
123   resetViewMenu->addAction(action);
124   ret = ret &&
125         parent->connect(action, SIGNAL(triggered()), SLOT(resetPosition()));
126 
127   // actual pixel size
128   action = commandManager->createAction(V_ActualPixelSize, this);
129   addAction(action);
130   ret = ret && parent->connect(action, SIGNAL(triggered()),
131                                SLOT(setActualPixelSize()));
132 
133   // onion skin
134   if (Preferences::instance()->isOnionSkinEnabled() &&
135       !parent->isPreviewEnabled())
136     OnioniSkinMaskGUI::addOnionSkinCommand(this);
137 
138   if (tool->getTargetType() & TTool::VectorImage) {
139     auto addOptionAction = [](const QString &label, const int data,
140                               const int currentData, QMenu *menu,
141                               QActionGroup *group) {
142       QAction *action = menu->addAction(label);
143       action->setData(data);
144       action->setCheckable(true);
145       action->setChecked(data == currentData);
146       group->addAction(action);
147     };
148     QMenu *guidedDrawingMenu = addMenu(tr("Vector Guided Drawing"));
149     int guidedDrawingStatus  = Preferences::instance()->getGuidedDrawingType();
150 
151     QActionGroup *guidedDrawingGroup = new QActionGroup(this);
152     addOptionAction(tr("Off"), 0, guidedDrawingStatus, guidedDrawingMenu,
153                     guidedDrawingGroup);
154     addOptionAction(tr("Closest Drawing"), 1, guidedDrawingStatus,
155                     guidedDrawingMenu, guidedDrawingGroup);
156     addOptionAction(tr("Farthest Drawing"), 2, guidedDrawingStatus,
157                     guidedDrawingMenu, guidedDrawingGroup);
158     addOptionAction(tr("All Drawings"), 3, guidedDrawingStatus,
159                     guidedDrawingMenu, guidedDrawingGroup);
160     ret =
161         ret && parent->connect(guidedDrawingGroup, SIGNAL(triggered(QAction *)),
162                                this, SLOT(setGuidedDrawingType(QAction *)));
163 
164     guidedDrawingMenu->addSeparator();
165     bool enableOption = guidedDrawingStatus == 1 || guidedDrawingStatus == 2;
166     action            = guidedDrawingMenu->addAction(tr("Auto Inbetween"));
167     action->setCheckable(true);
168     action->setChecked(Preferences::instance()->getGuidedAutoInbetween());
169     action->setEnabled(enableOption);
170     ret = ret && parent->connect(action, SIGNAL(triggered()), this,
171                                  SLOT(setGuidedAutoInbetween()));
172     guidedDrawingMenu->addSeparator();
173     int guidedInterpolation = Preferences::instance()->getGuidedInterpolation();
174     QActionGroup *interpolationGroup = new QActionGroup(this);
175     addOptionAction(tr("Linear Interpolation"), 1, guidedInterpolation,
176                     guidedDrawingMenu, interpolationGroup);
177     addOptionAction(tr("Ease In Interpolation"), 2, guidedInterpolation,
178                     guidedDrawingMenu, interpolationGroup);
179     addOptionAction(tr("Ease Out Interpolation"), 3, guidedInterpolation,
180                     guidedDrawingMenu, interpolationGroup);
181     addOptionAction(tr("Ease In/Out Interpolation"), 4, guidedInterpolation,
182                     guidedDrawingMenu, interpolationGroup);
183     ret = ret &&
184           parent->connect(interpolationGroup, SIGNAL(triggered(QAction *)),
185                           this, SLOT(setGuidedInterpolationState(QAction *)));
186     interpolationGroup->setEnabled(enableOption);
187     /*
188         guidedDrawingMenu->addSeparator();
189         action =
190        CommandManager::instance()->getAction(MI_SelectPrevGuideStroke);
191         action->setEnabled(enableOption);
192         guidedDrawingMenu->addAction(action);
193         action =
194        CommandManager::instance()->getAction(MI_SelectNextGuideStroke);
195         action->setEnabled(enableOption);
196         guidedDrawingMenu->addAction(action);
197         action =
198        CommandManager::instance()->getAction(MI_SelectBothGuideStrokes);
199         action->setEnabled(enableOption);
200         guidedDrawingMenu->addAction(action);
201         action =
202        CommandManager::instance()->getAction(MI_SelectGuideStrokeReset);
203         action->setEnabled(true);
204         guidedDrawingMenu->addAction(action);
205         guidedDrawingMenu->addSeparator();
206         action = CommandManager::instance()->getAction(MI_TweenGuideStrokes);
207         action->setEnabled(enableOption);
208         guidedDrawingMenu->addAction(action);
209         action =
210             CommandManager::instance()->getAction(MI_TweenGuideStrokeToSelected);
211         action->setEnabled(enableOption);
212         guidedDrawingMenu->addAction(action);
213         action =
214        CommandManager::instance()->getAction(MI_SelectGuidesAndTweenMode);
215         action->setEnabled(enableOption);
216         guidedDrawingMenu->addAction(action);
217     */
218     // Zero Thick
219     if (!parent->isPreviewEnabled())
220       ZeroThickToggleGui::addZeroThickCommand(this);
221   }
222   // Brush size outline
223   CursorOutlineToggleGui::addCursorOutlineCommand(this);
224 
225   // preview
226   if (parent->isPreviewEnabled()) {
227     addSeparator();
228 
229     // save previewed frames
230     action = addAction(tr("Save Previewed Frames"));
231     action->setShortcut(QKeySequence(
232         CommandManager::instance()->getKeyFromId(MI_SavePreviewedFrames)));
233     ret = ret && parent->connect(action, SIGNAL(triggered()), this,
234                                  SLOT(savePreviewedFrames()));
235 
236     // regenerate preview
237     action = addAction(tr("Regenerate Preview"));
238     action->setShortcut(QKeySequence(
239         CommandManager::instance()->getKeyFromId(MI_RegeneratePreview)));
240     ret = ret && parent->connect(action, SIGNAL(triggered()),
241                                  SLOT(regeneratePreview()));
242 
243     // regenerate frame preview
244     action = addAction(tr("Regenerate Frame Preview"));
245     action->setShortcut(QKeySequence(
246         CommandManager::instance()->getKeyFromId(MI_RegenerateFramePr)));
247     ret = ret && parent->connect(action, SIGNAL(triggered()),
248                                  SLOT(regeneratePreviewFrame()));
249   }
250 
251   assert(ret);
252 }
253 
~SceneViewerContextMenu()254 SceneViewerContextMenu::~SceneViewerContextMenu() {}
255 
addEnterGroupCommands(const TPointD & pos)256 void SceneViewerContextMenu::addEnterGroupCommands(const TPointD &pos) {
257   bool ret         = true;
258   TVectorImageP vi = (TVectorImageP)TTool::getImage(false);
259   if (!vi) return;
260 
261   if (vi->isInsideGroup() > 0) {
262     addAction(CommandManager::instance()->getAction(MI_ExitGroup));
263   }
264 
265   StrokeSelection *ss =
266       dynamic_cast<StrokeSelection *>(TSelection::getCurrent());
267   if (!ss) return;
268 
269   for (int i = 0; i < vi->getStrokeCount(); i++)
270     if (ss->isSelected(i) && vi->canEnterGroup(i)) {
271       m_groupIndexToBeEntered = i;
272       addAction(CommandManager::instance()->getAction(MI_EnterGroup));
273       return;
274     }
275 
276   assert(ret);
277 }
278 
getName(TStageObject * obj)279 static QString getName(TStageObject *obj) {
280   return QString::fromStdString(obj->getFullName());
281 }
282 
addShowHideCommand(QMenu * menu,TXshColumn * column)283 void SceneViewerContextMenu::addShowHideCommand(QMenu *menu,
284                                                 TXshColumn *column) {
285   bool isHidden = !column->isCamstandVisible();
286   TXsheet *xsh  = TApp::instance()->getCurrentXsheet()->getXsheet();
287   TStageObject *stageObject =
288       xsh->getStageObject(TStageObjectId::ColumnId(column->getIndex()));
289   QString text = isHidden ? tr("Show %1").arg(getName(stageObject))
290                           : tr("Hide %1").arg(getName(stageObject));
291   QAction *action = new QAction(text, this);
292   action->setData(column->getIndex());
293   connect(action, SIGNAL(triggered()), this, SLOT(onShowHide()));
294   menu->addAction(action);
295 }
296 
addSelectCommand(QMenu * menu,const TStageObjectId & id)297 void SceneViewerContextMenu::addSelectCommand(QMenu *menu,
298                                               const TStageObjectId &id) {
299   TXsheet *xsh              = TApp::instance()->getCurrentXsheet()->getXsheet();
300   TStageObject *stageObject = xsh->getStageObject(id);
301   if (!stageObject) return;
302   QString text = (id.isTable()) ? tr("Table") : getName(stageObject);
303   if (menu == this) text = tr("Select %1").arg(text);
304   QAction *action = new QAction(text, this);
305   action->setData(id.getCode());
306   connect(action, SIGNAL(triggered()), this, SLOT(onSetCurrent()));
307   menu->addAction(action);
308 }
309 
addLevelCommands(std::vector<int> & indices)310 void SceneViewerContextMenu::addLevelCommands(std::vector<int> &indices) {
311   addSeparator();
312   TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
313   TStageObjectId currentId =
314       TApp::instance()->getCurrentObject()->getObjectId();
315 
316   /*- Xsheet内の、空でないColumnを登録 -*/
317   std::vector<TXshColumn *> columns;
318   for (int i = 0; i < (int)indices.size(); i++) {
319     if (xsh->isColumnEmpty(indices[i])) continue;
320     TXshColumn *column = xsh->getColumn(indices[i]);
321     if (column) {
322       columns.push_back(column);
323     }
324   }
325 
326   if (!columns.empty()) {
327     // show/hide
328     if (columns.size() > 1) {
329       QMenu *subMenu = addMenu(tr("Show / Hide"));
330       for (int i = 0; i < (int)columns.size(); i++)
331         addShowHideCommand(subMenu, columns[i]);
332     } else
333       addShowHideCommand(this, columns[0]);
334     addSeparator();
335   }
336 
337   // selection
338   /*
339     if(selectableColumns.size()==1)
340     {
341       addSelectCommand(this,
342     TStageObjectId::ColumnId(selectableColumns[0]->getIndex()));
343     }
344     else
345     */
346 
347   /*-- Scene内の全Objectを選択可能にする --*/
348   TStageObjectId id;
349   QMenu *cameraMenu = addMenu(tr("Select Camera"));
350   QMenu *pegbarMenu = addMenu(tr("Select Pegbar"));
351   QMenu *columnMenu = addMenu(tr("Select Column"));
352 
353   bool flag = false;
354 
355   for (int i = 0; i < xsh->getStageObjectTree()->getStageObjectCount(); i++) {
356     id = xsh->getStageObjectTree()->getStageObject(i)->getId();
357     if (id.isColumn()) {
358       int columnIndex = id.getIndex();
359       if (xsh->isColumnEmpty(columnIndex))
360         continue;
361       else {
362         addSelectCommand(columnMenu, id);
363         flag = true;
364       }
365     } else if (id.isTable())
366       addSelectCommand(this, id);
367     else if (id.isCamera())
368       addSelectCommand(cameraMenu, id);
369     else if (id.isPegbar())
370       addSelectCommand(pegbarMenu, id);
371   }
372 
373   /*- カラムがひとつも無かったらDisable -*/
374   if (!flag) columnMenu->setEnabled(false);
375 }
376 
377 //-----------------------------------------------------------------------------
378 
enterVectorImageGroup()379 void SceneViewerContextMenu::enterVectorImageGroup() {
380   if (m_groupIndexToBeEntered == -1) return;
381 
382   TVectorImageP vi =
383       (TVectorImageP)TTool::getImage(false);  // getCurrentImage();
384   if (!vi) return;
385   vi->enterGroup(m_groupIndexToBeEntered);
386   TSelection *selection = TSelection::getCurrent();
387   if (selection) selection->selectNone();
388   m_viewer->update();
389 }
390 
391 //-----------------------------------------------------------------------------
392 
exitVectorImageGroup()393 void SceneViewerContextMenu::exitVectorImageGroup() {
394   TVectorImageP vi =
395       (TVectorImageP)TTool::getImage(false);  // getCurrentImage();
396   if (!vi) return;
397   vi->exitGroup();
398   m_viewer->update();
399 }
400 
401 //-----------------------------------------------------------------------------
402 
onShowHide()403 void SceneViewerContextMenu::onShowHide() {
404   int columnIndex = qobject_cast<QAction *>(sender())->data().toInt();
405   TXsheetHandle *xsheetHandle = TApp::instance()->getCurrentXsheet();
406   TXshColumn *column = xsheetHandle->getXsheet()->getColumn(columnIndex);
407   if (column) {
408     column->setCamstandVisible(!column->isCamstandVisible());
409     xsheetHandle->notifyXsheetChanged();
410   }
411 }
412 //-----------------------------------------------------------------------------
413 
onSetCurrent()414 void SceneViewerContextMenu::onSetCurrent() {
415   TStageObjectId id;
416   id.setCode(qobject_cast<QAction *>(sender())->data().toUInt());
417   TApp *app = TApp::instance();
418   if (id.isColumn()) {
419     app->getCurrentColumn()->setColumnIndex(id.getIndex());
420     app->getCurrentObject()->setObjectId(id);
421   } else {
422     app->getCurrentObject()->setObjectId(id);
423     app->getCurrentTool()->setTool(T_Edit);
424   }
425 }
426 
427 //-----------------------------------------------------------------------------
setGuidedDrawingType(QAction * action)428 void SceneViewerContextMenu::setGuidedDrawingType(QAction *action) {
429   Preferences::instance()->setValue(guidedDrawingType, action->data().toInt());
430 
431   QAction *guidedDrawingAction =
432       CommandManager::instance()->getAction(MI_VectorGuidedDrawing);
433   if (guidedDrawingAction)
434     guidedDrawingAction->setChecked(
435         Preferences::instance()->isGuidedDrawingEnabled());
436 
437   TApp::instance()->getCurrentScene()->notifyPreferenceChanged(
438       "GuidedDrawingFrame");
439 }
440 
441 //-----------------------------------------------------------------------------
setGuidedAutoInbetween()442 void SceneViewerContextMenu::setGuidedAutoInbetween() {
443   Preferences::instance()->setValue(
444       guidedAutoInbetween, !Preferences::instance()->getGuidedAutoInbetween());
445   TApp::instance()->getCurrentScene()->notifyPreferenceChanged(
446       "GuidedDrawingAutoInbetween");
447 }
448 
449 //-----------------------------------------------------------------------------
setGuidedInterpolationState(QAction * action)450 void SceneViewerContextMenu::setGuidedInterpolationState(QAction *action) {
451   Preferences::instance()->setValue(guidedInterpolationType,
452                                     action->data().toInt());
453   TApp::instance()->getCurrentScene()->notifyPreferenceChanged(
454       "GuidedDrawingInterpolation");
455 }
456 
457 //-----------------------------------------------------------------------------
458 
savePreviewedFrames()459 void SceneViewerContextMenu::savePreviewedFrames() {
460   Previewer::instance(m_viewer->getPreviewMode() ==
461                       SceneViewer::SUBCAMERA_PREVIEW)
462       ->saveRenderedFrames();
463 }
464 
465 //-----------------------------------------------------------------------------
466 class ZeroThickToggle : public MenuItemHandler {
467 public:
ZeroThickToggle()468   ZeroThickToggle() : MenuItemHandler(MI_ZeroThick) {}
execute()469   void execute() {
470     QAction *action = CommandManager::instance()->getAction(MI_ZeroThick);
471     if (!action) return;
472     bool checked = action->isChecked();
473     enableZeroThick(checked);
474   }
475 
enableZeroThick(bool enable=true)476   static void enableZeroThick(bool enable = true) {
477     Preferences::instance()->setValue(show0ThickLines, enable);
478     TApp::instance()->getCurrentScene()->notifySceneChanged();
479   }
480 } ZeroThickToggle;
481 
addZeroThickCommand(QMenu * menu)482 void ZeroThickToggleGui::addZeroThickCommand(QMenu *menu) {
483   static ZeroThickToggleHandler switcher;
484   if (Preferences::instance()->getShow0ThickLines()) {
485     QAction *hideZeroThick =
486         menu->addAction(QString(QObject::tr("Hide Zero Thickness Lines")));
487     menu->connect(hideZeroThick, SIGNAL(triggered()), &switcher,
488                   SLOT(deactivate()));
489   } else {
490     QAction *showZeroThick =
491         menu->addAction(QString(QObject::tr("Show Zero Thickness Lines")));
492     menu->connect(showZeroThick, SIGNAL(triggered()), &switcher,
493                   SLOT(activate()));
494   }
495 }
496 
activate()497 void ZeroThickToggleGui::ZeroThickToggleHandler::activate() {
498   ZeroThickToggle::enableZeroThick(true);
499 }
500 
deactivate()501 void ZeroThickToggleGui::ZeroThickToggleHandler::deactivate() {
502   ZeroThickToggle::enableZeroThick(false);
503 }
504 
505 class CursorOutlineToggle : public MenuItemHandler {
506 public:
CursorOutlineToggle()507   CursorOutlineToggle() : MenuItemHandler(MI_CursorOutline) {}
execute()508   void execute() {
509     QAction *action = CommandManager::instance()->getAction(MI_CursorOutline);
510     if (!action) return;
511     bool checked = action->isChecked();
512     enableCursorOutline(checked);
513   }
514 
enableCursorOutline(bool enable=true)515   static void enableCursorOutline(bool enable = true) {
516     Preferences::instance()->setValue(cursorOutlineEnabled, enable);
517   }
518 } CursorOutlineToggle;
519 
addCursorOutlineCommand(QMenu * menu)520 void CursorOutlineToggleGui::addCursorOutlineCommand(QMenu *menu) {
521   static CursorOutlineToggleHandler switcher;
522   if (Preferences::instance()->isCursorOutlineEnabled()) {
523     QAction *hideCursorOutline =
524         menu->addAction(QString(QObject::tr("Hide cursor size outline")));
525     menu->connect(hideCursorOutline, SIGNAL(triggered()), &switcher,
526                   SLOT(deactivate()));
527   } else {
528     QAction *showCursorOutline =
529         menu->addAction(QString(QObject::tr("Show cursor size outline")));
530     menu->connect(showCursorOutline, SIGNAL(triggered()), &switcher,
531                   SLOT(activate()));
532   }
533 }
534 
activate()535 void CursorOutlineToggleGui::CursorOutlineToggleHandler::activate() {
536   CursorOutlineToggle::enableCursorOutline(true);
537 }
538 
deactivate()539 void CursorOutlineToggleGui::CursorOutlineToggleHandler::deactivate() {
540   CursorOutlineToggle::enableCursorOutline(false);
541 }
542