1 
2 
3 #include "pane.h"
4 
5 // Tnz6 includes
6 #include "tapp.h"
7 #include "mainwindow.h"
8 #include "tenv.h"
9 #include "saveloadqsettings.h"
10 
11 #include "toonzqt/gutil.h"
12 
13 // TnzLib includes
14 #include "toonz/preferences.h"
15 #include "toonz/toonzfolders.h"
16 #include "toonz/tscenehandle.h"
17 
18 // TnzCore includes
19 #include "tsystem.h"
20 
21 // Qt includes
22 #include <QPainter>
23 #include <QStyleOptionDockWidget>
24 #include <QMouseEvent>
25 #include <QMainWindow>
26 #include <QSettings>
27 #include <QToolBar>
28 #include <QMap>
29 #include <QApplication>
30 #include <QFile>
31 #include <qdrawutil.h>
32 #include <assert.h>
33 #include <QDesktopWidget>
34 #include <QDialog>
35 #include <QLineEdit>
36 
37 extern TEnv::StringVar EnvSafeAreaName;
38 
39 //=============================================================================
40 // TPanel
41 //-----------------------------------------------------------------------------
42 
TPanel(QWidget * parent,Qt::WindowFlags flags,TDockWidget::Orientation orientation)43 TPanel::TPanel(QWidget *parent, Qt::WindowFlags flags,
44                TDockWidget::Orientation orientation)
45     : TDockWidget(parent, flags)
46     , m_panelType("")
47     , m_isMaximizable(true)
48     , m_isMaximized(false)
49     , m_panelTitleBar(0)
50     , m_multipleInstancesAllowed(true) {
51   // setFeatures(QDockWidget::DockWidgetMovable |
52   // QDockWidget::DockWidgetFloatable);
53   // setFloating(false);
54   m_panelTitleBar = new TPanelTitleBar(this, orientation);
55   setTitleBarWidget(m_panelTitleBar);
56   // connect(m_panelTitleBar,SIGNAL(doubleClick()),this,SLOT(onDoubleClick()));
57   connect(m_panelTitleBar, SIGNAL(doubleClick(QMouseEvent *)), this,
58           SIGNAL(doubleClick(QMouseEvent *)));
59   connect(m_panelTitleBar, SIGNAL(closeButtonPressed()), this,
60           SLOT(onCloseButtonPressed()));
61   setOrientation(orientation);
62 }
63 
64 //-----------------------------------------------------------------------------
65 
~TPanel()66 TPanel::~TPanel() {
67   // On quitting, save the floating panel's geomtry and state in order to
68   // restore them when opening the floating panel next time
69   if (isFloating()) {
70     TFilePath savePath =
71         ToonzFolder::getMyModuleDir() + TFilePath("popups.ini");
72     QSettings settings(QString::fromStdWString(savePath.getWideString()),
73                        QSettings::IniFormat);
74     settings.beginGroup("Panels");
75     settings.beginGroup(QString::fromStdString(m_panelType));
76     settings.setValue("geometry", geometry());
77     if (SaveLoadQSettings *persistent =
78             dynamic_cast<SaveLoadQSettings *>(widget()))
79       persistent->save(settings);
80   }
81 }
82 
83 //-----------------------------------------------------------------------------
84 
paintEvent(QPaintEvent * e)85 void TPanel::paintEvent(QPaintEvent *e) {
86   QPainter painter(this);
87 
88   if (widget()) {
89     QRect dockRect = widget()->geometry();
90 
91     dockRect.adjust(0, 0, -1, -1);
92     painter.fillRect(dockRect, m_bgcolor);
93     painter.setPen(Qt::black);
94     painter.drawRect(dockRect);
95   }
96 
97   painter.end();
98 }
99 
100 //-----------------------------------------------------------------------------
101 
onCloseButtonPressed()102 void TPanel::onCloseButtonPressed() {
103   emit closeButtonPressed();
104 
105   // Currently, Toonz panels that get closed indeed just remain hidden -
106   // ready to reappair if they are needed again. However, the user expects
107   // a new panel to be created - so we just reset the panel here.
108   // reset();    //Moved to panel invocation in floatingpanelcommand.cpp
109 
110   // Also, remove widget from its dock layout control
111   if (parentLayout()) parentLayout()->removeWidget(this);
112 }
113 
114 //-----------------------------------------------------------------------------
115 /*! activate the panel and set focus specified widget when mouse enters
116  */
enterEvent(QEvent * event)117 void TPanel::enterEvent(QEvent *event) {
118   // Only when Toonz application is active
119   QWidget *w = qApp->activeWindow();
120   if (w) {
121     // grab the focus, unless a line-edit is focused currently
122     QWidget *focusWidget = qApp->focusWidget();
123     if (focusWidget && dynamic_cast<QLineEdit *>(focusWidget)) {
124       event->accept();
125       return;
126     }
127 
128     widgetFocusOnEnter();
129 
130     // Some panels (e.g. Viewer, StudioPalette, Palette, ColorModel) are
131     // activated when mouse enters. Viewer is activatable only when being
132     // docked.
133     // Active windows will NOT switch when the current active window is dialog.
134     if (qobject_cast<QDialog *>(w) == 0 && isActivatableOnEnter())
135       activateWindow();
136     event->accept();
137   } else
138     event->accept();
139 }
140 
141 //-----------------------------------------------------------------------------
142 /*! clear focus when mouse leaves
143  */
leaveEvent(QEvent * event)144 void TPanel::leaveEvent(QEvent *event) {
145   QWidget *focusWidget = qApp->focusWidget();
146   if (focusWidget && dynamic_cast<QLineEdit *>(focusWidget)) {
147     return;
148   }
149   widgetClearFocusOnLeave();
150 }
151 
152 //-----------------------------------------------------------------------------
153 /*! load and restore previous geometry and state of the floating panel.
154     called from the function OpenFloatingPanel::getOrOpenFloatingPanel()
155     in floatingpanelcommand.cpp
156 */
restoreFloatingPanelState()157 void TPanel::restoreFloatingPanelState() {
158   TFilePath savePath = ToonzFolder::getMyModuleDir() + TFilePath("popups.ini");
159   QSettings settings(QString::fromStdWString(savePath.getWideString()),
160                      QSettings::IniFormat);
161   settings.beginGroup("Panels");
162 
163   if (!settings.childGroups().contains(QString::fromStdString(m_panelType)))
164     return;
165 
166   settings.beginGroup(QString::fromStdString(m_panelType));
167 
168   QRect geom = settings.value("geometry", saveGeometry()).toRect();
169   // check if it can be visible in the current screen
170   if (!(geom & QApplication::desktop()->availableGeometry(this)).isEmpty())
171     setGeometry(geom);
172   // load optional settings
173   if (SaveLoadQSettings *persistent =
174           dynamic_cast<SaveLoadQSettings *>(widget()))
175     persistent->load(settings);
176 }
177 
178 //=============================================================================
179 // TPanelTitleBarButton
180 //-----------------------------------------------------------------------------
181 
TPanelTitleBarButton(QWidget * parent,const QString & standardPixmapName)182 TPanelTitleBarButton::TPanelTitleBarButton(QWidget *parent,
183                                            const QString &standardPixmapName)
184     : QWidget(parent)
185     , m_standardPixmap(standardPixmapName)
186     , m_standardPixmapName(standardPixmapName)
187     , m_rollover(false)
188     , m_pressed(false)
189     , m_buttonSet(0)
190     , m_id(0) {
191   setFixedSize(m_standardPixmap.size());
192 }
193 
194 //-----------------------------------------------------------------------------
195 
TPanelTitleBarButton(QWidget * parent,const QPixmap & standardPixmap)196 TPanelTitleBarButton::TPanelTitleBarButton(QWidget *parent,
197                                            const QPixmap &standardPixmap)
198     : QWidget(parent)
199     , m_standardPixmap(standardPixmap)
200     , m_rollover(false)
201     , m_pressed(false)
202     , m_buttonSet(0)
203     , m_id(0) {
204   setFixedSize(m_standardPixmap.size() / m_standardPixmap.devicePixelRatio());
205 }
206 
207 //-----------------------------------------------------------------------------
208 
setButtonSet(TPanelTitleBarButtonSet * buttonSet,int id)209 void TPanelTitleBarButton::setButtonSet(TPanelTitleBarButtonSet *buttonSet,
210                                         int id) {
211   m_buttonSet = buttonSet;
212   m_id        = id;
213   m_buttonSet->add(this);
214 }
215 
216 //-----------------------------------------------------------------------------
217 
setPressed(bool pressed)218 void TPanelTitleBarButton::setPressed(bool pressed) {
219   if (pressed != m_pressed) {
220     m_pressed = pressed;
221     update();
222   }
223 }
224 
225 //-----------------------------------------------------------------------------
226 
paintEvent(QPaintEvent * event)227 void TPanelTitleBarButton::paintEvent(QPaintEvent *event) {
228   // Set unique pressed colors if filename contains the following words:
229   QColor bgColor = getPressedColor();
230   if (m_standardPixmapName.contains("freeze", Qt::CaseInsensitive))
231     bgColor = getFreezeColor();
232   if (m_standardPixmapName.contains("preview", Qt::CaseInsensitive))
233     bgColor = getPreviewColor();
234 
235   QPixmap panePixmap    = recolorPixmap(svgToPixmap(m_standardPixmapName));
236   QPixmap panePixmapOff = compositePixmap(panePixmap, 0.8);
237   QPixmap panePixmapOver =
238       compositePixmap(panePixmap, 1, QSize(), 0, 0, getOverColor());
239   QPixmap panePixmapOn = compositePixmap(panePixmap, 1, QSize(), 0, 0, bgColor);
240 
241   QPainter painter(this);
242   painter.drawPixmap(
243       0, 0,
244       m_pressed ? panePixmapOn : m_rollover ? panePixmapOver : panePixmapOff);
245   painter.end();
246 }
247 
248 //-----------------------------------------------------------------------------
249 
mouseMoveEvent(QMouseEvent * event)250 void TPanelTitleBarButton::mouseMoveEvent(QMouseEvent *event) {}
251 
252 //-----------------------------------------------------------------------------
253 
enterEvent(QEvent *)254 void TPanelTitleBarButton::enterEvent(QEvent *) {
255   if (!m_rollover) {
256     m_rollover = true;
257     if (!m_pressed) update();
258   }
259 }
260 
261 //-----------------------------------------------------------------------------
262 
leaveEvent(QEvent *)263 void TPanelTitleBarButton::leaveEvent(QEvent *) {
264   if (m_rollover) {
265     m_rollover = false;
266     if (!m_pressed) update();
267   }
268 }
269 
270 //-----------------------------------------------------------------------------
271 
mousePressEvent(QMouseEvent * e)272 void TPanelTitleBarButton::mousePressEvent(QMouseEvent *e) {
273   if (m_buttonSet) {
274     if (m_pressed) return;
275     m_buttonSet->select(this);
276   } else {
277     m_pressed = !m_pressed;
278     emit toggled(m_pressed);
279     update();
280   }
281 }
282 
283 //=============================================================================
284 // TPanelTitleBarButtonForSafeArea
285 //-----------------------------------------------------------------------------
286 
getSafeAreaNameList(QList<QString> & nameList)287 void TPanelTitleBarButtonForSafeArea::getSafeAreaNameList(
288     QList<QString> &nameList) {
289   TFilePath fp                = TEnv::getConfigDir();
290   QString currentSafeAreaName = QString::fromStdString(EnvSafeAreaName);
291 
292   std::string safeAreaFileName = "safearea.ini";
293 
294   while (!TFileStatus(fp + safeAreaFileName).doesExist() && !fp.isRoot() &&
295          fp.getParentDir() != TFilePath())
296     fp = fp.getParentDir();
297 
298   fp = fp + safeAreaFileName;
299 
300   if (TFileStatus(fp).doesExist()) {
301     QSettings settings(toQString(fp), QSettings::IniFormat);
302 
303     // find the current safearea name from the list
304     QStringList groups = settings.childGroups();
305     for (int g = 0; g < groups.size(); g++) {
306       settings.beginGroup(groups.at(g));
307       nameList.push_back(settings.value("name", "").toString());
308       settings.endGroup();
309     }
310   }
311 }
312 
313 //-----------------------------------------------------------------------------
314 
mousePressEvent(QMouseEvent * e)315 void TPanelTitleBarButtonForSafeArea::mousePressEvent(QMouseEvent *e) {
316   if (e->button() != Qt::RightButton) {
317     m_pressed = !m_pressed;
318     emit toggled(m_pressed);
319     update();
320   }
321 }
322 
323 //-----------------------------------------------------------------------------
324 
contextMenuEvent(QContextMenuEvent * e)325 void TPanelTitleBarButtonForSafeArea::contextMenuEvent(QContextMenuEvent *e) {
326   QMenu menu(this);
327 
328   QList<QString> safeAreaNameList;
329   getSafeAreaNameList(safeAreaNameList);
330   for (int i = 0; i < safeAreaNameList.size(); i++) {
331     QAction *action = new QAction(safeAreaNameList.at(i), this);
332     action->setData(safeAreaNameList.at(i));
333     connect(action, SIGNAL(triggered()), this, SLOT(onSetSafeArea()));
334     if (safeAreaNameList.at(i) == QString::fromStdString(EnvSafeAreaName)) {
335       action->setCheckable(true);
336       action->setChecked(true);
337     }
338     menu.addAction(action);
339   }
340 
341   menu.exec(e->globalPos());
342 }
343 
344 //-----------------------------------------------------------------------------
345 
onSetSafeArea()346 void TPanelTitleBarButtonForSafeArea::onSetSafeArea() {
347   QString safeAreaName = qobject_cast<QAction *>(sender())->data().toString();
348   // change safearea if the different one is selected
349   if (QString::fromStdString(EnvSafeAreaName) != safeAreaName) {
350     EnvSafeAreaName = safeAreaName.toStdString();
351     // emit sceneChanged without setting dirty flag
352     TApp::instance()->getCurrentScene()->notifySceneChanged(false);
353   }
354 }
355 
356 //-----------------------------------------------------------------------------
357 
358 //=============================================================================
359 // TPanelTitleBarButtonSet
360 //-----------------------------------------------------------------------------
361 
TPanelTitleBarButtonSet()362 TPanelTitleBarButtonSet::TPanelTitleBarButtonSet() {}
363 
~TPanelTitleBarButtonSet()364 TPanelTitleBarButtonSet::~TPanelTitleBarButtonSet() {}
365 
add(TPanelTitleBarButton * button)366 void TPanelTitleBarButtonSet::add(TPanelTitleBarButton *button) {
367   m_buttons.push_back(button);
368 }
369 
select(TPanelTitleBarButton * button)370 void TPanelTitleBarButtonSet::select(TPanelTitleBarButton *button) {
371   int i;
372   for (i = 0; i < (int)m_buttons.size(); i++)
373     m_buttons[i]->setPressed(button == m_buttons[i]);
374   emit selected(button->getId());
375 }
376 
377 //=============================================================================
378 // PaneTitleBar
379 //-----------------------------------------------------------------------------
380 
TPanelTitleBar(QWidget * parent,TDockWidget::Orientation orientation)381 TPanelTitleBar::TPanelTitleBar(QWidget *parent,
382                                TDockWidget::Orientation orientation)
383     : QFrame(parent), m_closeButtonHighlighted(false) {
384   setMouseTracking(true);
385   setFocusPolicy(Qt::NoFocus);
386 }
387 
388 //-----------------------------------------------------------------------------
389 
minimumSizeHint() const390 QSize TPanelTitleBar::minimumSizeHint() const { return QSize(20, 18); }
391 
392 //-----------------------------------------------------------------------------
393 
paintEvent(QPaintEvent *)394 void TPanelTitleBar::paintEvent(QPaintEvent *) {
395   QPainter painter(this);
396   QRect rect = this->rect();
397 
398   bool isPanelActive;
399 
400   TPanel *dw = qobject_cast<TPanel *>(parentWidget());
401   Q_ASSERT(dw != 0);
402   // docked panel
403   if (!dw->isFloating()) {
404     isPanelActive = dw->widgetInThisPanelIsFocused();
405     qDrawBorderPixmap(&painter, rect, QMargins(3, 3, 3, 3),
406                       (isPanelActive) ? m_activeBorderPm : m_borderPm);
407   }
408   // floating panel
409   else {
410     isPanelActive = isActiveWindow();
411     qDrawBorderPixmap(
412         &painter, rect, QMargins(3, 3, 3, 3),
413         (isPanelActive) ? m_floatActiveBorderPm : m_floatBorderPm);
414   }
415 
416   if (dw->getOrientation() == TDockWidget::vertical) {
417     QString titleText = painter.fontMetrics().elidedText(
418         dw->windowTitle(), Qt::ElideRight, rect.width() - 50);
419 
420     painter.setBrush(Qt::NoBrush);
421     painter.setPen(isPanelActive ? m_activeTitleColor : m_titleColor);
422     painter.drawText(QPointF(8, 13), titleText);
423   }
424 
425   if (dw->isFloating()) {
426     QIcon paneCloseIcon = createQIcon("pane_close");
427     const static QPixmap closeButtonPixmap(
428         paneCloseIcon.pixmap(20, 18, QIcon::Normal, QIcon::Off));
429     const static QPixmap closeButtonPixmapOver(
430         paneCloseIcon.pixmap(20, 18, QIcon::Active));
431 
432     QPoint closeButtonPos(rect.right() - 20, rect.top());
433 
434     if (m_closeButtonHighlighted)
435       painter.drawPixmap(closeButtonPos, closeButtonPixmapOver);
436     else
437       painter.drawPixmap(closeButtonPos, closeButtonPixmap);
438   }
439 
440   painter.end();
441 }
442 
443 //-----------------------------------------------------------------------------
444 
mousePressEvent(QMouseEvent * event)445 void TPanelTitleBar::mousePressEvent(QMouseEvent *event) {
446   TDockWidget *dw = static_cast<TDockWidget *>(parentWidget());
447 
448   QPoint pos = event->pos();
449 
450   if (dw->isFloating()) {
451     QRect rect = this->rect();
452     QRect closeButtonRect(rect.right() - 20, rect.top() + 1, 20, 18);
453     if (closeButtonRect.contains(pos) && dw->isFloating()) {
454       event->accept();
455       dw->hide();
456       m_closeButtonHighlighted = false;
457       emit closeButtonPressed();
458       return;
459     }
460   }
461   event->ignore();
462 }
463 
464 //-----------------------------------------------------------------------------
465 
mouseMoveEvent(QMouseEvent * event)466 void TPanelTitleBar::mouseMoveEvent(QMouseEvent *event) {
467   TDockWidget *dw = static_cast<TDockWidget *>(parentWidget());
468 
469   if (dw->isFloating()) {
470     QPoint pos = event->pos();
471     QRect rect = this->rect();
472     QRect closeButtonRect(rect.right() - 18, rect.top() + 1, 18, 18);
473 
474     if (closeButtonRect.contains(pos) && dw->isFloating())
475       m_closeButtonHighlighted = true;
476     else
477       m_closeButtonHighlighted = false;
478   }
479 
480   update();
481   event->ignore();
482 }
483 
484 //-----------------------------------------------------------------------------
485 
mouseDoubleClickEvent(QMouseEvent * me)486 void TPanelTitleBar::mouseDoubleClickEvent(QMouseEvent *me) {
487   emit doubleClick(me);
488   me->ignore();
489 }
490 
491 //-----------------------------------------------------------------------------
492 
add(const QPoint & pos,QWidget * widget)493 void TPanelTitleBar::add(const QPoint &pos, QWidget *widget) {
494   m_buttons.push_back(std::make_pair(pos, widget));
495 }
496 
497 //-----------------------------------------------------------------------------
498 
resizeEvent(QResizeEvent * e)499 void TPanelTitleBar::resizeEvent(QResizeEvent *e) {
500   QWidget::resizeEvent(e);
501   int i;
502   for (i = 0; i < (int)m_buttons.size(); i++) {
503     QPoint p   = m_buttons[i].first;
504     QWidget *w = m_buttons[i].second;
505     if (p.x() < 0) p.setX(p.x() + width());
506     w->move(p);
507   }
508 }
509 
510 //=============================================================================
511 // TPanelFactory
512 //-----------------------------------------------------------------------------
513 
TPanelFactory(QString panelType)514 TPanelFactory::TPanelFactory(QString panelType) : m_panelType(panelType) {
515   assert(tableInstance().count(panelType) == 0);
516   tableInstance()[m_panelType] = this;
517 }
518 
519 //-----------------------------------------------------------------------------
520 
~TPanelFactory()521 TPanelFactory::~TPanelFactory() { tableInstance().remove(m_panelType); }
522 
523 //-----------------------------------------------------------------------------
524 
tableInstance()525 QMap<QString, TPanelFactory *> &TPanelFactory::tableInstance() {
526   static QMap<QString, TPanelFactory *> table;
527   return table;
528 }
529 
530 //-----------------------------------------------------------------------------
531 
createPanel(QWidget * parent,QString panelType)532 TPanel *TPanelFactory::createPanel(QWidget *parent, QString panelType) {
533   TPanel *panel = 0;
534 
535   QMap<QString, TPanelFactory *>::iterator it = tableInstance().find(panelType);
536   if (it == tableInstance().end()) {
537     TPanel *panel = new TPanel(parent);
538     panel->setPanelType(panelType.toStdString());
539     return panel;
540   } else {
541     TPanelFactory *factory = it.value();
542     TPanel *panel          = factory->createPanel(parent);
543     panel->setPanelType(panelType.toStdString());
544     return panel;
545   }
546 }
547 
548 //-----------------------------------------------------------------------------
549 
createPanel(QWidget * parent)550 TPanel *TPanelFactory::createPanel(QWidget *parent) {
551   TPanel *panel = new TPanel(parent);
552   panel->setObjectName(getPanelType());
553   panel->setWindowTitle(getPanelType());
554   initialize(panel);
555   return panel;
556 }
557 
558 //-----------------------------------------------------------------------------
559