1 // Copyright (C) 2012-2019 The VPaint Developers.
2 // See the COPYRIGHT file at the top-level directory of this distribution
3 // and at https://github.com/dalboris/vpaint/blob/master/COPYRIGHT
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16
17 #include "Global.h"
18
19 #include "DevSettings.h"
20 #include "Scene.h"
21 #include "View.h"
22 #include "MainWindow.h"
23 #include "SettingsDialog.h"
24 #include "DevSettings.h"
25 #include "ColorSelector.h"
26 #include "Timeline.h"
27
28 #include <QDebug>
29 #include <QApplication>
30 #include <QKeyEvent>
31 #include <QHBoxLayout>
32 #include <QPushButton>
33 #include <QToolBar>
34 #include <QMenu>
35 #include <QAction>
36 #include <QSettings>
37 #include <QStatusBar>
38 #include <QDir>
39
40 // -------- Initialization --------
41
42 Global * global_ = 0;
global()43 Global * global() { return global_; }
initialize(MainWindow * w)44 void Global::initialize(MainWindow * w) { global_ = new Global(w); }
45
Global(MainWindow * w)46 Global::Global(MainWindow * w) :
47 toolMode_(SELECT),
48 toolBar_(0),
49 toolModeToolBar_(0),
50 isScalingCorner_(false),
51 isScalingEdge_(false),
52 isRotating_(false),
53 isDragAndDropping_(false),
54 isDraggingPivot_(false),
55 xSceneCursorPos_(0),
56 ySceneCursorPos_(0),
57 currentDisplayMode_(ILLUSTRATION),
58 switchToDisplayMode_(OUTLINE),
59 otherDisplayMode_(ILLUSTRATION_OUTLINE),
60 mainWindow_(w),
61 preferences_(),
62 preferencesDialog_(0),
63 settings_(0),
64 documentDir_(QDir::home())
65 {
66 // Color selectors
67 currentColor_ = new ColorSelector();
68 currentColor_->setToolTip(tr("Current color (C)"));
69 currentColor_->setStatusTip(tr("Click to open the color selector"));
70
71 snapThreshold_ = new SpinBox();
72 snapThreshold_->setCaption(tr(" snap threshold "));
73
74 sculptRadius_ = new SpinBox();
75 sculptRadius_->setCaption(tr(" sculpt radius "));
76
77 // Event filter
78 QCoreApplication::instance()->installEventFilter(this);
79
80 // Status bar help
81 statusBarHelp_ = new QLabel();
82 statusBarHelp_->setText("Find help here.");
83 w->statusBar()->addWidget(statusBarHelp_);
84 connect(this, SIGNAL(keyboardModifiersChanged()), this, SLOT(updateStatusBarHelp()));
85
86 }
87
deleteIsolatedVertices()88 bool Global::deleteIsolatedVertices()
89 {
90 return true;
91 }
92
deleteShortEdges()93 bool Global::deleteShortEdges()
94 {
95 return true;
96 }
97
keyboardModifiers()98 Qt::KeyboardModifiers Global::keyboardModifiers()
99 {
100 return keyboardModifiers_;
101 }
102
updateModifiers()103 void Global::updateModifiers()
104 {
105 Qt::KeyboardModifiers keyboardModifiers = QGuiApplication::queryKeyboardModifiers();
106 if(keyboardModifiers_ != keyboardModifiers)
107 {
108 keyboardModifiers_ = keyboardModifiers;
109 emit keyboardModifiersChanged();
110 }
111 }
112
eventFilter(QObject *,QEvent * event)113 bool Global::eventFilter(QObject * /*watched*/, QEvent * event)
114 {
115 // Every single event delivered by Qt go through this method first before
116 // going to its target object, so keep it as lightweight as possible
117
118 // It is used as a convenient way to fix a few event behaviours that were
119 // not quite right out of the box.
120
121 // --------------------- Detect modifier key presses --------------
122
123 // Detect modifier key presses (Shift, Ctrl, Alt, etc.) and update application
124 // state accordingly (e.g., indicate which modifiers are pressed in the status bar, or
125 // redraw the scene, since highlighting color depends on which modifiers are pressed)
126
127 // If a modifier is pressed or released, update the modifier state, and emit a signal
128 // if this state has changed
129 // If a modifier is pressed or released, update the modifier state, and emit a signal
130 // if this state has changed
131 if(event->type() == QEvent::KeyPress ||
132 event->type() == QEvent::KeyRelease)
133 {
134 QKeyEvent * keyEvent = static_cast<QKeyEvent *>(event);
135 if(keyEvent)
136 {
137 // Workaround for Mac delete key
138 // This is needed because of a bug in QT 5 that has not been resolved as of 5.5.0
139 #ifdef Q_OS_MAC
140 if(keyEvent->key() == Qt::Key_Backspace)
141 {
142 scene()->smartDelete();
143 }
144 #endif
145 if(keyEvent->key() == Qt::Key_Shift ||
146 keyEvent->key() == Qt::Key_Alt ||
147 keyEvent->key() == Qt::Key_Meta ||
148 keyEvent->key() == Qt::Key_AltGr ||
149 keyEvent->key() == Qt::Key_Control)
150 {
151 updateModifiers();
152 }
153 }
154
155 // Continue normal processing of the event
156 return false;
157 }
158 else if(event->type() == QEvent::FocusIn )
159 {
160 updateModifiers();
161
162 // Continue normal processing of the event
163 return false;
164 }
165
166 // --------------------- Resolve shortcut overloads --------------
167
168 // Resolve shortcut overloads
169 else if(event->type() == QEvent::Shortcut)
170 {
171 QShortcutEvent * shortcutEvent = static_cast<QShortcutEvent *>(event);
172
173 if(shortcutEvent->isAmbiguous())
174 {
175 QKeySequence key = shortcutEvent->key();
176 resolveAmbiguousShortcuts(key);
177
178 // Stop processing of the event
179 return true;
180
181 }
182 else
183 {
184 // Continue normal processing of the event
185 return false;
186 }
187 }
188
189 // --------------------- Keep standard behaviour --------------
190
191 // Continue normal processing of the event
192 return false;
193 }
194
resolveAmbiguousShortcuts(const QKeySequence & key)195 void Global::resolveAmbiguousShortcuts(const QKeySequence & key)
196 {
197 qDebug() << "Ambiguous shortcut:" << key;
198 }
199
sceneCursorPos() const200 Eigen::Vector2d Global::sceneCursorPos() const
201 {
202 return Eigen::Vector2d(xSceneCursorPos_, ySceneCursorPos_);
203 }
204
setSceneCursorPos(const Eigen::Vector2d & pos)205 void Global::setSceneCursorPos(const Eigen::Vector2d & pos)
206 {
207 xSceneCursorPos_ = pos[0];
208 ySceneCursorPos_ = pos[1];
209 }
210
toolModeToolBar() const211 QToolBar * Global::toolModeToolBar() const
212 {
213 return toolModeToolBar_;
214 }
215
toolBar() const216 QToolBar * Global::toolBar() const
217 {
218 return toolBar_;
219 }
220
createToolBars()221 void Global::createToolBars()
222 {
223 // ----- Tool modes -----
224
225 // Create toolbar
226 toolBar_ = mainWindow()->addToolBar(tr("Toolbar"));
227 toolBar_->setOrientation(Qt::Vertical);
228 toolBar_->setMovable(false);
229 mainWindow()->addToolBar(Qt::LeftToolBarArea, toolBar_);
230
231 // Set toolbar size
232 int iconWidth = 32;
233 toolBar_->setIconSize(QSize(iconWidth,iconWidth));
234 currentColor_->setIconSize(QSize(iconWidth,iconWidth));
235 currentColor_->updateIcon();
236
237 // Create actions (exclusive checkable)
238 QActionGroup * actionGroup = new QActionGroup(this);
239 for(int i=0; i<NUMBER_OF_TOOL_MODES; i++)
240 {
241 toolModeActions[i] = new ToolModeAction(static_cast<ToolMode>(i), actionGroup);
242 toolModeActions[i]->setCheckable(true);
243 toolModeActions[i]->setShortcutContext(Qt::ApplicationShortcut);
244 toolBar_->addAction(toolModeActions[i]);
245 connect(toolModeActions[i], SIGNAL(triggered(Global::ToolMode)),
246 this, SLOT(setToolMode(Global::ToolMode)));
247 }
248
249 // Select
250 toolModeActions[SELECT]->setText(tr("Select and move (F1)"));
251 toolModeActions[SELECT]->setIcon(QIcon(":/images/select.png"));
252 toolModeActions[SELECT]->setStatusTip(tr("Select objects, move objects, glue objects together, and split curves."));
253 toolModeActions[SELECT]->setShortcut(QKeySequence(Qt::Key_F1));
254
255 // Sketch
256 toolModeActions[SKETCH]->setText(tr("Sketch (F2)"));
257 toolModeActions[SKETCH]->setIcon(QIcon(":/images/sketch.png"));
258 toolModeActions[SKETCH]->setStatusTip(tr("Sketch curves."));
259 toolModeActions[SKETCH]->setShortcut(QKeySequence(Qt::Key_F2));
260
261 // Paint
262 toolModeActions[PAINT]->setText(tr("Paint (F3)"));
263 toolModeActions[PAINT]->setIcon(QIcon(":/images/paint.png"));
264 toolModeActions[PAINT]->setStatusTip(tr("Paint an empty space or an existing object."));
265 toolModeActions[PAINT]->setShortcut(QKeySequence(Qt::Key_F3));
266
267 // Sculpt
268 toolModeActions[SCULPT]->setText(tr("Sculpt (F4)"));
269 toolModeActions[SCULPT]->setIcon(QIcon(":/images/sculpt.png"));
270 toolModeActions[SCULPT]->setStatusTip(tr("Sculpt curves."));
271 toolModeActions[SCULPT]->setShortcut(QKeySequence(Qt::Key_F4));
272
273 // ----- Color selectors -----
274
275 // Colors
276 colorSelectorAction_ = toolBar_->addWidget(currentColor_);
277 colorSelectorAction_->setText(tr("Color"));
278 colorSelectorAction_->setToolTip(tr("Color (C)"));
279 colorSelectorAction_->setStatusTip(tr("Click to open the color selector"));
280 colorSelectorAction_->setShortcut(QKeySequence(Qt::Key_C));
281 colorSelectorAction_->setShortcutContext(Qt::ApplicationShortcut);
282 connect(colorSelectorAction_, SIGNAL(triggered()), currentColor_, SLOT(click()));
283
284 // ----- Tool Options -----
285
286 toolModeToolBar_ = new QToolBar("Action Bar");
287 toolModeToolBar_->setIconSize(QSize(200,iconWidth));
288 toolModeToolBar_->setMovable(false);
289 mainWindow()->addToolBar(toolModeToolBar_);
290
291 // --------------------- Color ------------------------
292
293 actionChangeColor_ = new QAction(this);
294 actionChangeColor_->setText(tr("Change color"));
295 actionChangeColor_->setIcon(QIcon(":/images/change-color.png"));
296 actionChangeColor_->setStatusTip(tr("Change the color of the selected cells"));
297 //actionChangeColor_->setShortcut(QKeySequence(Qt::Key_C));
298 actionChangeColor_->setShortcutContext(Qt::ApplicationShortcut);
299 mainWindow()->addAction(actionChangeColor_);
300 connect(actionChangeColor_, SIGNAL(triggered()), mainWindow()->scene(), SLOT(changeColor()));
301
302 // --------------------- Edges ------------------------
303
304 actionChangeEdgeWidth_ = new QAction(this);
305 actionChangeEdgeWidth_->setText(tr("Change edge width (W)"));
306 actionChangeEdgeWidth_->setIcon(QIcon(":/images/change-width.png"));
307 actionChangeEdgeWidth_->setStatusTip(tr("Change the width of the selected edges"));
308 actionChangeEdgeWidth_->setShortcut(QKeySequence(Qt::Key_W));
309 actionChangeEdgeWidth_->setShortcutContext(Qt::ApplicationShortcut);
310 mainWindow()->addAction(actionChangeEdgeWidth_);
311 connect(actionChangeEdgeWidth_, SIGNAL(triggered()), mainWindow()->scene(), SLOT(changeEdgeWidth()));
312
313
314 // --------------------- Faces ------------------------
315
316 actionCreateFace_ = new QAction(this);
317 actionCreateFace_->setText(tr("Create Face (F)"));
318 actionCreateFace_->setIcon(QIcon(":/images/create-face.png"));
319 actionCreateFace_->setStatusTip(tr("Create a face whose boundary is the selected edges"));
320 actionCreateFace_->setShortcut(QKeySequence(Qt::Key_F));
321 actionCreateFace_->setShortcutContext(Qt::ApplicationShortcut);
322 mainWindow()->addAction(actionCreateFace_);
323 connect(actionCreateFace_, SIGNAL(triggered()), mainWindow()->scene(), SLOT(createFace()));
324
325 actionAddCycles_ = new QAction(this);
326 actionAddCycles_->setText(tr("Add Holes (H)"));
327 actionAddCycles_->setIcon(QIcon(":/images/add-cycles.png"));
328 actionAddCycles_->setStatusTip(tr("Add holes to the selected face, whose boundaries are the selected edges"));
329 actionAddCycles_->setShortcut(QKeySequence(Qt::Key_H));
330 actionAddCycles_->setShortcutContext(Qt::ApplicationShortcut);
331 mainWindow()->addAction(actionAddCycles_);
332 connect(actionAddCycles_, SIGNAL(triggered()), mainWindow()->scene(), SLOT(addCyclesToFace()));
333
334 actionRemoveCycles_ = new QAction(this);
335 actionRemoveCycles_->setText(tr("Remove Holes (Ctrl+H)"));
336 actionRemoveCycles_->setIcon(QIcon(":/images/remove-cycles.png"));
337 actionRemoveCycles_->setStatusTip(tr("Remove holes from the selected face, whose boundaries are the selected edges"));
338 actionRemoveCycles_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_H));
339 actionRemoveCycles_->setShortcutContext(Qt::ApplicationShortcut);
340 mainWindow()->addAction(actionRemoveCycles_);
341 connect(actionRemoveCycles_, SIGNAL(triggered()), mainWindow()->scene(), SLOT(removeCyclesFromFace()));
342
343 // --------------------- Topological operations ------------------------
344
345 actionGlue_ = new QAction(this);
346 actionGlue_->setText(tr("Glue"));
347 actionGlue_->setToolTip(tr("Glue (G)"));
348 actionGlue_->setIcon(QIcon(":/images/glue.png"));
349 actionGlue_->setStatusTip(tr("Glue two endpoints or two curves together"));
350 actionGlue_->setShortcut(QKeySequence(Qt::Key_G));
351 actionGlue_->setShortcutContext(Qt::ApplicationShortcut);
352 mainWindow()->addAction(actionGlue_);
353 connect(actionGlue_, SIGNAL(triggered()), mainWindow()->scene(), SLOT(glue()));
354
355 actionUnglue_ = new QAction(this);
356 actionUnglue_->setText(tr("Explode"));
357 actionUnglue_->setToolTip(tr("Explode (E)"));
358 actionUnglue_->setIcon(QIcon(":/images/unglue.png"));
359 actionUnglue_->setStatusTip(tr("Duplicate the selected objects to disconnect adjacent curves and surfaces"));
360 //actionUnglue_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_G));
361 actionUnglue_->setShortcut(QKeySequence(Qt::Key_E));
362 actionUnglue_->setShortcutContext(Qt::ApplicationShortcut);
363 mainWindow()->addAction(actionUnglue_);
364 connect(actionUnglue_, SIGNAL(triggered()), mainWindow()->scene(), SLOT(unglue()));
365
366 actionUncut_ = new QAction(this);
367 actionUncut_->setText(tr("Simplify"));
368 actionUncut_->setToolTip(tr("Simplify (Backspace)"));
369 actionUncut_->setIcon(QIcon(":/images/simplify.png"));
370 actionUncut_->setStatusTip(tr("Simplify the selected objects, by merging curves and surfaces together"));
371 actionUncut_->setShortcut(QKeySequence(Qt::Key_Backspace));
372 actionUncut_->setShortcutContext(Qt::ApplicationShortcut);
373 mainWindow()->addAction(actionUncut_);
374 connect(actionUncut_, SIGNAL(triggered()), mainWindow()->scene(), SLOT(uncut()));
375
376 // Desired icon size
377 double sideLength = 40;
378
379 // --------------------- Shared actions/options ------------------------
380
381 // None
382
383 // --------------------- Select options ------------------------
384
385 toolModeToolBar_->addAction(actionChangeColor_);
386 toolModeToolBar_->addAction(actionChangeEdgeWidth_);
387 separatorSelect1_ = toolModeToolBar_->addSeparator();
388 toolModeToolBar_->addAction(actionCreateFace_);
389 toolModeToolBar_->addAction(actionAddCycles_);
390 toolModeToolBar_->addAction(actionRemoveCycles_);
391 separatorSelect2_ = toolModeToolBar_->addSeparator();
392
393 toolModeToolBar_->widgetForAction(actionChangeColor_)->setFixedSize(sideLength,sideLength);
394 toolModeToolBar_->widgetForAction(actionChangeEdgeWidth_)->setFixedSize(sideLength,sideLength);
395 toolModeToolBar_->widgetForAction(actionCreateFace_)->setFixedSize(sideLength,sideLength);
396 toolModeToolBar_->widgetForAction(actionAddCycles_)->setFixedSize(sideLength,sideLength);
397 toolModeToolBar_->widgetForAction(actionRemoveCycles_)->setFixedSize(sideLength,sideLength);
398
399 toolModeToolBar_->addAction(actionGlue_);
400 toolModeToolBar_->addAction(actionUnglue_);
401 toolModeToolBar_->addAction(actionUncut_);
402 toolModeToolBar_->widgetForAction(actionGlue_)->setFixedSize(sideLength+20,sideLength);
403 toolModeToolBar_->widgetForAction(actionUnglue_)->setFixedSize(sideLength+20,sideLength);
404 toolModeToolBar_->widgetForAction(actionUncut_)->setFixedSize(sideLength+20,sideLength);
405
406
407 // --------------------- Sketch options ------------------------
408
409 // Tablet pressure
410 actionUseTabletPressure_ = new QAction(this);
411 actionUseTabletPressure_->setCheckable(true);
412 actionUseTabletPressure_->setChecked(true);
413 toolModeToolBar_->addAction(actionUseTabletPressure_);
414 toolModeToolBar_->widgetForAction(actionUseTabletPressure_)->setFixedSize(sideLength,sideLength);
415 actionUseTabletPressure_->setText(tr("Toggle stylus pressure"));
416 actionUseTabletPressure_->setIcon(QIcon(":/images/pressure.png"));
417 actionUseTabletPressure_->setStatusTip(tr("Enable or disable stylus pressure (only for users with a pen tablet)"));
418 //actionUseTabletPressure_->setShortcut(QKeySequence(Qt::Key_Backspace));
419 actionUseTabletPressure_->setShortcutContext(Qt::ApplicationShortcut);
420 mainWindow()->addAction(actionUseTabletPressure_);
421 connect(actionUseTabletPressure_, SIGNAL(triggered()), this, SLOT(toggleStylusPressure()));
422
423 // Edge width
424 edgeWidth_ = new SpinBox();
425 edgeWidth_->setCaption(tr(" pen width "));
426 edgeWidth_->setValue(settings().edgeWidth());
427 actionEdgeWidth_ = toolModeToolBar_->addWidget(edgeWidth_);
428 connect(edgeWidth_, SIGNAL(valueChanged(double)), this, SLOT(setEdgeWidth_(double)));
429
430 // Separator
431 separatorSketch1_ = toolModeToolBar_->addSeparator();
432
433 // Planar map mode
434 actionPlanarMapMode_ = new QAction(this);
435 actionPlanarMapMode_->setCheckable(true);
436 actionPlanarMapMode_->setChecked(true);
437 toolModeToolBar_->addAction(actionPlanarMapMode_);
438 toolModeToolBar_->widgetForAction(actionPlanarMapMode_)->setFixedSize(110,sideLength);
439 actionPlanarMapMode_->setText(tr("Toggle intersections"));
440 actionPlanarMapMode_->setIcon(QIcon(":/images/planar-map-on.png"));
441 actionPlanarMapMode_->setStatusTip(tr("When intersections are enabled, the sketched curve automatically splits existing curves and surfaces."));
442 //actionPlanarMapMode_->setShortcut(QKeySequence(Qt::Key_Backspace));
443 actionPlanarMapMode_->setShortcutContext(Qt::ApplicationShortcut);
444 mainWindow()->addAction(actionPlanarMapMode_);
445 connect(actionPlanarMapMode_, SIGNAL(triggered()), this, SLOT(togglePlanarMapMode()));
446
447 // Separator
448 separatorSketch2_ = toolModeToolBar_->addSeparator();
449
450 // Snapping
451 actionSnapMode_ = new QAction(this);
452 actionSnapMode_->setCheckable(true);
453 actionSnapMode_->setChecked(true);
454 toolModeToolBar_->addAction(actionSnapMode_);
455 toolModeToolBar_->widgetForAction(actionSnapMode_)->setFixedSize(110,sideLength);
456 actionSnapMode_->setText(tr("Toggle snapping"));
457 actionSnapMode_->setIcon(QIcon(":/images/snapping-on.png"));
458 actionSnapMode_->setStatusTip(tr("When snapping is enabled, the sketched curve is automatically glued to existing curves."));
459 //actionSnapMode_->setShortcut(QKeySequence(Qt::Key_Backspace));
460 actionSnapMode_->setShortcutContext(Qt::ApplicationShortcut);
461 mainWindow()->addAction(actionSnapMode_);
462 connect(actionSnapMode_, SIGNAL(triggered()), this, SLOT(toggleSnapping()));
463
464 // Edge width
465 actionSnapThreshold_ = toolModeToolBar_->addWidget(snapThreshold_);
466
467 // --------------------- Sculpt options ------------------------
468
469 actionSculptRadius_ = toolModeToolBar_->addWidget(sculptRadius_);
470
471 // --------------------- Cut options ------------------------
472
473 // Set default Tool Mode
474 setToolMode(SKETCH);
475 }
476
displayMode() const477 Global::DisplayMode Global::displayMode() const
478 {
479 return currentDisplayMode_;
480 }
481
activeView() const482 View * Global::activeView() const
483 {
484 return mainWindow()->activeView();
485 }
486
hoveredView() const487 View * Global::hoveredView() const
488 {
489 return mainWindow()->hoveredView();
490 }
491
activeTime() const492 Time Global::activeTime() const
493 {
494 return activeView()->activeTime();
495 }
496
timeline() const497 Timeline * Global::timeline() const
498 {
499 return mainWindow()->timeline();
500 }
501
setDisplayMode(Global::DisplayMode mode)502 void Global::setDisplayMode(Global::DisplayMode mode)
503 {
504 if(currentDisplayMode_ == mode)
505 {
506 // do nothing
507 }
508 else
509 {
510 currentDisplayMode_ = mode;
511 }
512 }
513
showCanvas() const514 bool Global::showCanvas() const
515 {
516 return mainWindow()->isShowCanvasChecked();
517 }
518
togglePlanarMapMode()519 void Global::togglePlanarMapMode()
520 {
521 if(actionPlanarMapMode_->isChecked())
522 {
523 actionPlanarMapMode_->setText(tr("Disable intersections"));
524 actionPlanarMapMode_->setIcon(QIcon(":/images/planar-map-on.png"));
525 }
526 else
527 {
528 actionPlanarMapMode_->setText(tr("Enable intersections"));
529 actionPlanarMapMode_->setIcon(QIcon(":/images/planar-map-off.png"));
530 }
531 }
532
toggleSnapping()533 void Global::toggleSnapping()
534 {
535 if(actionSnapMode_->isChecked())
536 {
537 actionSnapMode_->setText(tr("Disable snapping"));
538 actionSnapMode_->setIcon(QIcon(":/images/snapping-on.png"));
539 actionSnapThreshold_->setEnabled(true);
540 }
541 else
542 {
543 actionSnapMode_->setText(tr("Enable snapping"));
544 actionSnapMode_->setIcon(QIcon(":/images/snapping-off.png"));
545 actionSnapThreshold_->setEnabled(false);
546 }
547 }
548
toggleStylusPressure()549 void Global::toggleStylusPressure()
550 {
551 // Nothing to do
552 }
553
setScalingCorner(bool b)554 void Global::setScalingCorner(bool b)
555 {
556 isScalingCorner_ = b;
557 updateStatusBarHelp();
558 }
559
setScalingEdge(bool b)560 void Global::setScalingEdge(bool b)
561 {
562 isScalingEdge_ = b;
563 updateStatusBarHelp();
564 }
565
setRotating(bool b)566 void Global::setRotating(bool b)
567 {
568 isRotating_ = b;
569 updateStatusBarHelp();
570 }
571
setDragAndDropping(bool b)572 void Global::setDragAndDropping(bool b)
573 {
574 isDragAndDropping_ = b;
575 updateStatusBarHelp();
576 }
577
setDraggingPivot(bool b)578 void Global::setDraggingPivot(bool b)
579 {
580 isDraggingPivot_ = b;
581 updateStatusBarHelp();
582 }
583
toolMode() const584 Global::ToolMode Global::toolMode() const
585 {
586 if(mainWindow()->isEditCanvasSizeVisible())
587 {
588 return EDIT_CANVAS_SIZE;
589 }
590 else
591 {
592 return toolMode_;
593 }
594 }
595
setToolMode(Global::ToolMode mode)596 void Global::setToolMode(Global::ToolMode mode)
597 {
598 // Check consistency with action state
599 if(!toolModeActions[mode]->isChecked())
600 toolModeActions[mode]->setChecked(true);
601
602 // Set member variable
603 toolMode_ = mode;
604
605 // Hide everything
606 actionChangeColor_->setVisible(false);
607 actionChangeEdgeWidth_->setVisible(false);
608 actionCreateFace_->setVisible(false);
609 actionAddCycles_->setVisible(false);
610 actionRemoveCycles_->setVisible(false);
611
612 toolModeToolBar_->removeAction(actionGlue_);
613 toolModeToolBar_->removeAction(actionUnglue_);
614 toolModeToolBar_->removeAction(actionUncut_);
615 actionPlanarMapMode_->setVisible(false);
616 actionSnapMode_->setVisible(false);
617 actionUseTabletPressure_->setVisible(false);
618 actionEdgeWidth_->setVisible(false);
619 actionSnapThreshold_->setVisible(false);
620 actionSculptRadius_->setVisible(false);
621 separatorSelect1_->setVisible(false);
622 separatorSelect2_->setVisible(false);
623 separatorSketch1_->setVisible(false);
624 separatorSketch2_->setVisible(false);
625
626 // Desired icon size
627 double sideLength = 40;
628
629 // Show relevant
630 switch(toolMode_)
631 {
632 case SELECT:
633 actionChangeColor_->setVisible(true);
634 actionChangeEdgeWidth_->setVisible(true);
635 actionCreateFace_->setVisible(true);
636 actionAddCycles_->setVisible(true);
637 actionRemoveCycles_->setVisible(true);
638 toolModeToolBar_->addAction(actionGlue_);
639 toolModeToolBar_->addAction(actionUnglue_);
640 toolModeToolBar_->addAction(actionUncut_);
641 toolModeToolBar_->widgetForAction(actionGlue_)->setFixedSize(sideLength+20,sideLength);
642 toolModeToolBar_->widgetForAction(actionUnglue_)->setFixedSize(sideLength+20,sideLength);
643 toolModeToolBar_->widgetForAction(actionUncut_)->setFixedSize(sideLength+20,sideLength);
644 separatorSelect1_->setVisible(true);
645 separatorSelect2_->setVisible(true);
646 break;
647 case SKETCH:
648 actionPlanarMapMode_->setVisible(true);
649 actionSnapMode_->setVisible(true);
650 actionUseTabletPressure_->setVisible(true);
651 actionSnapThreshold_->setVisible(true);
652 actionEdgeWidth_->setVisible(true);
653 separatorSketch1_->setVisible(true);
654 separatorSketch2_->setVisible(true);
655 break;
656 case SCULPT:
657 actionSculptRadius_->setVisible(true);
658 break;
659 case PAINT:
660 break;
661 default:
662 break;
663 }
664
665 // Even when there's no icon, make it high enough
666 toolModeToolBar_->setMinimumHeight(50);
667
668 // Update help
669 updateStatusBarHelp();
670
671 // Update scene
672 mainWindow()->update();
673 mainWindow()->updatePicking();
674 }
675
676
updateStatusBarHelp()677 void Global::updateStatusBarHelp()
678 {
679 Qt::KeyboardModifiers keys = keyboardModifiers();
680 bool isCtrlDown = keys & Qt::ControlModifier;
681 bool isShiftDown = keys & Qt::ShiftModifier;
682 bool isAltDown = keys & Qt::AltModifier;
683
684 QString message;
685 if(isCtrlDown || isShiftDown || isAltDown)
686 {
687 message += "[";
688 if(isCtrlDown)
689 {
690 message += QString(ACTION_MODIFIER_NAME_SHORT).toUpper();
691 if(isShiftDown || isAltDown)
692 message += ",";
693 }
694 if(isShiftDown)
695 {
696 message += "SHIFT";
697 if(isAltDown)
698 message += ",";
699 }
700
701 if(isAltDown)
702 {
703 message += "ALT";
704 }
705 message += "] ";
706 }
707
708 if (isScalingCorner_)
709 {
710 if(!isShiftDown)
711 message += "Hold SHIFT to preserve proportions. ";
712 if(!isAltDown)
713 message += "Hold ALT to scale relative to center/pivot. ";
714 }
715 else if (isScalingEdge_)
716 {
717 if(!isAltDown)
718 message += "Hold ALT to scale relative to center/pivot. ";
719 }
720 else if (isRotating_)
721 {
722 if(!isShiftDown)
723 message += "Hold SHIFT to rotate by 45° only. ";
724 if(!isAltDown)
725 message += "Hold ALT to rotate relative to opposite corner. ";
726 }
727 else if (isDragAndDropping_)
728 {
729 if(!isShiftDown)
730 message += "Hold SHIFT to constrain translation along 45° axes. ";
731 }
732 else if (isDraggingPivot_)
733 {
734 if(!isShiftDown)
735 message += "Hold SHIFT to snap to center and corners of bounding box. ";
736 }
737 else if(toolMode() == SELECT)
738 {
739 if(!isCtrlDown && !isShiftDown && !isAltDown) {
740 message += "Click to select highlighted object. Click on background to deselect all. Hold " + QString(ACTION_MODIFIER_NAME_SHORT).toUpper() + ", SHIFT, or ALT for more actions.";
741 }
742 else if(isCtrlDown && !isShiftDown && !isAltDown) {
743 message += "Click on curve to insert end point. Click on face to insert point-in-face.";
744 }
745 else if(!isCtrlDown && isShiftDown && !isAltDown) {
746 message += "Click to add highlighted object to the selection. Hold also ALT for different action.";
747 }
748 else if(!isCtrlDown && !isShiftDown && isAltDown) {
749 message += "Click to remove highlighted object from the selection. Hold also SHIFT for different action.";
750 }
751 else if(!isCtrlDown && isShiftDown && isAltDown) {
752 message += "Click to select unselected objects, or deselect selected objects.";
753 }
754 else {
755 message += "No action available for this combination of keyboard modifiers.";
756 }
757 }
758 else if(toolMode() == SKETCH)
759 {
760 if(!isCtrlDown && !isShiftDown && !isAltDown) {
761 message += "Hold left mouse button to draw a curve. " + QString(ACTION_MODIFIER_NAME_SHORT).toUpper() + ": Change pen width. ALT: Change snap threshold.";
762 }
763 else if(isCtrlDown && !isShiftDown && !isAltDown) {
764 message += "Hold left mouse button to change pen width.";
765 }
766 else if(!isCtrlDown && !isShiftDown && isAltDown) {
767 message += "Hold left mouse button to change snap threshold.";
768 }
769 else if(isCtrlDown && !isShiftDown && isAltDown) {
770 message += "Hold left mouse button to change both pen width and snap threshold.";
771 }
772 else {
773 message += "No action available for this combination of keyboard modifiers.";
774 }
775 }
776 else if(toolMode() == PAINT)
777 {
778 if(!isCtrlDown && !isShiftDown && !isAltDown) {
779 message += "Click on closed region delimited by curves to fill. Click on object to change color. Click on background to change background color.";
780 }
781 else {
782 message += "No action available for this combination of keyboard modifiers.";
783 }
784 }
785 else if(toolMode() == SCULPT)
786 {
787 if(!isCtrlDown && !isShiftDown && !isAltDown) {
788 message += "Hold left mouse button (LMB) to drag endpoint, or drag curve within radius. " + QString(ACTION_MODIFIER_NAME_SHORT).toUpper() + ": radius. SHIFT: smooth. ALT: thickness.";
789 }
790 else if(isCtrlDown && !isShiftDown && !isAltDown) {
791 message += "Hold LMB to change the radius of the sculpting tool. Note: radius not visible if cursor too far from curve.";
792 }
793 else if(!isCtrlDown && isShiftDown && !isAltDown) {
794 message += "Hold LMB to smooth curve within radius.";
795 }
796 else if(!isCtrlDown && !isShiftDown && isAltDown) {
797 message += "Hold LMB to change thickness of curve within radius. Trick: use a large radius to edit thickness of the whole curve.";
798 }
799 else {
800 message += "No action available for this combination of keyboard modifiers.";
801 }
802 }
803
804 statusBarHelp_->setText(message);
805 }
806
807 // Other getters
devSettings()808 DevSettings * Global::devSettings() { return DevSettings::instance(); }
mainWindow() const809 MainWindow * Global::mainWindow() const { return mainWindow_; }
settings()810 Settings & Global::settings() { return preferences_; }
scene() const811 Scene * Global::scene() const {return mainWindow()->scene();}
812
edgeColor()813 QColor Global::edgeColor()
814 {
815 return currentColor_->color();
816 }
817
faceColor()818 QColor Global::faceColor()
819 {
820 return currentColor_->color();
821 }
822
useTabletPressure() const823 bool Global::useTabletPressure() const
824 {
825 return actionUseTabletPressure_->isChecked();
826 }
827
edgeWidth() const828 double Global::edgeWidth() const
829 {
830 return edgeWidth_->value();
831 }
832
setEdgeWidth_(double w)833 void Global::setEdgeWidth_(double w)
834 {
835 preferences_.setEdgeWidth(w);
836 }
837
setEdgeWidth(double w)838 void Global::setEdgeWidth(double w)
839 {
840 if(edgeWidth() != w)
841 {
842 edgeWidth_->setValue(w);
843 }
844
845 preferences_.setEdgeWidth(w);
846 }
847
openPreferencesDialog()848 void Global::openPreferencesDialog()
849 {
850 // Create preferences dialog
851 if(!preferencesDialog_)
852 {
853 preferencesDialog_ = new SettingsDialog(mainWindow());
854 connect(preferencesDialog_, SIGNAL(preferencesChanged()), this, SLOT(updateWidgetValuesFromPreferences()));
855 }
856
857 // Update and show references dialog
858 preferencesDialog_->go();
859 }
860
updateWidgetValuesFromPreferences()861 void Global::updateWidgetValuesFromPreferences()
862 {
863 edgeWidth_->setValue(preferences_.edgeWidth());
864 }
865
planarMapMode() const866 bool Global::planarMapMode() const
867 {
868 return actionPlanarMapMode_->isChecked();
869 }
870
snapMode() const871 bool Global::snapMode() const
872 {
873 return actionSnapMode_->isChecked();
874 }
875
snapThreshold() const876 double Global::snapThreshold() const
877 {
878 return snapThreshold_->value();
879 }
880
setSnapThreshold(double newSnapThreshold)881 void Global::setSnapThreshold(double newSnapThreshold)
882 {
883 return snapThreshold_->setValue(newSnapThreshold);
884 }
885
sculptRadius() const886 double Global::sculptRadius() const
887 {
888 return sculptRadius_->value();
889 }
890
setSculptRadius(double newRadius)891 void Global::setSculptRadius(double newRadius)
892 {
893 return sculptRadius_->setValue(newRadius);
894 }
895
readSettings()896 void Global::readSettings()
897 {
898 QSettings qsettings;
899
900 // Geometry of the window
901 QSize size = qsettings.value("size", QSize(400, 400)).toSize();
902 QPoint pos = qsettings.value("pos", QPoint(200, 200)).toPoint();
903 mainWindow()->resize(size);
904 mainWindow()->move(pos);
905
906 // User settings
907 settings().readFromDisk(qsettings);
908
909 // Other settings
910 snapThreshold_->setValue( qsettings.value("tools-sketch-snapthreshold", 15.0).toDouble() );
911 sculptRadius_->setValue( qsettings.value("tools-sculpt-radius", 50.0).toDouble() );
912 }
913
writeSettings()914 void Global::writeSettings()
915 {
916 QSettings qsettings;
917
918 // Geometry of the window
919 qsettings.setValue("size", mainWindow()->size());
920 qsettings.setValue("pos", mainWindow()->pos());
921
922 // User settings
923 settings().writeToDisk(qsettings);
924
925 // Other settings
926 qsettings.setValue("tools-sketch-snapthreshold", snapThreshold_->value() );
927 qsettings.setValue("tools-sculpt-radius", sculptRadius_->value() );
928 }
929
930 // ----- ToolModeAction -----
931
ToolModeAction(Global::ToolMode mode,QObject * parent)932 ToolModeAction::ToolModeAction(Global::ToolMode mode, QObject * parent) :
933 QAction(parent),
934 toolMode(mode)
935 {
936 connect(this, SIGNAL(triggered()), this, SLOT(emitSpecializedTriggered()));
937 }
938
emitSpecializedTriggered()939 void ToolModeAction::emitSpecializedTriggered()
940 {
941 emit triggered(toolMode);
942 }
943
setDocumentDir(const QDir & dir)944 void Global::setDocumentDir(const QDir & dir)
945 {
946 documentDir_ = dir;
947 }
948
documentDir() const949 QDir Global::documentDir() const
950 {
951 return documentDir_;
952 }
953