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 "MultiView.h"
18 #include "View.h"
19 #include "ViewWidget.h"
20 #include "Scene.h"
21 #include "Timeline.h"
22 #include "Global.h"
23 
24 #include <QKeyEvent>
25 #include <QtDebug>
26 #include <QSplitter>
27 #include <QVBoxLayout>
28 
29 // Splitter invariants:
30 //  1. For every View v, v->parentWidget() is a QSplitter, accessed via getParentSplitter(v)
31 //  2. For every View v, v is the only child of getParentSplitter(v)
32 //  3. For every QSplitter s, s has either:
33 //      a. One child, in which case it is a View, or
34 //      b. Two children, in which case both of them are QSplitters
35 
36 namespace
37 {
38 
viewWidgetFromView_(View * view)39 ViewWidget * viewWidgetFromView_(View * view)
40 {
41     return qobject_cast<ViewWidget*>(view->parentWidget());
42 }
43 
viewFromViewWidget_(ViewWidget * viewWidget)44 View * viewFromViewWidget_(ViewWidget * viewWidget)
45 {
46     return viewWidget->view();
47 }
48 
getParentSplitter_(QSplitter * w)49 QSplitter * getParentSplitter_(QSplitter * w)
50 {
51     return qobject_cast<QSplitter*>(w->parentWidget());
52 }
53 
getParentSplitter_(ViewWidget * w)54 QSplitter * getParentSplitter_(ViewWidget * w)
55 {
56     return qobject_cast<QSplitter*>(w->parentWidget());
57 }
58 
getParentSplitter_(View * w)59 QSplitter * getParentSplitter_(View * w)
60 {
61     return getParentSplitter_(viewWidgetFromView_(w));
62 }
63 
64 }
65 
MultiView(Scene * scene,QWidget * parent)66 MultiView::MultiView(Scene *scene, QWidget *parent) :
67     QWidget(parent),
68     views_(),
69     activeView_(0),
70     hoveredView_(0),
71     scene_(scene)
72 {
73     // create initial view and splitter
74     View * view = createView_();
75     QSplitter * splitter = new QSplitter();
76     splitter->addWidget(viewWidgetFromView_(view));
77 
78     // set layout
79     QVBoxLayout * layout = new QVBoxLayout();
80     layout->addWidget(splitter);
81     layout->setContentsMargins(0,0,0,0);
82     setLayout(layout);
83 
84     // Redraw views when necessary
85     // This line is necessary since some mouse cursor are drawn by VAC->draw()
86     // and depends on which view is hovered, if any
87     connect(this, SIGNAL(hoveredViewChanged()), this, SIGNAL(allViewsNeedToUpdate()));
88 }
89 
createView_()90 View * MultiView::createView_()
91 {
92     ViewWidget * viewWidget = new ViewWidget(scene_, this);
93     View * view = viewFromViewWidget_(viewWidget);
94     views_ << viewWidget;
95     setActiveView(view);
96     hoveredView_ = 0;
97     Timeline * timeline = global()->timeline();
98     if(timeline)
99     {
100         timeline->addView(view);
101     }
102 
103     connect(view, SIGNAL(allViewsNeedToUpdate()), this, SIGNAL(allViewsNeedToUpdate()));
104     connect(view, SIGNAL(allViewsNeedToUpdatePicking()), this, SIGNAL(allViewsNeedToUpdatePicking()));
105     connect(view, SIGNAL(mousePressed(GLWidget*)), this, SLOT(setActive(GLWidget*)));
106     connect(view, SIGNAL(mouseEntered(GLWidget*)), this, SLOT(setHovered(GLWidget*)));
107     connect(view, SIGNAL(mouseLeft(GLWidget*)), this, SLOT(unsetHovered(GLWidget*)));
108     connect(view, SIGNAL(mouseMoved(GLWidget*)), this, SLOT(setHovered(GLWidget*)));
109     connect(view, SIGNAL(viewIsBeingChanged(int,int)), this, SIGNAL(cameraChanged()));
110     connect(view, SIGNAL(viewChanged(int,int)), this, SIGNAL(cameraChanged()));
111     connect(view, SIGNAL(viewResized()), this, SIGNAL(cameraChanged()));
112     connect(view, SIGNAL(settingsChanged()), this, SIGNAL(settingsChanged()));
113 
114     return view;
115 }
116 
117 
deleteView_(View * view)118 void MultiView::deleteView_(View * view)
119 {
120     views_.removeAll(viewWidgetFromView_(view));
121     Timeline * timeline = global()->timeline();
122     if(timeline)
123     {
124         timeline->removeView(view);
125     }
126     if(activeView_ == view)
127     {
128         if(views_.size() > 0)
129             setActiveView(viewFromViewWidget_(views_[0]));
130         else
131             setActiveView(0);
132     }
133     if(hoveredView_ == view)
134     {
135         hoveredView_ = 0;
136     }
137 }
138 
setActiveView(View * view)139 void MultiView::setActiveView(View * view)
140 {
141     if(view && (activeView_!=view))
142     {
143         if(activeView_)
144             activeView_->setActive(false);
145 
146         activeView_ = view;
147         activeView_->setActive(true);
148 
149         emit activeViewChanged();
150     }
151 }
152 
setActive(GLWidget * w)153 void MultiView::setActive(GLWidget * w)
154 {
155     View * view =  qobject_cast<View*>(w);
156     setActiveView(view);
157 }
158 
setHovered(GLWidget * w)159 void MultiView::setHovered(GLWidget * w)
160 {
161     View * oldHoveredView =  hoveredView_;
162 
163     View * view =  qobject_cast<View*>(w);
164     if(view)
165     {
166         hoveredView_ = view;
167     }
168     else
169     {
170         hoveredView_ = 0;
171     }
172 
173     if(hoveredView_ != oldHoveredView)
174         emit hoveredViewChanged();
175 }
176 
unsetHovered(GLWidget *)177 void MultiView::unsetHovered(GLWidget * /*w*/)
178 {
179     View * oldHoveredView =  hoveredView_;
180 
181     hoveredView_ = 0;
182 
183     if(hoveredView_ != oldHoveredView)
184         emit hoveredViewChanged();
185 }
186 
187 
split_(View * view,Qt::Orientation orientation)188 void MultiView::split_(View * view, Qt::Orientation orientation)
189 {
190     // Schematic view of what happens
191     //   _________________________________
192     //  | splitterParent                  |
193     //  |  _____________________________  |
194     //  | | view                        | |
195     //  | |                             | |
196     //  | |                             | |
197     //  | |                             | |
198     //  | |                             | |
199     //  | |_____________________________| |
200     //  |_________________________________|
201     //
202     //                  []
203     //                 _[]_
204     //                 \  /
205     //                  \/
206     //   _________________________________
207     //  | splitterParent                  |
208     //  |  _____________   _____________  |
209     //  | | splitter1   | | splitter2   | |
210     //  | |  _________  | |  _________  | |
211     //  | | | view    | | | | view2   | | |
212     //  | | |         | | | |         | | |
213     //  | | |_________| | | |_________| | |
214     //  | |_____________| |_____________| |
215     //  |_________________________________|
216     //
217 
218     // Get current splitter and view
219     QSplitter * splitterParent = getParentSplitter_(view);
220     assert(splitterParent);
221 
222     // create view
223     View * view2 = createView_();
224 
225     // Create two new splitters
226     QSplitter * splitter1 = new QSplitter;
227     QSplitter * splitter2 = new QSplitter;
228 
229     // Change parent widgets
230     splitterParent->setOrientation(orientation);
231     splitterParent->addWidget(splitter1);
232     splitterParent->addWidget(splitter2);
233     splitter1->addWidget(viewWidgetFromView_(view)); // This also removes view from splitterParent
234     splitter2->addWidget(viewWidgetFromView_(view2));
235 }
236 
splitClose_(View * view)237 void MultiView::splitClose_(View * view)
238 {
239     assert( numViews() >= 2);
240 
241     // Schematic view of what happens
242     // Note: [*] is either 1 view, or 2 splitters
243     //   _________________________________
244     //  | splitterParent                  |
245     //  |  _____________   _____________  |
246     //  | | splitter1   | | splitter2   | |
247     //  | |  _________  | |  _________  | |
248     //  | | | view    | | | | *       | | |
249     //  | | |         | | | |         | | |
250     //  | | |_________| | | |_________| | |
251     //  | |_____________| |_____________| |
252     //  |_________________________________|
253     //
254     //                  []
255     //                 _[]_
256     //                 \  /
257     //                  \/
258     //   _________________________________
259     //  | splitterParent                  |
260     //  |  _____________________________  |
261     //  | | *                           | |
262     //  | |                             | |
263     //  | |                             | |
264     //  | |                             | |
265     //  | |                             | |
266     //  | |_____________________________| |
267     //  |_________________________________|
268     //
269 
270     // Get splitters
271     QSplitter * splitter1 = getParentSplitter_(view);
272     QSplitter * splitterParent = getParentSplitter_(splitter1);
273     assert(splitterParent->count() == 2);
274     QSplitter * splitter2 = qobject_cast<QSplitter*>(splitterParent->widget(0));
275     if(splitter2 == splitter1)
276         splitter2 = qobject_cast<QSplitter*>(splitterParent->widget(1));
277 
278     // Delete view and splitter1
279     // Caution: order is important, because deleteView_(view) does some
280     //          management before actually deleting view, and deleting
281     //          splitter1 would recursively delete view
282     deleteView_(view);
283     delete splitter1;
284 
285     // Save layout of splitter2
286     Qt::Orientation orientation = splitter2->orientation();
287     QList<int> sizes = splitter2->sizes();
288 
289     // Transfer children of splitter2 to splitterParent
290     QList<QWidget*> splitter2Children;
291     for(int i=0; i<splitter2->count(); ++i)
292         splitter2Children << splitter2->widget(i);
293     for(int i=0; i<splitter2Children.size(); ++i)
294         splitterParent->addWidget(splitter2Children[i]);
295 
296     // Delete splitter2
297     delete splitter2;
298 
299     // Transfer layout of splitter2 to splitterParent
300     splitterParent->setOrientation(orientation);
301     splitterParent->setSizes(sizes);
302 }
303 
304 
split_(Qt::Orientation orientation)305 void MultiView::split_(Qt::Orientation orientation)
306 {
307     // Get current splitter and view
308     View * view1 = activeView();
309     QSplitter * s = getParentSplitter_(view1);
310 
311     if(s)
312     {
313         // create view
314         View * view2 = createView_();
315 
316         // Create two new splitters
317         QSplitter * s1 = new QSplitter;
318         QSplitter * s2 = new QSplitter;
319 
320         // Change parent widgets
321         s->setOrientation(orientation);
322         s->addWidget(s1);
323         s->addWidget(s2);
324         s1->addWidget(viewWidgetFromView_(view1)); // This also removes view1 from s
325         s2->addWidget(viewWidgetFromView_(view2));
326     }
327 }
328 
splitVertical()329 void MultiView::splitVertical()
330 {
331     split_(Qt::Horizontal);
332 }
333 
splitHorizontal()334 void MultiView::splitHorizontal()
335 {
336     split_(Qt::Vertical);
337 }
338 
numViews() const339 int MultiView::numViews() const
340 {
341     return views_.size();
342 }
343 
splitClose()344 void MultiView::splitClose()
345 {
346     View * view = activeView();
347     if(view && numViews() >= 2)
348     {
349         splitClose_(view);
350     }
351 }
352 
splitOne()353 void MultiView::splitOne()
354 {
355     View * view = activeView();
356     if(view && numViews() >= 2)
357     {
358         QList<ViewWidget*> viewsToClose = views_;
359         viewsToClose.removeAll(viewWidgetFromView_(view));
360         for(int i=0; i<viewsToClose.size(); ++i)
361         {
362             splitClose_(viewFromViewWidget_(viewsToClose[i]));
363         }
364     }
365 }
366 
367 
368 
activeView() const369 View * MultiView::activeView() const
370 {
371     return activeView_;
372 }
373 
hoveredView() const374 View * MultiView::hoveredView() const
375 {
376     return hoveredView_;
377 }
378 
~MultiView()379 MultiView::~MultiView()
380 {
381 }
382 
update()383 void MultiView::update()
384 {
385     foreach(ViewWidget * viewWidget, views_)
386     {
387         View * view = viewFromViewWidget_(viewWidget);
388         if(view->isVisible())
389             view->update();
390     }
391 }
392 
updatePicking()393 void MultiView::updatePicking()
394 {
395     foreach(ViewWidget * viewWidget, views_)
396     {
397         View * view = viewFromViewWidget_(viewWidget);
398         if(view->isVisible())
399             view->updatePicking();
400     }
401 }
402 
zoomIn()403 void MultiView::zoomIn()
404 {
405     activeView()->zoomIn();
406 }
407 
zoomOut()408 void MultiView::zoomOut()
409 {
410     activeView()->zoomOut();
411 }
412 
fitAllInWindow()413 void MultiView::fitAllInWindow()
414 {
415     activeView()->fitAllInWindow();
416 }
417 
fitSelectionInWindow()418 void MultiView::fitSelectionInWindow()
419 {
420     activeView()->fitSelectionInWindow();
421 }
422 
keyPressEvent(QKeyEvent * event)423 void MultiView::keyPressEvent(QKeyEvent *event)
424 {
425     event->ignore();
426 }
427 
keyReleaseEvent(QKeyEvent * event)428 void MultiView::keyReleaseEvent(QKeyEvent *event)
429 {
430     event->ignore();
431 }
432 
433 // toChange
zoom() const434 double MultiView::zoom() const { return activeView()->camera2D().zoom(); }
435 
toggleOutline()436 void MultiView::toggleOutline()
437 {
438     activeView()->toggleOutline();
439 }
440 
toggleOutlineOnly()441 void MultiView::toggleOutlineOnly()
442 {
443     activeView()->toggleOutlineOnly();
444 }
setDisplayMode(ViewSettings::DisplayMode displayMode)445 void MultiView::setDisplayMode(ViewSettings::DisplayMode displayMode)
446 {
447     activeView()->setDisplayMode(displayMode);
448 }
449 
setOnionSkinningEnabled(bool enabled)450 void MultiView::setOnionSkinningEnabled(bool enabled)
451 {
452     activeView()->setOnionSkinningEnabled(enabled);
453 }
454