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