2
@author Shin'ichiro Nakaoka
5
#include "MainWindow.h"
9
#include "ToolBarArea.h"
11
#include "MenuManager.h"
12
#include "AppConfig.h"
13
#include <cnoid/YamlNodes>
17
#include <QMouseEvent>
18
#include <QApplication>
20
#include <QRubberBand>
21
#include <QFileDialog>
22
#include <boost/bind.hpp>
23
#include <boost/tuple/tuple.hpp>
24
#include <boost/filesystem.hpp>
32
using namespace boost;
33
using namespace cnoid;
37
const bool TRACE_FUNCTIONS = false;
39
MainWindow* mainWindow = 0;
41
enum DropArea { OVER = -1, LEFT = 0, TOP, RIGHT, BOTTOM, NUM_DROP_AREAS };
42
const int SPLIT_DISTANCE_THRESHOLD = 35;
44
class TabWidget : public QTabWidget
47
TabWidget(MainWindowImpl* mwi, QWidget* parent = 0);
49
int addView(View* view) {
50
return addTab(view, view->windowTitle());
53
QTabBar* tabBar() const { return QTabWidget::tabBar(); }
54
virtual bool eventFilter(QObject* object, QEvent* event);
56
// For the whole tab widget dragging
57
//virtual void mousePressEvent(QMouseEvent *event);
58
//virtual void mouseMoveEvent(QMouseEvent *event);
59
//virtual void mouseReleaseEvent(QMouseEvent *event);
67
class MainWindowImpl //: public boost::signals::trackable
72
MainWindowImpl(MainWindow* self, const char* appName, ExtensionManager* ext);
75
void setupMenus(ExtensionManager* ext);
76
void createDefaultPanes();
78
bool addView(const std::string& pluginName, View* view);
79
bool removeView(View* view);
82
void onFullScreenToggled(bool on);
83
void resizeEvent(QResizeEvent* event);
84
void keyPressEvent(QKeyEvent* event);
86
bool viewTabMousePressEvent(TabWidget* pane, QMouseEvent* event);
87
bool viewTabMouseMoveEvent(TabWidget* pane, QMouseEvent* event);
88
bool viewTabMouseReleaseEvent(TabWidget* pane, QMouseEvent *event);
90
void startViewDrag(View* view);
91
void dragView(QMouseEvent* event);
92
void dragViewInsidePane(const QPoint& posInDestPane);
93
void dropViewInsidePane();
94
void dragViewOutsidePane();
95
void dropViewOutsidePane();
96
void removePaneIfEmpty(TabWidget* pane);
98
void clearAllPanesSub(QSplitter* splitter);
99
void clearEmptyPanes();
102
void onSaveLayoutAs();
103
void storeLayout(YamlMappingPtr layout);
104
YamlMapping* storeSplitterState(QSplitter* splitter);
105
YamlMapping* storePaneState(TabWidget* pane);
107
void onLoadLayoutAs();
108
void restoreLayout(const YamlMappingPtr layout);
109
QWidget* restoreSplitterState(const YamlMapping& state, TabWidget*& out_firstPane);
110
TabWidget* restorePaneState(const YamlMapping& state);
111
void restoreDefaultLayout();
113
std::vector<View*> views;
115
typedef multimap<QString, View*> NameToViewMap;
116
NameToViewMap nameToViewMap;
117
typedef set<View*> ViewSet;
120
std::vector<ToolBar*> toolBars;
122
TabWidget* areaToPane[View::NUM_AREAS];
124
struct AreaDetectionInfo {
125
AreaDetectionInfo() {
126
for(int i=0; i < View::NUM_AREAS; ++i){
131
int scores[View::NUM_AREAS];
134
typedef bitset<NUM_DROP_AREAS> EdgeContactState;
136
ToolBarArea* toolBarArea;
138
QWidget* centralWidget;
139
QVBoxLayout* centralVBox;
140
QSplitter* topSplitter;
142
YamlMappingPtr config;
143
YamlMappingPtr initialLayout;
145
bool isBeforeShowing;
146
bool isBeforeDoingInitialLayout;
148
bool isMaximizedJustBeforeFullScreen;
150
QSize normalStateSize;
152
QString currentLayoutFolder;
154
Action* fullScreenCheck;
156
QPoint tabDragStartPosition;
158
TabWidget* dragSrcPane;
159
TabWidget* dragDestPane;
160
bool isViewDraggingOutsidePane;
162
QRubberBand* rubberBand;
167
TabWidget::TabWidget(MainWindowImpl* mwi, QWidget* parent)
168
: QTabWidget(parent),
172
setUsesScrollButtons(true);
173
tabBar()->installEventFilter(this);
177
bool TabWidget::eventFilter(QObject* object, QEvent* event)
179
if(object == tabBar()){
180
switch(event->type()){
181
case QEvent::MouseButtonPress:
182
return mwi->viewTabMousePressEvent(this, static_cast<QMouseEvent*>(event));
183
case QEvent::MouseButtonDblClick:
185
case QEvent::MouseButtonRelease:
186
return mwi->viewTabMouseReleaseEvent(this, static_cast<QMouseEvent*>(event));
187
case QEvent::MouseMove:
188
return mwi->viewTabMouseMoveEvent(this, static_cast<QMouseEvent*>(event));
197
void MainWindow::initialize(const char* appName, ExtensionManager* ext)
200
new MainWindow(appName, ext);
205
MainWindow* MainWindow::instance()
211
MainWindow::MainWindow(const char* appName, ExtensionManager* ext)
215
setWindowTitle(appName);
216
setFocusPolicy(Qt::WheelFocus);
218
impl = new MainWindowImpl(this, appName, ext);
222
MainWindowImpl::MainWindowImpl(MainWindow* self, const char* appName, ExtensionManager* ext)
225
isBeforeDoingInitialLayout = true;
227
isViewDragging = false;
229
centralWidget = new QWidget(self);
231
centralVBox = new QVBoxLayout(centralWidget);
232
centralVBox->setSpacing(0);
233
centralVBox->setContentsMargins(0, 0, 0, 0);
235
toolBarArea = new ToolBarArea(centralWidget);
236
centralVBox->addWidget(toolBarArea);
238
//centralVBox->addSpacing(2);
240
topSplitter = new QSplitter(centralWidget);
241
centralVBox->addWidget(topSplitter, 1);
243
self->setCentralWidget(centralWidget);
245
rubberBand = new QRubberBand(QRubberBand::Rectangle, centralWidget);
248
config = AppConfig::archive()->openMapping("MainWindow");
250
createDefaultPanes();
252
self->setStatusBar(InfoBar::instance());
256
initialLayout = config;
257
toolBarArea->setInitialLayout(config);
259
isBeforeShowing = true;
262
cout << "size = (" << self->width() << ", " << self->height() << ")" << endl;
265
normalStateSize.setWidth(config->get("width", self->width()));
266
normalStateSize.setHeight(config->get("height", self->width()));
270
MainWindow::~MainWindow()
279
MainWindowImpl::~MainWindowImpl()
281
config->write("fullScreen", self->isFullScreen());
282
config->write("maximized", isMaximized);
283
config->write("width", normalStateSize.width());
284
config->write("height", normalStateSize.height());
288
void MainWindowImpl::setupMenus(ExtensionManager* ext)
290
MenuManager& mm = ext->menuManager();
292
mm.setPath("/" N_("File")).setBackwardMode().addItem(_("Exit"))
293
->sigTriggered().connect(bind(&MainWindow::close, self));
295
mm.setPath("/" N_("Edit"));
297
mm.setPath("/" N_("View"));
299
fullScreenCheck = mm.addCheckItem(_("Full Screen"));
300
fullScreenCheck->setChecked(config->get("fullScreen", false));
301
fullScreenCheck->sigToggled().connect(bind(&MainWindowImpl::onFullScreenToggled, this, _1));
303
mm.setPath("/View").setPath(N_("Layout"));
305
mm.addItem(_("Save Default Layout"))
306
->sigTriggered().connect(bind(&MainWindowImpl::onSaveLayout, this));
307
mm.addItem(_("Load Default Layout"))
308
->sigTriggered().connect(bind(&MainWindowImpl::onLoadLayout, this));
309
mm.addItem(_("Save Layout As"))
310
->sigTriggered().connect(bind(&MainWindowImpl::onSaveLayoutAs, this));
311
mm.addItem(_("Load Layout As"))
312
->sigTriggered().connect(bind(&MainWindowImpl::onLoadLayoutAs, this));
314
mm.setPath("/" N_("Tools"));
315
mm.setPath("/" N_("Filters"));
316
mm.setPath("/" N_("Options"));
317
mm.setPath("/").setBackwardMode().setPath(N_("Help"));
321
void MainWindowImpl::createDefaultPanes()
323
topSplitter->setOrientation(Qt::Horizontal);
325
QSplitter* vSplitter0 = new QSplitter(Qt::Vertical, topSplitter);
326
topSplitter->addWidget(vSplitter0);
328
areaToPane[View::LEFT_TOP] = new TabWidget(this, vSplitter0);
329
vSplitter0->addWidget(areaToPane[View::LEFT_TOP]);
331
areaToPane[View::LEFT_BOTTOM] = new TabWidget(this, vSplitter0);
332
vSplitter0->addWidget(areaToPane[View::LEFT_BOTTOM]);
334
QSplitter* vSplitter1 = new QSplitter(Qt::Vertical, topSplitter);
335
topSplitter->addWidget(vSplitter1);
337
QSplitter* hSplitter1 = new QSplitter(Qt::Horizontal, vSplitter1);
338
vSplitter1->addWidget(hSplitter1);
340
areaToPane[View::BOTTOM] = new TabWidget(this, vSplitter1);
341
vSplitter1->addWidget(areaToPane[View::BOTTOM]);
343
areaToPane[View::CENTER] = new TabWidget(this, hSplitter1);
344
hSplitter1->addWidget(areaToPane[View::CENTER]);
346
areaToPane[View::RIGHT] = new TabWidget(this, hSplitter1);
347
hSplitter1->addWidget(areaToPane[View::RIGHT]);
351
topSplitter->setSizes(sizes);
353
vSplitter0->setSizes(sizes);
355
vSplitter1->setSizes(sizes);
357
hSplitter1->setSizes(sizes);
362
void MainWindowImpl::detectExistingPaneAreas()
364
for(int i=0; i < View::NUM_AREAS; ++i){
368
vector<AreaDetectionInfo> infos;
369
EdgeContactState edge;
372
updateAreaDetectionInfos(topSplitter, edge, infos);
375
createDefaultPanes();
378
areaToPane[View::CENTER] = extractBestAreaMatchPane(infos, View::CENTER);
379
areaToPane[View::LEFT] = extractBestAreaMatchPane(infos, View::LEFT);
380
areaToPane[View::RIGHT] = extractBestAreaMatchPane(infos, View::RIGHT);
381
areaToPane[View::BOTTOM] = extractBestAreaMatchPane(infos, View::BOTTOM);
386
void MainWindowImpl::updateAreaDetectionInfos
387
(QSplitter* splitter, const EdgeContactState& edge, vector<AreaDetectionInfo>& infos)
389
QWidget* childWidgets[2];
390
childWidgets[0] = splitter->get_child1();
391
childWidgets[1] = splitter->get_child2();
392
bool isSingle = !(childWidgets[0] && childWidgets[1]);
394
for(int i=0; i < 2; ++i){
395
EdgeContactState currentEdge(edge);
397
if(dynamic_cast<HSplitter*>(splitter)){
398
currentEdge.reset((i == 0) ? BOTTOM : TOP);
400
currentEdge.reset((i == 0) ? RIGHT : LEFT);
404
Splitter* childSplitter = dynamic_cast<Splitter*>(childWidgets[i]);
406
updateAreaDetectionInfos(childSplitter, currentEdge, infos);
408
Pane* pane = dynamic_cast<Pane*>(childWidgets[i]);
410
AreaDetectionInfo info;
413
// calculate scores for area matching
414
static const int offset = 100000000;
415
int width = pane->get_width();
416
int height = pane->get_height();
418
info.scores[View::CENTER] = (4 - currentEdge.count()) * offset + width * height;
420
if(currentEdge.test(LEFT) && !currentEdge.test(RIGHT)){
421
info.scores[View::LEFT] = offset + height;
423
if(currentEdge.test(RIGHT) && !currentEdge.test(LEFT)){
424
info.scores[View::RIGHT] = offset + height;
426
if(currentEdge.test(BOTTOM) && !currentEdge.test(TOP)){
427
info.scores[View::BOTTOM] = offset + width;
430
infos.push_back(info);
438
Pane* MainWindowImpl::extractBestAreaMatchPane(vector<AreaDetectionInfo>& infos, View::LayoutArea area)
443
for(int i=0; i < (signed)infos.size(); ++i){
444
int s = infos[i].scores[area];
454
pane = infos[topIndex].pane;
455
infos.erase(infos.begin() + topIndex);
463
bool MainWindow::addView(const std::string& pluginName, View* view)
465
return impl->addView(pluginName, view);
469
bool MainWindowImpl::addView(const std::string& pluginName, View* view)
471
if(view->isManagedByMainWindow){
474
view->isManagedByMainWindow = true;
476
areaToPane[view->defaultLayoutArea()]->addView(view);
477
views.push_back(view);
478
nameToViewMap.insert(make_pair(view->name(), view));
484
bool MainWindow::removeView(View* view)
486
return impl->removeView(view);
490
bool MainWindowImpl::removeView(View* view)
492
bool removed = false;
494
if(view && view->isManagedByMainWindow){
495
std::remove(views.begin(), views.end(), view);
497
TabWidget* tab = dynamic_cast<TabWidget*>(view->parentWidget());
499
tab->removeTab(tab->indexOf(view));
501
view->isManagedByMainWindow = false;
510
void MainWindow::addToolBar(ToolBar* toolbar)
512
impl->toolBarArea->addToolBar(toolbar);
516
std::vector<View*> MainWindow::allViews()
522
std::vector<ToolBar*> MainWindow::allToolBars()
524
return impl->toolBarArea->getAllToolBars();
528
void MainWindow::setInitialLayout(const YamlMappingPtr layout)
531
cout << "MainWindow::setInitialLayout()" << endl;
533
if(impl->isBeforeDoingInitialLayout){
534
impl->initialLayout = layout;
535
impl->toolBarArea->setInitialLayout(layout);
540
void MainWindow::changeEvent(QEvent* event)
542
if(event->type() == QEvent::WindowStateChange){
544
cout << "MainWindow::changeEvent() of WindowStateChange: " << windowState() << endl;
546
if(!(windowState() & Qt::WindowFullScreen)){
547
impl->isMaximized = (windowState() & Qt::WindowMaximized);
553
void MainWindow::show()
559
void MainWindowImpl::showFirst()
562
cout << "MainWindowImpl::showFirst()" << endl;
566
//self->resize(normalStateSize);
568
if(config->get("fullScreen", false)){
569
isMaximizedJustBeforeFullScreen = isMaximized;
570
self->showFullScreen();
571
} else if(config->get("maximized", true)){
572
self->showMaximized();
574
self->QMainWindow::show();
576
isBeforeShowing = false;
578
self->QMainWindow::show();
583
void MainWindowImpl::onFullScreenToggled(bool on)
586
if(!self->isFullScreen()){
587
isMaximizedJustBeforeFullScreen = isMaximized;
588
self->showFullScreen();
591
if(self->isFullScreen()){
592
if(isMaximizedJustBeforeFullScreen){
593
self->showMaximized();
602
void MainWindow::resizeEvent(QResizeEvent* event)
604
QMainWindow::resizeEvent(event);
605
impl->resizeEvent(event);
609
void MainWindowImpl::resizeEvent(QResizeEvent* event)
612
cout << "MainWindowImpl::resizeEvent(): size = (";
613
cout << event->size().width() << ", " << event->size().height() << ")";
614
cout << ", isMaximized = " << (self->windowState() & Qt::WindowMaximized) << ", " << isMaximized;
615
cout << ", isVisible = " << self->isVisible() << endl;
618
if(isBeforeDoingInitialLayout){
620
if(!(self->windowState() & (Qt::WindowMaximized | Qt::WindowFullScreen)) || self->isVisible()){
623
cout << "MainWindowImpl::resizeEvent(): initializeLayout" << endl;
627
restoreLayout(initialLayout);
629
restoreDefaultLayout();
632
isBeforeDoingInitialLayout = false;
636
if(!(self->windowState() &
637
(Qt::WindowMinimized | Qt::WindowMaximized | Qt::WindowFullScreen))){ // normal state ?
638
normalStateSize = self->size();
644
void MainWindow::keyPressEvent(QKeyEvent* event)
646
impl->keyPressEvent(event);
650
void MainWindowImpl::keyPressEvent(QKeyEvent* event)
652
switch(event->key()){
654
fullScreenCheck->toggle();
663
void MainWindowImpl::restoreDefaultLayout()
666
detectExistingPaneAreas();
668
for(ViewSet::iterator p = storedViews.begin(); p != storedViews.end(); ++p){
671
Pane* pane = areaToPane[view->defaultLayoutArea()];
673
pane = areaToPane[View::CENTER];
675
pane->appendView(view);
679
topSplitter->show_all();
684
void MainWindow::restoreLayout(const YamlMappingPtr layout)
686
impl->restoreLayout(layout);
690
void MainWindowImpl::onLoadLayout()
692
restoreLayout(config);
696
void MainWindowImpl::onLoadLayoutAs()
698
QFileDialog dialog(self);
699
dialog.setWindowTitle(_("Open a layout"));
700
dialog.setFileMode(QFileDialog::ExistingFile);
701
dialog.setViewMode(QFileDialog::List);
702
dialog.setLabelText(QFileDialog::Accept, _("Open"));
703
dialog.setLabelText(QFileDialog::Reject, _("Cancel"));
706
filters << _("Layout files (*.conf)");
707
filters << _("Any files (*)");
708
dialog.setNameFilters(filters);
710
if(!currentLayoutFolder.isEmpty()){
711
dialog.setDirectory(currentLayoutFolder);
714
currentLayoutFolder = dialog.directory().absolutePath();
715
AppConfig::load(dialog.selectedFiles().front().toStdString());
716
config = AppConfig::archive()->openMapping("MainWindow");
717
restoreLayout(config);
722
void MainWindowImpl::restoreLayout(const YamlMappingPtr layout)
725
cout << "MainWindowImpl::restoreLayout()" << endl;
729
if(isBeforeDoingInitialLayout){
730
self->setInitialLayout(layout);
735
if(!isBeforeDoingInitialLayout){
736
toolBarArea->restoreLayout(layout);
738
toolBarArea->doInitialLayout();
741
const YamlMappingPtr layoutOfViews = layout->findMapping("layoutOfViews");
743
if(layoutOfViews->isValid()){
747
TabWidget* firstPane = 0;
749
QWidget* restoredWidget = restoreSplitterState(*layoutOfViews, firstPane);
751
topSplitter = dynamic_cast<QSplitter*>(restoredWidget);
754
topSplitter = new QSplitter();
755
firstPane = dynamic_cast<TabWidget*>(restoredWidget);
757
firstPane = new TabWidget(this);
759
topSplitter->addWidget(firstPane);
761
centralVBox->addWidget(topSplitter, 1);
763
for(ViewSet::iterator p = storedViews.begin(); p != storedViews.end(); ++p){
764
firstPane->addView(*p);
771
void MainWindowImpl::clearAllPanes()
774
cout << "clearAllPanes" << endl;
776
clearAllPanesSub(topSplitter);
780
cout << "End of clearAllPanes" << endl;
785
void MainWindowImpl::clearAllPanesSub(QSplitter* splitter)
788
cout << "clearAllPanesSub" << endl;
791
for(int i=0; i < splitter->count(); ++i){
792
QSplitter* childSplitter = dynamic_cast<QSplitter*>(splitter->widget(i));
794
clearAllPanesSub(childSplitter);
796
TabWidget* pane = dynamic_cast<TabWidget*>(splitter->widget(i));
798
for(int i=0; i < pane->count(); ++i){
799
View* view = dynamic_cast<View*>(pane->widget(i));
801
storedViews.insert(view);
804
while(pane->count() > 0){
805
int index = pane->count() - 1;
806
QWidget* view = pane->widget(index);
807
pane->removeTab(index);
809
view->setParent(centralWidget);
817
QWidget* MainWindowImpl::restoreSplitterState(const YamlMapping& state, TabWidget*& out_firstPane)
820
cout << "MainWindowImpl::restoreSplitterState" << endl;
823
QWidget* restoredWidget = 0;
824
QWidget* childWidgets[2] = { 0, 0 };
826
const YamlSequence& children = *state.findSequence("children");
827
if(children.isValid()){
828
int numChildren = std::min(children.size(), 2);
829
for(int i=0; i < numChildren; ++i){
830
if(children[i].isMapping()){
831
const YamlMapping& childState = *children[i].toMapping();
833
if(childState.read("type", type)){
834
if(type == "splitter"){
835
childWidgets[i] = restoreSplitterState(childState, out_firstPane);
836
} else if(type == "pane"){
837
TabWidget* pane = restorePaneState(childState);
839
childWidgets[i] = pane;
841
out_firstPane = pane;
849
if(childWidgets[0] && childWidgets[1]){
851
QSplitter* splitter = new QSplitter();
854
if(state.read("orientation", orientation)){
855
splitter->setOrientation((orientation == "vertical") ? Qt::Vertical : Qt::Horizontal);
858
splitter->addWidget(childWidgets[0]);
859
splitter->addWidget(childWidgets[1]);
861
const YamlSequence& sizes = *state.findSequence("sizes");
862
if(sizes.isValid() && sizes.size() == 2){
865
for(int i=0; i < 2; ++i){
866
if(sizes[i].read(size)){
870
splitter->setSizes(s);
872
restoredWidget = splitter;
875
for(int i=0; i < 2; ++i){
877
restoredWidget = childWidgets[i];
884
return restoredWidget;
888
TabWidget* MainWindowImpl::restorePaneState(const YamlMapping& state)
891
cout << "MainWindowImpl::restorePaneState" << endl;
895
const YamlSequence& viewNames = *state.findSequence("views");
897
if(viewNames.isValid() && !viewNames.empty()){
898
pane = new TabWidget(this);
899
QString currentViewName(state.get("current", "").c_str());
900
for(int i=0; i < viewNames.size(); ++i){
901
if(viewNames[i].isString()){
902
NameToViewMap::iterator p, upper_bound;
903
tie(p, upper_bound) = nameToViewMap.equal_range(viewNames[i].toString().c_str());
904
if(p != upper_bound){
905
View* view = p->second;
906
ViewSet::iterator q = storedViews.find(view);
907
if(q != storedViews.end()){
908
storedViews.erase(q);
910
if(view->name() == currentViewName){
911
pane->setCurrentIndex(i);
917
if(pane->count() == 0){
927
void MainWindow::storeLayout(YamlMappingPtr layout)
929
impl->storeLayout(layout);
934
void MainWindowImpl::onSaveLayout()
940
void MainWindowImpl::onSaveLayoutAs()
942
QFileDialog dialog(self);
943
dialog.setWindowTitle(_("Save a layout"));
944
dialog.setFileMode(QFileDialog::AnyFile);
945
dialog.setViewMode(QFileDialog::List);
946
dialog.setLabelText(QFileDialog::Accept, _("Save"));
947
dialog.setLabelText(QFileDialog::Reject, _("Cancel"));
950
filters << _("Layout files (*.conf)");
951
filters << _("Any files (*)");
952
dialog.setNameFilters(filters);
954
if(!currentLayoutFolder.isEmpty()){
955
dialog.setDirectory(currentLayoutFolder);
958
currentLayoutFolder = dialog.directory().absolutePath();
959
string filename(dialog.selectedFiles().front().toStdString());
960
string ext = filesystem::extension(filesystem::path(filename));
965
AppConfig::save(filename);
970
void MainWindowImpl::storeLayout(YamlMappingPtr layout)
973
layout->insert("layoutOfViews", storeSplitterState(topSplitter));
974
toolBarArea->storeLayout(layout);
976
catch(const YamlNode::Exception& ex){
977
cout << ex.message() << endl;
982
YamlMapping* MainWindowImpl::storeSplitterState(QSplitter* splitter)
984
YamlMapping* state = new YamlMapping();
986
state->write("type", "splitter");
988
if(splitter->count() == 2){
989
state->write("orientation", (splitter->orientation() == Qt::Vertical) ? "vertical" : "horizontal");
990
YamlSequence* sizeSeq = state->createSequence("sizes");
991
QList<int> sizes = splitter->sizes();
992
for(int i=0; i < sizes.size(); ++i){
993
sizeSeq->append(sizes[i]);
997
YamlSequence* children = state->createSequence("children");
999
for(int i=0; i < splitter->count(); ++i){
1000
QSplitter* childSplitter = dynamic_cast<QSplitter*>(splitter->widget(i));
1002
children->append(storeSplitterState(childSplitter));
1004
TabWidget* pane = dynamic_cast<TabWidget*>(splitter->widget(i));
1005
if(pane && pane->count() > 0){
1006
children->append(storePaneState(pane));
1015
YamlMapping* MainWindowImpl::storePaneState(TabWidget* pane)
1017
YamlMapping* state = new YamlMapping();
1019
state->write("type", "pane");
1021
YamlSequence* views = state->createFlowStyleSequence("views");
1022
const int n = pane->count();
1023
for(int i=0; i < n; ++i){
1024
View* view = dynamic_cast<View*>(pane->widget(i));
1026
const string name(view->name().toStdString());
1027
views->append(name, YAML_DOUBLE_QUOTED);
1028
if(i == pane->currentIndex()){
1029
state->write("current", name, YAML_DOUBLE_QUOTED);
1037
void MainWindowImpl::clearEmptyPanes()
1043
bool MainWindowImpl::viewTabMousePressEvent(TabWidget* pane, QMouseEvent* event)
1045
if(event->button() == Qt::LeftButton){
1046
tabDragStartPosition = event->pos();
1052
bool MainWindowImpl::viewTabMouseMoveEvent(TabWidget* pane, QMouseEvent* event)
1054
if(!isViewDragging){
1055
if(event->buttons() & Qt::LeftButton){
1056
if((event->pos() - tabDragStartPosition).manhattanLength() > QApplication::startDragDistance()){
1057
if(!pane->tabBar()->geometry().contains(event->pos())){
1058
View* view = dynamic_cast<View*>(pane->currentWidget());
1060
isViewDragging = true;
1062
startViewDrag(view);
1069
QWidget* pointed = topSplitter->childAt(topSplitter->mapFromGlobal(event->globalPos()));
1071
dragDestPane = dynamic_cast<TabWidget*>(pointed);
1076
pointed = pointed->parentWidget();
1086
bool MainWindowImpl::viewTabMouseReleaseEvent(TabWidget* pane, QMouseEvent *event)
1090
if(isViewDraggingOutsidePane){
1091
dropViewOutsidePane();
1093
dropViewInsidePane();
1096
QApplication::restoreOverrideCursor();
1098
isViewDragging = false;
1103
void MainWindowImpl::startViewDrag(View* view)
1105
QApplication::setOverrideCursor(Qt::ClosedHandCursor);
1109
void MainWindowImpl::dragView(QMouseEvent* event)
1113
QPoint p = topSplitter->mapFromGlobal(event->globalPos());
1114
const int w = topSplitter->width();
1115
const int h = topSplitter->height();
1118
distance[LEFT] = p.x();
1119
distance[TOP] = p.y();
1120
distance[RIGHT] = w - p.x();
1121
distance[BOTTOM] = h - p.y();
1123
for(int i=TOP; i <= BOTTOM; ++i){
1124
if(distance[dropEdge] > distance[i]){
1129
isViewDraggingOutsidePane = false;
1130
if(distance[dropEdge] < 8){
1131
isViewDraggingOutsidePane = true;
1132
dragViewOutsidePane();
1134
dragViewInsidePane(dragDestPane->mapFromGlobal(event->globalPos()));
1139
void MainWindowImpl::dragViewInsidePane(const QPoint& posInDestPane)
1143
const int w = dragDestPane->width();
1144
const int h = dragDestPane->height();
1147
distance[LEFT] = posInDestPane.x();
1148
distance[TOP] = posInDestPane.y();
1149
distance[RIGHT] = w - posInDestPane.x();
1150
distance[BOTTOM] = h - posInDestPane.y();
1152
for(int i=TOP; i <= BOTTOM; ++i){
1153
if(distance[dropEdge] > distance[i]){
1159
if(SPLIT_DISTANCE_THRESHOLD < distance[dropEdge]){
1160
r.setRect(0, 0, w, h);
1162
} else if(dropEdge == LEFT){
1163
r.setRect(0, 0, w / 2, h);
1164
} else if(dropEdge == TOP){
1165
r.setRect(0, 0, w, h /2);
1166
} else if(dropEdge == RIGHT){
1167
r.setRect(w / 2, 0, w / 2, h);
1168
} else if(dropEdge == BOTTOM){
1169
r.setRect(0, h / 2, w, h / 2);
1172
r.translate(centralWidget->mapFromGlobal(dragDestPane->mapToGlobal(QPoint(0, 0))));
1173
rubberBand->setGeometry(r);
1178
void MainWindowImpl::dropViewInsidePane()
1180
View* view = static_cast<View*>(dragSrcPane->currentWidget());
1181
if(dropEdge == OVER){
1182
int index = dragDestPane->addView(view);
1183
dragDestPane->setCurrentIndex(index);
1186
QSize destSize = dragDestPane->size();
1188
QSplitter* parentSplitter = static_cast<QSplitter*>(dragDestPane->parentWidget());
1189
QList<int> parentSizes = parentSplitter->sizes();
1190
QSplitter* newSplitter = new QSplitter(parentSplitter);
1191
parentSplitter->insertWidget(parentSplitter->indexOf(dragDestPane), newSplitter);
1192
TabWidget* newTabWidget = new TabWidget(this, newSplitter);
1194
if(dropEdge == LEFT){
1195
newSplitter->setOrientation(Qt::Horizontal);
1196
newSplitter->addWidget(newTabWidget);
1197
newSplitter->addWidget(dragDestPane);
1198
} else if(dropEdge == RIGHT){
1199
newSplitter->setOrientation(Qt::Horizontal);
1200
newSplitter->addWidget(dragDestPane);
1201
newSplitter->addWidget(newTabWidget);
1202
} else if(dropEdge == TOP){
1203
newSplitter->setOrientation(Qt::Vertical);
1204
newSplitter->addWidget(newTabWidget);
1205
newSplitter->addWidget(dragDestPane);
1207
newSplitter->setOrientation(Qt::Vertical);
1208
newSplitter->addWidget(dragDestPane);
1209
newSplitter->addWidget(newTabWidget);
1211
newTabWidget->addView(view);
1214
if(newSplitter->orientation() == Qt::Horizontal){
1215
half = destSize.height() / 2;
1217
half = destSize.width() / 2;
1220
sizes << half << half;
1221
newSplitter->setSizes(sizes);
1223
parentSplitter->setSizes(parentSizes);
1225
if(dragSrcPane->count() > 0){
1226
dragSrcPane->setCurrentIndex(0);
1228
removePaneIfEmpty(dragSrcPane);
1233
void MainWindowImpl::dragViewOutsidePane()
1236
int w = topSplitter->width();
1237
int h = topSplitter->height();
1238
if(dropEdge == LEFT){
1239
r.setRect(0, 0, w / 2, h);
1240
} else if(dropEdge == TOP){
1241
r.setRect(0, 0, w, h /2);
1242
} else if(dropEdge == RIGHT){
1243
r.setRect(w / 2, 0, w / 2, h);
1244
} else if(dropEdge == BOTTOM){
1245
r.setRect(0, h / 2, w, h / 2);
1247
r.translate(topSplitter->pos());
1248
rubberBand->setGeometry(r);
1253
void MainWindowImpl::dropViewOutsidePane()
1255
View* view = static_cast<View*>(dragSrcPane->currentWidget());
1257
QSize size = topSplitter->size();
1259
if(topSplitter->count() >= 2){
1260
QSplitter* newTopSplitter = new QSplitter(centralWidget);
1261
newTopSplitter->addWidget(topSplitter);
1262
topSplitter = newTopSplitter;
1263
centralVBox->addWidget(topSplitter, 1);
1265
TabWidget* newTabWidget = new TabWidget(this, topSplitter);
1267
if(dropEdge == LEFT){
1268
topSplitter->setOrientation(Qt::Horizontal);
1269
topSplitter->insertWidget(0, newTabWidget);
1270
} else if(dropEdge == RIGHT){
1271
topSplitter->setOrientation(Qt::Horizontal);
1272
topSplitter->addWidget(newTabWidget);
1273
} else if(dropEdge == TOP){
1274
topSplitter->setOrientation(Qt::Vertical);
1275
topSplitter->insertWidget(0, newTabWidget);
1277
topSplitter->setOrientation(Qt::Vertical);
1278
topSplitter->addWidget(newTabWidget);
1280
newTabWidget->addView(view);
1283
if(topSplitter->orientation() == Qt::Horizontal){
1284
half = size.height() / 2;
1286
half = size.width() / 2;
1289
sizes << half << half;
1290
topSplitter->setSizes(sizes);
1292
removePaneIfEmpty(dragSrcPane);
1297
void MainWindowImpl::removePaneIfEmpty(TabWidget* pane)
1299
if(pane->count() == 0){
1300
QSplitter* parentSplitter = dynamic_cast<QSplitter*>(pane->parentWidget());
1301
pane->deleteLater();
1303
if(parentSplitter->count() <= 1){
1304
QSplitter* grandParentSplitter = dynamic_cast<QSplitter*>(parentSplitter->parentWidget());
1305
if(grandParentSplitter){
1306
if(parentSplitter->count() == 1){
1307
int parentSplitterIndex = grandParentSplitter->indexOf(parentSplitter);
1308
grandParentSplitter->insertWidget(parentSplitterIndex, parentSplitter->widget(0));
1310
delete parentSplitter;