1 
2 
3 // TnzQt includes
4 #include "toonzqt/menubarcommand.h"
5 #include "docklayout.h"
6 
7 // Qt includes
8 #include <QEvent>
9 #include <QMouseEvent>
10 #include <QApplication>
11 #include <QDesktopWidget>
12 
13 // STD includes
14 #include <assert.h>
15 #include <math.h>
16 #include <algorithm>
17 
18 //========================================================
19 
20 //-----------------------
21 //    Dock Lock Check
22 //-----------------------
23 
instance()24 DockingCheck *DockingCheck::instance() {
25   static DockingCheck _instance;
26   return &_instance;
27 }
28 
29 //-------------------------------------
30 
setToggle(QAction * toggle)31 void DockingCheck::setToggle(QAction *toggle) { m_toggle = toggle; }
32 
33 //-------------------------------------
34 
setIsEnabled(bool on)35 void DockingCheck::setIsEnabled(bool on) {
36   m_enabled = on;
37   if (m_toggle) m_toggle->setChecked(on);
38 }
39 
40 //========================================================
41 
42 class DockingToggleCommand final : public MenuItemHandler {
43 public:
DockingToggleCommand()44   DockingToggleCommand() : MenuItemHandler("MI_DockingCheck") {}
45 
execute()46   void execute() override {
47     DockingCheck *dc = DockingCheck::instance();
48     dc->setIsEnabled(!dc->isEnabled());
49   }
50 
51 } dockingToggleCommand;
52 
53 //========================================================
54 
55 //------------------------
56 //    Geometry inlines
57 //------------------------
58 
59 //! Calculates infinite-norm distance between points \b a and \b b.
absoluteDistance(const QPoint & a,const QPoint & b)60 inline int absoluteDistance(const QPoint &a, const QPoint &b) {
61   return std::max(abs(a.x() - b.x()), abs(a.y() - b.y()));
62 }
63 
64 //-------------------------------------
65 
66 // Il metodo QRectF::toRect pare che faccia la cosa commentata. Ovviamente non
67 // e' la cosa piu' simpatica - considera
68 // che in questo modo i bordi del rect di input *non vengono approssimati alle
69 // piu' vicine coordinate intere*!
70 //  es:   topLeft= (1/3, 1/3); width= 4/3, height= 4/3 => left= top= right=
71 //  bottom= 0.
toRect(const QRectF & rect)72 inline QRect toRect(const QRectF &rect) {
73   // return QRect(qRound(rect.left()), qRound(rect.top()), qRound(rect.width()),
74   // qRound(rect.height()));
75   return QRect(rect.topLeft().toPoint(),
76                rect.bottomRight().toPoint() -= QPoint(1, 1));
77 }
78 
79 //========================================================
80 
81 // Forward declaration
82 namespace {
83 QDesktopWidget *desktop;
84 void getClosestAvailableMousePosition(QPoint &globalPos);
85 }
86 
87 //========================================================
88 
89 //----------------------
90 //    Layout inlines
91 //----------------------
92 
update()93 inline void DockLayout::update() {
94   // E' necessario?
95   applyGeometry();
96 }
97 
98 //========================================================
99 
100 //------------------------------
101 //    My Dock Widget Methods
102 //------------------------------
103 
104 //! Constructs a dock Widget; every newly constructed dock widget is floating
105 //! (i.e. not docked
106 //! into the layout).
DockWidget(QWidget * parent,Qt::WindowFlags flags)107 DockWidget::DockWidget(QWidget *parent, Qt::WindowFlags flags)
108     : QFrame(parent, flags)
109     , m_dragging(false)
110     , m_resizing(false)
111     , m_floating(true)
112     , m_undocking(false)
113     , m_parentLayout(0)
114     , m_selectedPlace(0)
115     , m_maximized(0) {
116   // Don't let this widget inherit the parent's backround color
117   // setAutoFillBackground(true);
118   // setBackgroundRole(QPalette::Background);
119 
120   // setAttribute(Qt::WA_DeleteOnClose);   //Since Toonz just hides panels...
121   setAttribute(Qt::WA_Hover);
122 
123   // Set default minimum and maximum sizes.
124   setMinimumSize(QSize(50, 50));
125   setMaximumSize(QSize(10000, 10000));
126 
127   m_decoAllocator = new DockDecoAllocator;
128 
129   // Make sure the desktop is initialized and known
130   desktop = qApp->desktop();
131 }
132 
133 //-------------------------------------
134 
~DockWidget()135 DockWidget::~DockWidget() {
136   // Since close button lies on the title bar, make sure mouse is released if
137   // that is pressed.
138   if (QWidget::mouseGrabber() == this) releaseMouse();
139   clearDockPlaceholders();
140 
141   // Delete deco allocator
142   delete m_decoAllocator;
143 }
144 
145 //-------------------------------------
146 
147 //! Clears dock placeholders for this dockwidget. This is automatically called
148 //! at drag ends.
clearDockPlaceholders()149 void DockWidget::clearDockPlaceholders() {
150   unsigned int i;
151   for (i = 0; i < m_placeholders.size(); ++i) delete m_placeholders[i];
152   m_placeholders.clear();
153 }
154 
155 //-------------------------------------
156 
157 //! Shows dock widget in docked mode.
158 
159 //! Returns true or false whether input \b QPoint is inside the dragging grip
160 //! for \b this DockWidget, or not.
161 //! Typically (and by default), true is returned whenever p lies inside the
162 //! widget's title bar when floating;
163 //! however, you are free to place the drag grip anywhere reimplementing this
164 //! method in custom dockwidgets.
isDragGrip(QPoint p)165 bool DockWidget::isDragGrip(QPoint p) {
166   if (isFloating()) {
167     QRect frame        = frameGeometry();
168     QRect conts        = geometry();
169     int margin         = conts.left() - frame.left();
170     int titleBarHeight = conts.top() - frame.top();
171 
172     QRect titleArea(QPoint(0, margin - titleBarHeight),
173                     QPoint(width() - 1, -1));
174 
175     return titleArea.contains(p);
176   }
177 
178   return 0;
179 }
180 
181 //-------------------------------------
182 
event(QEvent * e)183 bool DockWidget::event(QEvent *e) {
184   // qDebug("Dock Widget - Event type: %d", e->type());
185 
186   // Little deviation or type specifications for received events
187   switch (e->type()) {
188   // Dock widgets are hover widgets - since their cursor may change on resize
189   // grips.
190   case QEvent::HoverMove:
191     hoverMoveEvent(static_cast<QHoverEvent *>(e));
192     return true;
193 
194   // Native titlebar press events must be redirected to common ones
195   // - and, in this case mouse has to grabbed
196   case QEvent::NonClientAreaMouseButtonPress:
197     // grabMouse();  //Cannot not go here or resizes cannot be natively handled
198     mousePressEvent(static_cast<QMouseEvent *>(e));
199     return true;
200 
201   // Titlebars are not natively handled in this raw class - their responsibility
202   // falls
203   // to user implementation of DockWidget class
204   case QEvent::WindowTitleChange:
205     windowTitleEvent(e);
206     return true;
207 
208   default:
209     return QWidget::event(e);
210   }
211 }
212 
213 //-------------------------------------
214 
215 //! Adjusts widget cursor depending on \b isResizeGrip()
216 // NOTA: Da migliorare: ricordare il cursor settato o se era unset e riapplicare
217 // quando fuori dal resize grip.
hoverMoveEvent(QHoverEvent * he)218 void DockWidget::hoverMoveEvent(QHoverEvent *he) {
219   if (m_floating && !m_resizing && !m_undocking) {
220     QCursor newCursor = Qt::ArrowCursor;
221 
222     if ((m_marginType = isResizeGrip(he->pos()))) {
223       // Hovering a margin - update cursor shape
224       if (m_marginType & leftMargin) {
225         if (m_marginType & topMargin)
226           newCursor = Qt::SizeFDiagCursor;
227         else if (m_marginType & bottomMargin)
228           newCursor = Qt::SizeBDiagCursor;
229         else
230           newCursor = Qt::SizeHorCursor;
231       } else if (m_marginType & rightMargin) {
232         if (m_marginType & topMargin)
233           newCursor = Qt::SizeBDiagCursor;
234         else if (m_marginType & bottomMargin)
235           newCursor = Qt::SizeFDiagCursor;
236         else
237           newCursor = Qt::SizeHorCursor;
238       } else
239         newCursor = Qt::SizeVerCursor;
240     }
241 
242     if (newCursor.shape() != cursor().shape()) setCursor(newCursor);
243   }
244 }
245 
246 //-------------------------------------
247 
248 //! Dispatches mouse presses for particular purposes...
249 
250 //! a)  Trigger a window resize (or none with native deco)
251 //!    if press is over a resize grip
252 
253 //! b)  Trigger a window drag if press is over a drag grip
mousePressEvent(QMouseEvent * me)254 void DockWidget::mousePressEvent(QMouseEvent *me) {
255   if ((m_marginType = m_floating ? isResizeGrip(me->pos()) : 0)) {
256     // Resize begins
257 
258     // NOTE: It is better to assume that resize grips dominate over drag grips:
259     // this ensures
260     // that mouse cursor changes are always consistent with resize events.
261 
262     m_resizing            = true;
263     m_dragMouseInitialPos = me->globalPos();  // Re-used as old position
264   } else if (isDragGrip(me->pos())) {
265     // Dragging begins
266     DockingCheck *lock = DockingCheck::instance();  // Docking system lock
267 
268     m_dragMouseInitialPos = me->globalPos();
269     m_dragInitialPos      = pos();
270     // Grab mouse inputs - useful if widget gets under placeholders
271     if (me->type() == QEvent::NonClientAreaMouseButtonPress)
272       // If can receive double-clicks, avoid grabbing mouse
273       grabMouse();
274 
275     if (m_floating) {
276       m_dragging = true;
277       // Do not allow docking if there is a maximized widget or the layout is
278       // locked
279       if (m_parentLayout && !m_parentLayout->getMaximized() &&
280           !lock->isEnabled())
281         m_parentLayout->calculateDockPlaceholders(this);
282     } else {
283       if (!lock->isEnabled()) m_undocking = true;
284       m_dragInitialPos = parentWidget()->mapToGlobal(m_dragInitialPos);
285     }
286   }
287 }
288 
289 //-------------------------------------
290 
mouseMoveEvent(QMouseEvent * me)291 void DockWidget::mouseMoveEvent(QMouseEvent *me) {
292   QPoint correctedGlobalPos(me->globalPos());
293   getClosestAvailableMousePosition(correctedGlobalPos);
294 
295   if (m_resizing) {
296     // m_dragMouseInitialPos re-used as old position
297     int dx = correctedGlobalPos.x() - m_dragMouseInitialPos.x();
298     int dy = correctedGlobalPos.y() - m_dragMouseInitialPos.y();
299 
300     QRect geom = geometry();
301 
302     if (m_marginType & leftMargin) {
303       int newWidth = geom.width() - dx;
304       if (newWidth >= minimumWidth() && newWidth <= maximumWidth())
305         geom.setLeft(geom.left() + dx);
306     } else if (m_marginType & rightMargin)
307       geom.setRight(geom.right() + dx);
308 
309     if (m_marginType & topMargin) {
310       int newHeight = geom.height() - dy;
311       if (newHeight >= minimumHeight() && newHeight <= maximumHeight())
312         geom.setTop(geom.top() + dy);
313     } else if (m_marginType & bottomMargin)
314       geom.setBottom(geom.bottom() + dy);
315 
316     setGeometry(geom);
317 
318     m_dragMouseInitialPos = correctedGlobalPos;
319   } else if (m_dragging) {
320     move(m_dragInitialPos + correctedGlobalPos - m_dragMouseInitialPos);
321     selectDockPlaceholder(me);
322   } else if (m_undocking) {
323     int distance = absoluteDistance(me->globalPos(), m_dragMouseInitialPos);
324     if (distance > 8) {
325       m_undocking = false;
326 
327       // Attempt undocking
328       if (m_parentLayout->undockItem(this)) {
329         m_dragging = true;
330 
331         // Then, move dock widget under cursor, as if drag actually begun at
332         // button press.
333         move(m_dragInitialPos + correctedGlobalPos - m_dragMouseInitialPos);
334         show();  // Dock widget is not automatically shown after undock.
335 
336         // Re-grab mouse inputs. Seems that making the window float (i.e.:
337         // reparenting) breaks old grab.
338         // NOTE: mouse *must* be grabbed only when visible - see Qt manual.
339         grabMouse();
340 
341         // After undocking takes place, docking possibilities have to be
342         // recalculated
343         m_parentLayout->calculateDockPlaceholders(this);
344         selectDockPlaceholder(me);
345       }
346     }
347   }
348 }
349 
350 //-------------------------------------
351 
mouseReleaseEvent(QMouseEvent * me)352 void DockWidget::mouseReleaseEvent(QMouseEvent *me) {
353   // Ensure mouse is released
354   releaseMouse();
355 
356   if (m_dragging) {
357     m_dragging = false;
358 
359     if (m_floating && m_selectedPlace) {
360       m_parentLayout->dockItem(this, m_selectedPlace);
361     } else {
362       // qDebug("Dock failed");
363     }
364 
365     // Clear dock placeholders
366     clearDockPlaceholders();
367     m_selectedPlace = 0;
368   } else if (m_undocking) {
369     // qDebug("Undock failed");
370     m_undocking = false;
371   } else if (m_resizing) {
372     m_resizing = false;
373   }
374 }
375 
376 //-------------------------------------
377 
378 //! DockWidgets respond to title bar double clicks maximizing the widget in
379 //! layout's contents rect.
mouseDoubleClickEvent(QMouseEvent * me)380 void DockWidget::mouseDoubleClickEvent(QMouseEvent *me) {
381   if (!m_floating && isDragGrip(me->pos())) {
382     parentLayout()->setMaximized(this, !m_maximized);
383   }
384 }
385 
386 //-------------------------------------
387 
maximizeDock()388 void DockWidget::maximizeDock() {
389   if (!m_floating) {
390     parentLayout()->setMaximized(this, !m_maximized);
391   }
392 }
393 
394 //-------------------------------------
395 //! Switch in selected dock placeholder's hierarchy.
wheelEvent(QWheelEvent * we)396 void DockWidget::wheelEvent(QWheelEvent *we) {
397   if (m_dragging) {
398     if (m_selectedPlace) {
399       DockPlaceholder *newSelected =
400           (we->delta() > 0)
401               ? m_selectedPlace->parentPlaceholder()
402               : m_selectedPlace->childPlaceholder(
403                     parentWidget()->mapFromGlobal(we->globalPos()));
404 
405       if (newSelected != m_selectedPlace) {
406         m_selectedPlace->hide();
407         newSelected->show();
408         m_selectedPlace = newSelected;
409       }
410     }
411   }
412 }
413 
414 //-------------------------------------
415 
416 //! Returns widget (separator or docked widget) containing input \point, or 0 if
417 //! none.
418 //! Convenience function combining parentLayout() and containerOf(\b point).
419 
420 //!\b NOTE: Observe that, in any case, the use of QEnterEvents is discouraged
421 //! for this purpose:
422 //! remember that we forcedly remain within its boundaries when dragging a dock
423 //! widget;
424 //! instead, we are rather interested about entering the dock widgets *below*.
hoveredWidget(QMouseEvent * me)425 QWidget *DockWidget::hoveredWidget(QMouseEvent *me) {
426   if (!m_parentLayout) return 0;
427 
428   QPoint point = parentWidget()->mapFromGlobal(me->globalPos());
429   return m_parentLayout->containerOf(point);
430 }
431 
432 //-------------------------------------
433 
434 //! Returns adjacent placeholders to hovered widget. If hovered is a separator,
435 //! just return associated placeholder.
placeAdjacentTo(DockWidget * dockWidget,int boundary)436 DockPlaceholder *DockWidget::placeAdjacentTo(DockWidget *dockWidget,
437                                              int boundary) {
438   Region *r = parentLayout()->find(dockWidget);
439 
440   if (((boundary == DockPlaceholder::left ||
441         boundary == DockPlaceholder::right) &&
442        r->getOrientation() == Region::horizontal) ||
443       ((boundary == DockPlaceholder::top ||
444         boundary == DockPlaceholder::bottom) &&
445        r->getOrientation() == Region::vertical)) {
446     // Placeholder is coherent with region orientation
447     return r->placeholders().size() ? r->placeholder(boundary % 2) : 0;
448   } else {
449     // Search in parent region
450     Region *parent = r->getParent();
451     if (parent) {
452       unsigned int i = parent->find(r);
453       return parent->placeholders().size()
454                  ? parent->placeholder(i + (boundary % 2))
455                  : 0;
456     } else {
457       // No parent region - dockWidget is the only widget of the whole layout;
458       // Then check the first 2 elements of placeholders vector.
459       if (!m_placeholders[boundary % 2]->getParentRegion()) {
460         return m_placeholders.size() ? m_placeholders[boundary % 2] : 0;
461       }
462     }
463   }
464 
465   return 0;
466 }
467 
468 //-------------------------------------
469 
470 //! Returns placeholder associated with input separator.
placeOfSeparator(DockSeparator * sep)471 DockPlaceholder *DockWidget::placeOfSeparator(DockSeparator *sep) {
472   Region *r = sep->getParentRegion();
473   int idx   = sep->getIndex();
474 
475   return r->placeholders().size() ? r->placeholder(idx + 1) : 0;
476 }
477 
478 //-------------------------------------
479 
480 //! Processes an input mouse event to select the active placeholder among the
481 //! possible ones
482 //! for \b this dock widget. The selected placeholder is also evidenced with
483 //! respect to the
484 //! other (or these are kept hidden) according to the body of this function.
selectDockPlaceholder(QMouseEvent * me)485 void DockWidget::selectDockPlaceholder(QMouseEvent *me) {
486   // const int inf= 1000000;
487   DockPlaceholder *selected = 0;
488 
489   // Search placeholders cotaining muose position
490   unsigned int i;
491   for (i = 0; i < m_placeholders.size(); ++i) {
492     if (m_placeholders[i]->geometry().contains(me->globalPos())) {
493       selected = m_placeholders[i];
494     }
495   }
496 
497   // In order to avoid flickering
498   if (m_selectedPlace != selected) {
499     if (m_selectedPlace) m_selectedPlace->hide();
500     if (selected) selected->show();
501   }
502 
503   m_selectedPlace = selected;
504 }
505 
506 //========================================================
507 
508 //-------------------------
509 //    Dock Placeholders
510 //-------------------------
511 
parentGeometry() const512 inline QRect DockPlaceholder::parentGeometry() const {
513   return m_region ? toRect(m_region->getGeometry())
514                   : m_owner->parentLayout()->contentsRect();
515 }
516 
517 //------------------------------------------------------
518 
519 //! Assigns the geometry of \b this placeholder.
520 
521 //! Once a placeholder is created, in response to a user window drag,
522 //! dock placeholders are calculated. After a placeholder is created,
523 //! its geometry is built according to this function. It is possible to
524 //! reimplement it in order to build custom placeholder styles.
buildGeometry()525 inline void DockPlaceholder::buildGeometry() {
526   QRect relativeToMainRect;
527 
528   if (m_separator)
529     relativeToMainRect = m_separator->geometry();
530   else {
531     QRect parentRect   = parentGeometry();
532     DockLayout *layout = m_owner->parentLayout();
533     QRect mainRect     = layout->contentsRect();
534     int sepWidth       = layout->spacing();
535     int margin = 6;  // layout->margin();   //Purtroppo questa info e' assegnata
536                      // prima delle Room...
537 
538     if (isRoot()) {
539       // Set the whole contents rect
540       relativeToMainRect = parentRect;
541 
542       // Set a square at middle of parent geometry
543       // QPoint center= parentRect.center();
544       // relativeToMainRect= QRect(center - QPoint(50,50), center +
545       // QPoint(50,50));
546     } else if (getParentRegion() == 0 ||
547                getParentRegion() == layout->rootRegion()) {
548       // Outer insertion case
549       switch (getAttribute()) {
550         int leftBound, upperBound;
551 
552       case left:
553         leftBound = parentRect.left() - margin;
554         relativeToMainRect =
555             QRect(leftBound, parentRect.top(), margin, parentRect.height());
556         break;
557       case right:
558         leftBound = parentRect.right() + 1;
559         relativeToMainRect =
560             QRect(leftBound, parentRect.top(), margin, parentRect.height());
561         break;
562       case top:
563         upperBound = parentRect.top() - margin;
564         relativeToMainRect =
565             QRect(parentRect.left(), upperBound, parentRect.width(), margin);
566         break;
567       default:
568         upperBound = parentRect.bottom() + 1;
569         relativeToMainRect =
570             QRect(parentRect.left(), upperBound, parentRect.width(), margin);
571         break;
572       }
573     } else
574       switch (getAttribute()) {
575         int leftBound, upperBound;
576 
577       case left:
578         leftBound = parentRect.left();
579         relativeToMainRect =
580             QRect(leftBound, parentRect.top(), sepWidth, parentRect.height());
581         break;
582       case right:
583         leftBound = parentRect.right() - sepWidth + 1;
584         relativeToMainRect =
585             QRect(leftBound, parentRect.top(), sepWidth, parentRect.height());
586         break;
587       case top:
588         upperBound = parentRect.top();
589         relativeToMainRect =
590             QRect(parentRect.left(), upperBound, parentRect.width(), sepWidth);
591         break;
592       default:
593         upperBound = parentRect.bottom() - sepWidth + 1;
594         relativeToMainRect =
595             QRect(parentRect.left(), upperBound, parentRect.width(), sepWidth);
596         break;
597       }
598   }
599 
600   QPoint topLeft =
601       m_owner->parentWidget()->mapToGlobal(relativeToMainRect.topLeft());
602   QPoint bottomRight =
603       m_owner->parentWidget()->mapToGlobal(relativeToMainRect.bottomRight());
604   setGeometry(QRect(topLeft, bottomRight));
605 }
606 
607 //------------------------------------------------------
608 
DockPlaceholder(DockWidget * owner,Region * r,int idx,int attributes)609 DockPlaceholder::DockPlaceholder(DockWidget *owner, Region *r, int idx,
610                                  int attributes)
611     : QWidget(owner)
612     , m_owner(owner)
613     , m_separator(0)
614     , m_region(r)
615     , m_idx(idx)
616     , m_attributes(attributes) {
617   setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint);
618 
619   // Set separators
620   if (r && idx && idx < (int)r->getChildList().size()) {
621     m_separator = r->separators()[idx - 1];
622   }
623 }
624 
625 //------------------------------------------------------
626 
getSeparator() const627 inline DockSeparator *DockPlaceholder::getSeparator() const {
628   return m_separator;
629 }
630 
631 //------------------------------------------------------
632 
633 //! Find the 'parent' of this placeholder (same side/orientation, on grandParent
634 //! region)
635 
636 //! NOTE: If no grandParent exists \b this is the root, which is then returned.
parentPlaceholder()637 DockPlaceholder *DockPlaceholder::parentPlaceholder() {
638   // Placeholders covering a whole separator or roots have no parent (return
639   // itself)
640   if (m_attributes == sepHor || m_attributes == sepVert || isRoot())
641     return this;
642 
643   // Now, check if owner Region has a parent.
644   Region *grandParent;
645   if (!m_region || !m_region->getParent()) return this;
646 
647   if ((grandParent = m_region->getParent()->getParent())) {
648     // Good, we finally have to search in grandParent's region our direct
649     // parent.
650     unsigned int idx = grandParent->find(m_region->getParent());
651 
652     // Since placeholders are built ordered, just use the found idx.
653     if (m_attributes == left || m_attributes == top)
654       return grandParent->placeholders().size() ? grandParent->placeholder(idx)
655                                                 : this;
656     else
657       return grandParent->placeholders().size()
658                  ? grandParent->placeholder(idx + 1)
659                  : this;
660   } else {
661     // GrandParent would be a new root. Then, take the first two possible
662     // placeholders
663     // of the entire dockWidget.
664     if (m_owner->m_placeholders.size()) {
665       DockPlaceholder *result = m_owner->m_placeholders[m_attributes % 2];
666       if (!result->m_region) return result;
667     }
668 
669     return this;
670   }
671 }
672 
673 //------------------------------------------------------
674 
675 //! The opposite of parentPlaceholder() - but a point belonging to the child
676 //! region to be selected is requested as univoque key among all children.
childPlaceholder(QPoint p)677 DockPlaceholder *DockPlaceholder::childPlaceholder(QPoint p) {
678   // Ensure this is not a root placeholder
679   if (m_attributes == root) return this;
680 
681   // Define children list and ensure it is not void.
682   Region *r;
683   unsigned int i;
684   bool lastExtremity;
685 
686   if (m_region) {
687     if (!m_region->getChildList().size()) return this;
688 
689     // Search the subregion containing p
690     for (i = 0; i < m_region->getChildList().size(); ++i)
691       if (m_region->childRegion(i)->getGeometry().contains(p)) break;
692 
693     if (i == m_region->getChildList().size())
694       return this;  // No subregion found...
695 
696     lastExtremity = (m_idx > (int)i);
697 
698     // Ensure it has subRegions.
699     r = m_region->childRegion(i);
700     if (!r->getChildList().size()) return this;
701   } else {
702     r             = m_owner->parentLayout()->rootRegion();
703     lastExtremity = m_attributes % 2;
704   }
705 
706   // Now, take the 'grandson' region as above.
707   for (i = 0; i < r->getChildList().size(); ++i)
708     if (r->childRegion(i)->getGeometry().contains(p)) break;
709 
710   if (i == r->getChildList().size()) return this;  // No subregion found...
711 
712   r = r->childRegion(i);
713 
714   // Finally, return child placeholder found.
715   return r->placeholders().size()
716              ? lastExtremity ? r->placeholders().back()
717                              : r->placeholders().front()
718              : this;
719 }
720 
721 //------------------------------------------------------
722 
723 //! Returns greatest placeholder which contains this one.
greatestPlaceholder()724 inline DockPlaceholder *DockPlaceholder::greatestPlaceholder() {
725   DockPlaceholder *old = this, *current = parentPlaceholder();
726 
727   while (current != old) {
728     old     = current;
729     current = old->parentPlaceholder();
730   }
731 
732   return current;
733 }
734 
735 //------------------------------------------------------
736 
737 //! Returns smallest placeholder contained by this one - a point belonging to
738 //! the child
739 //! region to be selected is requested as univoque key among all children.
smallestPlaceholder(QPoint p)740 inline DockPlaceholder *DockPlaceholder::smallestPlaceholder(QPoint p) {
741   DockPlaceholder *old = this, *current = childPlaceholder(p);
742 
743   while (current != old) {
744     old     = current;
745     current = old->childPlaceholder(p);
746   }
747 
748   return current;
749 }
750 
751 //========================================================================
752 
753 //------------------
754 //    Separators
755 //------------------
756 
DockSeparator(DockLayout * owner,bool orientation,Region * parentRegion)757 DockSeparator::DockSeparator(DockLayout *owner, bool orientation,
758                              Region *parentRegion)
759     : QWidget(owner->parentWidget())
760     , m_owner(owner)
761     , m_orientation(orientation)
762     , m_parentRegion(parentRegion)
763     , m_pressed(false) {
764   setObjectName("DockSeparator");
765   setWindowFlags(Qt::SubWindow);
766   setAutoFillBackground(false);
767 
768   // Set appropriate widget cursor
769   if (m_orientation == Region::horizontal)
770     setCursor(Qt::SplitHCursor);
771   else
772     setCursor(Qt::SplitVCursor);
773 
774   show();
775 }
776 
777 //-------------------------------------
778 
mousePressEvent(QMouseEvent * me)779 void DockSeparator::mousePressEvent(QMouseEvent *me) {
780   m_pressed = true;
781 
782   m_oldPos = me->globalPos();
783 
784   const std::deque<DockSeparator *> &sepList = m_parentRegion->separators();
785   const std::deque<Region *> &childList      = m_parentRegion->getChildList();
786 
787   // Find separated Regions (separator index)
788   unsigned int i;
789 
790   for (i = 0; i < sepList.size(); ++i)
791     if (sepList[i] == this) break;
792 
793   // Calculate bounds for separator shift
794   // First ensure extremal sizes are recalculated
795   m_parentRegion->calculateExtremalSizes();
796 
797   int sepWidth = m_owner->spacing();
798   Region *r    = getParentRegion();
799   double parentLeft, parentRight;
800   int leftSepCount = m_index;
801   int rightSepCount =
802       r->separators().size() - leftSepCount;  // This sep included
803 
804   if (m_orientation == Region::horizontal) {
805     parentLeft  = r->getGeometry().left();
806     parentRight = r->getGeometry().right();
807   } else {
808     parentLeft  = r->getGeometry().top();
809     parentRight = r->getGeometry().bottom();
810   }
811 
812   // Calculate left and right extremal sizes
813   int j, leftMinSize = 0, rightMinSize = 0, leftMaxSize = 0, rightMaxSize = 0;
814 
815   for (j = 0; j <= m_index; ++j) {
816     leftMinSize += r->childRegion(j)->getMinimumSize(m_orientation);
817     leftMaxSize += r->childRegion(j)->getMaximumSize(m_orientation);
818   }
819 
820   int size = r->getChildList().size();
821   for (j = m_index + 1; j < size; ++j) {
822     rightMinSize += r->childRegion(j)->getMinimumSize(m_orientation);
823     rightMaxSize += r->childRegion(j)->getMaximumSize(m_orientation);
824   }
825 
826   // Sull'intera regione padre
827   m_leftBound = std::max(parentLeft + leftMinSize + leftSepCount * sepWidth,
828                          parentRight - rightMaxSize - rightSepCount * sepWidth);
829 
830   m_rightBound =
831       std::min(parentLeft + leftMaxSize + leftSepCount * sepWidth,
832                parentRight - rightMinSize - rightSepCount * sepWidth);
833 }
834 
835 //-------------------------------------
836 
mouseReleaseEvent(QMouseEvent * me)837 inline void DockSeparator::mouseReleaseEvent(QMouseEvent *me) {
838   m_pressed = false;
839 }
840 
841 //-------------------------------------
842 
mouseMoveEvent(QMouseEvent * me)843 void DockSeparator::mouseMoveEvent(QMouseEvent *me) {
844   if (m_pressed) {
845     double movedPosition, newPosition;
846 
847     if (m_orientation == Region::horizontal) {
848       double dx = me->globalX() - m_oldPos.x();
849       movedPosition =
850           getParentRegion()->childRegion(m_index)->getGeometry().right() + dx;
851       newPosition =
852           std::min(std::max(movedPosition, m_leftBound), m_rightBound);
853       dx += newPosition - movedPosition;
854 
855       if (dx) {
856         Region *r;
857         QRectF newGeometry;
858         double newWidth, dxTemp = dx;
859         int i;
860 
861         // Propagate dx in left-adjacent regions
862         for (i = m_index; dx != 0 && i >= 0; --i) {
863           r           = getParentRegion()->childRegion(i);
864           newGeometry = r->getGeometry();
865 
866           // Right margin is shifted by dx
867           newGeometry.adjust(0, 0, dx, 0);
868           newWidth = newGeometry.width();
869           // New width absorbs part of dx according to constraints
870           newWidth = std::min(
871               std::max(newWidth, (double)r->getMinimumSize(Region::horizontal)),
872               (double)r->getMaximumSize(Region::horizontal));
873           newGeometry.adjust(dx = newGeometry.width() - newWidth, 0, 0, 0);
874           r->setGeometry(newGeometry);
875           r->redistribute();
876         }
877 
878         dx       = dxTemp;
879         int size = getParentRegion()->getChildList().size();
880         // Propagate dx in right-adjacent regions
881         for (i = m_index + 1; dx != 0 && i < size; ++i) {
882           r           = getParentRegion()->childRegion(i);
883           newGeometry = r->getGeometry();
884 
885           newGeometry.adjust(dx, 0, 0, 0);
886           newWidth = newGeometry.width();
887           newWidth = std::min(
888               std::max(newWidth, (double)r->getMinimumSize(Region::horizontal)),
889               (double)r->getMaximumSize(Region::horizontal));
890           newGeometry.adjust(0, 0, dx = newWidth - newGeometry.width(), 0);
891           r->setGeometry(newGeometry);
892           r->redistribute();
893         }
894 
895         m_owner->update();
896       }
897     } else {
898       double dy = me->globalY() - m_oldPos.y();
899       movedPosition =
900           getParentRegion()->childRegion(m_index)->getGeometry().bottom() + dy;
901       newPosition =
902           std::min(std::max(movedPosition, m_leftBound), m_rightBound);
903       dy += newPosition - movedPosition;
904 
905       if (dy) {
906         Region *r;
907         QRectF newGeometry;
908         double newHeight, dyTemp = dy;
909         int i;
910 
911         for (i = m_index; dy != 0 && i >= 0; --i) {
912           r                  = getParentRegion()->childRegion(i);
913           newGeometry        = r->getGeometry();
914           QRectF oldGeometry = newGeometry;
915 
916           newGeometry.adjust(0, 0, 0, dy);
917           newHeight = newGeometry.height();
918           newHeight = std::min(
919               std::max(newHeight, (double)r->getMinimumSize(Region::vertical)),
920               (double)r->getMaximumSize(Region::vertical));
921           newGeometry.adjust(0, dy = newGeometry.height() - newHeight, 0, 0);
922           r->setGeometry(newGeometry);
923           r->redistribute();
924         }
925 
926         dy       = dyTemp;
927         int size = getParentRegion()->getChildList().size();
928         for (i = m_index + 1; dy != 0 && i < size; ++i) {
929           r           = getParentRegion()->childRegion(i);
930           newGeometry = r->getGeometry();
931 
932           newGeometry.adjust(0, dy, 0, 0);
933           newHeight = newGeometry.height();
934           newHeight = std::min(
935               std::max(newHeight, (double)r->getMinimumSize(Region::vertical)),
936               (double)r->getMaximumSize(Region::vertical));
937           newGeometry.adjust(0, 0, 0, dy = newHeight - newGeometry.height());
938           r->setGeometry(newGeometry);
939           r->redistribute();
940         }
941 
942         m_owner->update();
943       }
944     }
945 
946     m_oldPos = QPoint(me->globalX(), me->globalY());
947   }
948 }
949 
950 //========================================================
951 
952 //----------------------------------
953 //    Available geometries code
954 //----------------------------------
955 
956 namespace {
957 
958 //-------------------------------------
959 
960 // Finds the closest mouse point belonging to some available geometry.
getClosestAvailableMousePosition(QPoint & globalPos)961 void getClosestAvailableMousePosition(QPoint &globalPos) {
962   // Search the screen rect containing the mouse position
963   int i, screens = desktop->numScreens();
964   for (i = 0; i < screens; ++i)
965     if (desktop->screenGeometry(i).contains(globalPos)) break;
966 
967   // Find the closest point to the corresponding available region
968   QRect rect(desktop->availableGeometry(i));
969   if (rect.contains(globalPos)) return;
970 
971   // Return the closest point to the available geometry
972   QPoint result;
973   if (globalPos.x() < rect.left())
974     globalPos.setX(rect.left());
975   else if (globalPos.x() > rect.right())
976     globalPos.setX(rect.right());
977 
978   if (globalPos.y() < rect.top())
979     globalPos.setY(rect.top());
980   else if (globalPos.y() > rect.bottom())
981     globalPos.setY(rect.bottom());
982 }
983 
984 }  // Local namespace
985