af58c61c3a059e6ac3a08958ab08171adb051fc5
[situare] / src / ui / tabbedpanel.cpp
1 /*
2     Situare - A location system for Facebook
3     Copyright (C) 2010  Ixonos Plc. Authors:
4
5         Kaj Wallin - kaj.wallin@ixonos.com
6         Pekka Nissinen - pekka.nissinen@ixonos.com
7
8     Situare is free software; you can redistribute it and/or
9     modify it under the terms of the GNU General Public License
10     version 2 as published by the Free Software Foundation.
11
12     Situare is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16
17     You should have received a copy of the GNU General Public License
18     along with Situare; if not, write to the Free Software
19     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
20     USA.
21 */
22
23 #include <QAbstractButton>
24 #include <QButtonGroup>
25 #include <QDebug>
26 #include <QPropertyAnimation>
27 #include <QRegion>
28 #include <QSignalTransition>
29 #include <QStackedWidget>
30 #include <QStateMachine>
31
32 #include "panelbar.h"
33 #include "panelbase.h"
34 #include "panelcontentstack.h"
35 #include "panelcontextbuttonbar.h"
36 #include "paneltabbar.h"
37
38 #include "tabbedpanel.h"
39
40 const int PANEL_CONTEXT_BUTTON_BAR_LEFT_X = 1;
41 const int PANEL_TAB_BAR_TOP_SPACING = 8;
42
43 TabbedPanel::TabbedPanel(QWidget *parent)
44     : QWidget(parent),
45       m_open(false),
46       m_closeRequestPending(false)
47 {
48     qDebug() << __PRETTY_FUNCTION__;
49
50     const int PANEL_LEFT_X = 0;
51     const int PANEL_TOP_Y = 0;
52
53     resize(PANEL_BAR_TABBED_WIDTH + PANEL_WIDTH, PANEL_HEIGHT);
54     move(PANEL_CLOSED_X, PANEL_TOP_PADDING);
55
56     // --- TABS ---
57     m_panelTabBar = new PanelTabBar(this);
58     m_panelTabBar->move(PANEL_LEFT_X, PANEL_TAB_BAR_TOP_SPACING);
59
60     connect(m_panelTabBar, SIGNAL(currentChanged(int)),
61             this, SLOT(setCurrentIndex(int)));
62
63     connect(m_panelTabBar, SIGNAL(sizeChangeRequested()),
64             this, SLOT(calculateMask()));
65
66     connect(m_panelTabBar, SIGNAL(tabCloseRequested(int)),
67              this, SLOT(closePanel()));
68
69     connect(this, SIGNAL(panelClosed()),
70             m_panelTabBar, SLOT(deselectTabs()));
71
72     // --- BAR ---
73     m_panelBar = new PanelBar(this);
74     m_panelBar->move(PANEL_TAB_BAR_WIDTH, PANEL_TOP_Y);
75
76     // --- CONTEXT BUTTON BAR ---
77     m_panelContextButtonBar = new PanelContextButtonBar(this);
78     m_panelContextButtonBar->move(PANEL_CONTEXT_BUTTON_BAR_LEFT_X, PANEL_HEIGHT);
79
80     connect(m_panelContextButtonBar, SIGNAL(barHidden()),
81             this, SLOT(closePanel()));
82
83     connect(m_panelContextButtonBar, SIGNAL(positionChangeRequested()),
84             this, SLOT(repositionContextButtonBar()));
85
86     // --- PANEL CONTENT ---
87     m_panelContentStack = new PanelContentStack(this);
88     m_panelContentStack->move(PANEL_TAB_BAR_WIDTH + PANEL_BAR_WIDTH, PANEL_TOP_Y);
89
90     // --- PANEL ANIMATION ---
91     QStateMachine *panelStateMachine = new QStateMachine(this);
92
93     m_stateClosed = new QState(panelStateMachine);
94     m_stateOpened = new QState(panelStateMachine);
95
96     QPropertyAnimation *panelAnimation = new QPropertyAnimation(this, "pos", this);
97
98     panelStateMachine->setInitialState(m_stateClosed);
99
100     QSignalTransition *panelTransitionOpen = m_stateClosed->addTransition(this,
101                                                                           SIGNAL(toggleState()),
102                                                                           m_stateOpened);
103     panelTransitionOpen->addAnimation(panelAnimation);
104
105     QSignalTransition *panelTransitionClose = m_stateOpened->addTransition(this,
106                                                                            SIGNAL(toggleState()),
107                                                                            m_stateClosed);
108     panelTransitionClose->addAnimation(panelAnimation);
109
110     connect(panelAnimation, SIGNAL(finished()),
111             this, SLOT(stateChanged()));
112
113     QPoint closedPosition(PANEL_CLOSED_X, PANEL_TOP_PADDING);
114     m_stateClosed->assignProperty(this, "pos", closedPosition);
115
116     QPoint openedPosition(PANEL_OPENED_X, PANEL_TOP_PADDING);
117     m_stateOpened->assignProperty(this, "pos", openedPosition);
118
119     panelStateMachine->start();
120 }
121
122 int TabbedPanel::addTab(QWidget *widget, const QIcon& icon)
123 {
124     qDebug() << __PRETTY_FUNCTION__;
125
126     const int APPEND_INDEX = -1;
127
128     return insertTab(APPEND_INDEX, widget, icon);
129 }
130
131 void TabbedPanel::calculateMask()
132 {
133     qDebug() << __PRETTY_FUNCTION__;
134
135     QRect panelTabBarRect = m_panelTabBar->rect();
136     QRect panelContextButtonBarRect = m_panelContextButtonBar->rect();
137     int panelContextButtonBarY = height() - panelContextButtonBarRect.height();
138
139     if (!m_open)
140         panelContextButtonBarY = height();
141
142     QRegion panelTabBarRegion(0, PANEL_TAB_BAR_TOP_SPACING,
143                          panelTabBarRect.width(), panelTabBarRect.height());
144     QRegion panelContextButtonBarRegion(0, panelContextButtonBarY,
145                                       panelContextButtonBarRect.width(),
146                                       panelContextButtonBarRect.height());
147     QRegion panelContentRegion(panelTabBarRect.right() + 1, 0,
148                                PANEL_WIDTH + PANEL_BAR_WIDTH, height());
149     QRegion panelRegion = panelTabBarRegion + panelContentRegion + panelContextButtonBarRegion;
150
151     setMask(panelRegion);
152 }
153
154 void TabbedPanel::closePanel()
155 {
156     qDebug() << __PRETTY_FUNCTION__;
157
158     if (m_open) {
159         m_open = false;
160
161         if (m_panelContextButtonBar->isBarVisible()) {
162             m_closeRequestPending = true;
163             m_panelContextButtonBar->hideContextButtonBar();
164         } else {
165             emit toggleState();
166         }
167     } else if (m_closeRequestPending) {
168         m_closeRequestPending = false;
169         emit toggleState();
170     }
171 }
172
173 int TabbedPanel::insertTab(int index, QWidget *widget, const QIcon& icon)
174 {
175     qDebug() << __PRETTY_FUNCTION__;
176
177     index = m_panelContentStack->insertWidget(index, widget);
178     m_panelTabBar->insertTab(index, icon);
179
180     return index;
181 }
182
183 void TabbedPanel::openPanel(QWidget *widget)
184 {
185     qDebug() << __PRETTY_FUNCTION__;
186
187     if (widget) {
188         m_panelTabBar->selectTab(m_panelContentStack->indexOf(widget));
189     } else if (!m_open) {
190         if (!m_closeRequestPending) {
191             m_open = true;
192             emit toggleState();
193         }
194     }
195 }
196
197 void TabbedPanel::removeTab(int index)
198 {
199     qDebug() << __PRETTY_FUNCTION__;
200
201     if (QWidget *widget = m_panelContentStack->widget(index)) {
202         m_panelContentStack->removeWidget(widget);
203         m_panelTabBar->removeTab(index);
204     }
205 }
206
207 void TabbedPanel::repositionContextButtonBar()
208 {
209     qDebug() << __PRETTY_FUNCTION__;
210
211     m_panelContextButtonBar->move(PANEL_CONTEXT_BUTTON_BAR_LEFT_X, height());
212     
213     calculateMask();
214 }
215
216 void TabbedPanel::resizePanel(const QSize &size)
217 {
218     qDebug() << __PRETTY_FUNCTION__;
219
220     resize(PANEL_BAR_TABBED_WIDTH + PANEL_WIDTH,
221            size.height() - PANEL_TOP_PADDING - PANEL_BOTTOM_PADDING);
222
223     if (!m_open)
224         move(size.width() - PANEL_TAB_BAR_WIDTH - PANEL_BAR_WIDTH, PANEL_TOP_PADDING);
225     else
226         move(size.width() - PANEL_TAB_BAR_WIDTH - PANEL_BAR_WIDTH - PANEL_WIDTH, PANEL_TOP_PADDING);
227
228     m_panelBar->resizeBar(size);
229
230     m_panelContextButtonBar->move(PANEL_CONTEXT_BUTTON_BAR_LEFT_X, size.height());
231
232     m_panelContentStack->resizeContentStack(size);
233
234     QPoint closedPosition(size.width() - PANEL_TAB_BAR_WIDTH - PANEL_BAR_WIDTH, PANEL_TOP_PADDING);
235     m_stateClosed->assignProperty(this, "pos", closedPosition);
236
237     QPoint openedPosition(size.width() - PANEL_TAB_BAR_WIDTH - PANEL_BAR_WIDTH - PANEL_WIDTH,
238                           PANEL_TOP_PADDING);
239     m_stateOpened->assignProperty(this, "pos", openedPosition);
240
241     calculateMask();
242 }
243
244 void TabbedPanel::setCurrentIndex(int index)
245 {
246     qDebug() << __PRETTY_FUNCTION__;
247
248     if ((index < m_panelContentStack->count()) && (index >= 0)) {
249         m_panelContentStack->setCurrentIndex(index);
250
251         if (!m_open)
252             openPanel();
253
254         m_panelContextButtonBar->setContextButtons(
255                 static_cast<PanelBase *>(m_panelContentStack->widget(index))->contextButtons());
256
257         emit currentChanged(index);
258     }
259 }
260
261 void TabbedPanel::setTabsEnabled(const QList<int> &tabIndexes, bool enabled)
262 {
263     qDebug() << __PRETTY_FUNCTION__;
264
265     QButtonGroup *tabs = m_panelTabBar->tabs();
266
267     foreach (int tabIndex, tabIndexes) {
268         QAbstractButton *tabButton = tabs->button(tabIndex);
269
270         if (tabButton) {
271             if (tabButton->isChecked())
272                 closePanel();
273
274             tabButton->setEnabled(enabled);
275         }
276     }
277 }
278
279 void TabbedPanel::stateChanged()
280 {
281     qDebug() << __PRETTY_FUNCTION__;
282
283     calculateMask();
284
285     if (m_open) {
286         m_panelContextButtonBar->showContextButtonBar();
287         emit panelOpened();
288     } else {
289         emit panelClosed();
290     }
291 }