52155d098cff895e3787306dd5cc434e830d6d06
[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 "listitemcontextbuttonbar.h"
33 #include "panelbar.h"
34 #include "panelbase.h"
35 #include "panelcontentstack.h"
36 #include "panelcontextbuttonbar.h"
37 #include "paneltabbar.h"
38
39 #include "tabbedpanel.h"
40
41 const int PANEL_CONTEXT_BUTTON_BAR_LEFT_X = 1;
42 const int PANEL_TAB_BAR_TOP_SPACING = 8;
43
44 TabbedPanel::TabbedPanel(QWidget *parent)
45     : QWidget(parent),
46       m_open(false),
47       m_closeRequestPending(false)
48 {
49     qDebug() << __PRETTY_FUNCTION__;
50
51     const int PANEL_LEFT_X = 0;
52     const int PANEL_TOP_Y = 0;
53
54     resize(PANEL_BAR_TABBED_WIDTH + PANEL_WIDTH, PANEL_HEIGHT);
55     move(PANEL_CLOSED_X, PANEL_TOP_PADDING);
56
57     // --- TABS ---
58     m_panelTabBar = new PanelTabBar(this);
59     m_panelTabBar->move(PANEL_LEFT_X, PANEL_TAB_BAR_TOP_SPACING);
60
61     connect(m_panelTabBar, SIGNAL(currentChanged(int)),
62             this, SLOT(setCurrentIndex(int)));
63
64     connect(m_panelTabBar, SIGNAL(sizeChangeRequested()),
65             this, SLOT(calculateMask()));
66
67     connect(m_panelTabBar, SIGNAL(tabCloseRequested(int)),
68              this, SLOT(closePanel()));
69
70     connect(this, SIGNAL(panelClosed()),
71             m_panelTabBar, SLOT(deselectTabs()));
72
73     // --- BAR ---
74     m_panelBar = new PanelBar(this);
75     m_panelBar->move(PANEL_TAB_BAR_WIDTH, PANEL_TOP_Y);
76
77     // --- GENERIC PANEL CONTEXT BUTTON BAR ---
78     m_panelContextButtonBar = new PanelContextButtonBar(this);
79     m_panelContextButtonBar->move(PANEL_CONTEXT_BUTTON_BAR_LEFT_X, PANEL_HEIGHT);
80
81     connect(m_panelContextButtonBar, SIGNAL(barHidden()),
82             this, SLOT(closePanel()));
83
84     connect(m_panelContextButtonBar, SIGNAL(positionChangeRequested()),
85             this, SLOT(repositionContextButtonBar()));
86
87     // --- LIST ITEM RELATED CONTEXT BUTTONS BAR ---
88     m_itemContextButtonBar = new ListItemContextButtonBar(this);
89
90     // --- PANEL CONTENT ---
91     m_panelContentStack = new PanelContentStack(this);
92     m_panelContentStack->move(PANEL_TAB_BAR_WIDTH + PANEL_BAR_WIDTH, PANEL_TOP_Y);
93     m_panelContentStack->stackUnder(m_itemContextButtonBar);
94
95     // --- PANEL ANIMATION ---
96     QStateMachine *panelStateMachine = new QStateMachine(this);
97
98     m_stateClosed = new QState(panelStateMachine);
99     m_stateOpened = new QState(panelStateMachine);
100
101     QPropertyAnimation *panelAnimation = new QPropertyAnimation(this, "pos", this);
102
103     panelStateMachine->setInitialState(m_stateClosed);
104
105     QSignalTransition *panelTransitionOpen = m_stateClosed->addTransition(this,
106                                                                           SIGNAL(toggleState()),
107                                                                           m_stateOpened);
108     panelTransitionOpen->addAnimation(panelAnimation);
109
110     QSignalTransition *panelTransitionClose = m_stateOpened->addTransition(this,
111                                                                            SIGNAL(toggleState()),
112                                                                            m_stateClosed);
113     panelTransitionClose->addAnimation(panelAnimation);
114
115     connect(panelAnimation, SIGNAL(finished()),
116             this, SLOT(stateChanged()));
117
118     QPoint closedPosition(PANEL_CLOSED_X, PANEL_TOP_PADDING);
119     m_stateClosed->assignProperty(this, "pos", closedPosition);
120
121     QPoint openedPosition(PANEL_OPENED_X, PANEL_TOP_PADDING);
122     m_stateOpened->assignProperty(this, "pos", openedPosition);
123
124     panelStateMachine->start();
125 }
126
127 int TabbedPanel::addTab(QWidget *widget, const QIcon& icon)
128 {
129     qDebug() << __PRETTY_FUNCTION__;
130
131     const int APPEND_INDEX = -1;
132
133     return insertTab(APPEND_INDEX, widget, icon);
134 }
135
136 void TabbedPanel::calculateMask()
137 {
138     qDebug() << __PRETTY_FUNCTION__;
139
140     QRect panelTabBarRect = m_panelTabBar->rect();
141     QRect panelContextButtonBarRect = m_panelContextButtonBar->rect();
142     int panelContextButtonBarY = height() - panelContextButtonBarRect.height();
143
144     if (!m_open)
145         panelContextButtonBarY = height();
146
147     QRegion panelTabBarRegion(0, PANEL_TAB_BAR_TOP_SPACING,
148                          panelTabBarRect.width(), panelTabBarRect.height());
149     QRegion panelContextButtonBarRegion(0, panelContextButtonBarY,
150                                       panelContextButtonBarRect.width(),
151                                       panelContextButtonBarRect.height());
152     QRegion panelContentRegion(panelTabBarRect.right() + 1, 0,
153                                PANEL_WIDTH + PANEL_BAR_WIDTH, height());
154     QRegion panelRegion = panelTabBarRegion + panelContentRegion + panelContextButtonBarRegion;
155
156     setMask(panelRegion);
157 }
158
159 void TabbedPanel::closePanel()
160 {
161     qDebug() << __PRETTY_FUNCTION__;
162
163     if (m_open) {
164         m_open = false;
165
166         if (m_panelContextButtonBar->isBarVisible()) {
167             m_closeRequestPending = true;
168             m_panelContextButtonBar->hideContextButtonBar();
169         } else {
170             emit toggleState();
171         }
172     } else if (m_closeRequestPending) {
173         m_closeRequestPending = false;
174         emit toggleState();
175     }
176 }
177
178 int TabbedPanel::insertTab(int index, QWidget *widget, const QIcon& icon)
179 {
180     qDebug() << __PRETTY_FUNCTION__;
181
182     index = m_panelContentStack->insertWidget(index, widget);
183     m_panelTabBar->insertTab(index, icon);
184
185     return index;
186 }
187
188 void TabbedPanel::openPanel(QWidget *widget)
189 {
190     qDebug() << __PRETTY_FUNCTION__;
191
192     if (widget) {
193         m_panelTabBar->selectTab(m_panelContentStack->indexOf(widget));
194     } else if (!m_open) {
195         if (!m_closeRequestPending) {
196             m_open = true;
197             emit toggleState();
198         }
199     }
200 }
201
202 void TabbedPanel::removeTab(int index)
203 {
204     qDebug() << __PRETTY_FUNCTION__;
205
206     if (QWidget *widget = m_panelContentStack->widget(index)) {
207         m_panelContentStack->removeWidget(widget);
208         m_panelTabBar->removeTab(index);
209     }
210 }
211
212 void TabbedPanel::repositionContextButtonBar()
213 {
214     qDebug() << __PRETTY_FUNCTION__;
215
216     m_panelContextButtonBar->move(PANEL_CONTEXT_BUTTON_BAR_LEFT_X, height());
217
218     calculateMask();
219 }
220
221 void TabbedPanel::resizePanel(const QSize &size)
222 {
223     qDebug() << __PRETTY_FUNCTION__;
224
225     resize(PANEL_BAR_TABBED_WIDTH + PANEL_WIDTH,
226            size.height() - PANEL_TOP_PADDING - PANEL_BOTTOM_PADDING);
227
228     if (!m_open)
229         move(size.width() - PANEL_TAB_BAR_WIDTH - PANEL_BAR_WIDTH, PANEL_TOP_PADDING);
230     else
231         move(size.width() - PANEL_TAB_BAR_WIDTH - PANEL_BAR_WIDTH - PANEL_WIDTH, PANEL_TOP_PADDING);
232
233     m_panelBar->resizeBar(size);
234
235     m_panelContextButtonBar->move(PANEL_CONTEXT_BUTTON_BAR_LEFT_X, size.height());
236
237     m_panelContentStack->resizeContentStack(size);
238
239     QPoint closedPosition(size.width() - PANEL_TAB_BAR_WIDTH - PANEL_BAR_WIDTH, PANEL_TOP_PADDING);
240     m_stateClosed->assignProperty(this, "pos", closedPosition);
241
242     QPoint openedPosition(size.width() - PANEL_TAB_BAR_WIDTH - PANEL_BAR_WIDTH - PANEL_WIDTH,
243                           PANEL_TOP_PADDING);
244     m_stateOpened->assignProperty(this, "pos", openedPosition);
245
246     calculateMask();
247 }
248
249 void TabbedPanel::setCurrentIndex(int index)
250 {
251     qDebug() << __PRETTY_FUNCTION__;
252
253     if ((index < m_panelContentStack->count()) && (index >= 0)) {
254         m_panelContentStack->setCurrentIndex(index);
255
256         if (!m_open)
257             openPanel();
258
259         m_panelContextButtonBar->setContextButtons(
260                 static_cast<PanelBase *>(m_panelContentStack->widget(index))->genericPanelButtons());
261
262         QWidget *itemContextButtons = static_cast<PanelBase *>(m_panelContentStack->widget(index))->itemButtons();
263         m_itemContextButtonBar->setContextButtons(itemContextButtons);
264
265         const int FROM_PANEL_CONTENTS_LEFT = PANEL_WIDTH / 2 - itemContextButtons->width() / 2;
266         const int Y = 0;
267
268         m_itemContextButtonBar->move(PANEL_TAB_BAR_WIDTH + PANEL_BAR_WIDTH
269                                      + FROM_PANEL_CONTENTS_LEFT, Y);
270
271         emit currentChanged(index);
272     }
273 }
274
275 void TabbedPanel::setTabsEnabled(const QList<int> &tabIndexes, bool enabled)
276 {
277     qDebug() << __PRETTY_FUNCTION__;
278
279     QButtonGroup *tabs = m_panelTabBar->tabs();
280
281     foreach (int tabIndex, tabIndexes) {
282         QAbstractButton *tabButton = tabs->button(tabIndex);
283
284         if (tabButton) {
285             if (tabButton->isChecked())
286                 closePanel();
287
288             tabButton->setEnabled(enabled);
289         }
290     }
291 }
292
293 void TabbedPanel::stateChanged()
294 {
295     qDebug() << __PRETTY_FUNCTION__;
296
297     calculateMask();
298
299     if (m_open) {
300         m_panelContextButtonBar->showContextButtonBar();
301         emit panelOpened();
302     } else {
303         emit panelClosed();
304     }
305 }