We now have a current svn snapshot of libqxt in our contrib dir, and
[quassel.git] / src / contrib / libqxt-2007-10-24 / src / gui / qxttabwidget.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) Qxt Foundation. Some rights reserved.
4 **
5 ** This file is part of the QxtGui module of the Qt eXTension library
6 **
7 ** This library is free software; you can redistribute it and/or modify it
8 ** under the terms of th Common Public License, version 1.0, as published by
9 ** IBM.
10 **
11 ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
12 ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
13 ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
14 ** FITNESS FOR A PARTICULAR PURPOSE.
15 **
16 ** You should have received a copy of the CPL along with this file.
17 ** See the LICENSE file and the cpl1.0.txt file included with the source
18 ** distribution for more information. If you did not receive a copy of the
19 ** license, contact the Qxt Foundation.
20 **
21 ** <http://libqxt.sourceforge.net>  <foundation@libqxt.org>
22 **
23 ****************************************************************************/
24 #include "qxttabwidget.h"
25 #include <QContextMenuEvent>
26 #include <QApplication>
27 #include <QTabBar>
28 #include <QAction>
29 #include <QMenu>
30
31 typedef QList<QAction*> Actions;
32
33 class QxtTabWidgetPrivate : public QxtPrivate<QxtTabWidget>
34 {
35 public:
36     QXT_DECLARE_PUBLIC(QxtTabWidget);
37
38     QxtTabWidgetPrivate();
39     int tabIndexAt(const QPoint& pos) const;
40
41     QList<Actions> actions;
42     Qt::ContextMenuPolicy policy;
43 };
44
45 QxtTabWidgetPrivate::QxtTabWidgetPrivate() : policy(Qt::DefaultContextMenu)
46 {}
47
48 int QxtTabWidgetPrivate::tabIndexAt(const QPoint& pos) const
49 {
50     const int count = qxt_p().count();
51     const QTabBar* tabBar = qxt_p().tabBar();
52     for (int i = 0; i < count; ++i)
53         if (tabBar->tabRect(i).contains(pos))
54             return i;
55     return -1;
56 }
57
58 /*!
59     \class QxtTabWidget QxtTabWidget
60     \ingroup QxtGui
61     \brief An extended QTabWidget.
62
63     QxtTabWidget provides some convenience for handling tab specific context menus.
64
65     Example usage:
66     \code
67     QxtTabWidget* tabWidget = new QxtTabWidget();
68     tabWidget->addTab(tab0, "1");
69     tabWidget->addTab(tab1, "2");
70
71     QList<QAction*> actions0;
72     actions0 << new QAction("Quisque", tab0) << new QAction("Aenean", tab0);
73     QList<QAction*> actions1;
74     actions1 << new QAction("Phasellus", tab1) << new QAction("Maecenas", tab1);
75
76     tabWidget->setTabContextMenuPolicy(Qt::ActionsContextMenu);
77     tabWidget->addTabActions(0, actions0);
78     tabWidget->addTabActions(1, actions1);
79     \endcode
80
81     \image html qxttabwidget.png "QxtTabWidget in WindowsXP style."
82
83     \note http://www.trolltech.com/developer/task-tracker/index_html?method=entry&id=137891
84  */
85
86 /*!
87     \fn QxtTabWidget::tabContextMenuRequested(int index, const QPoint& globalPos)
88
89     This signal is emitted whenever the context menu is requested over
90     tab at \a index at \a globalPos.
91  */
92
93 /*!
94     Constructs a new QxtTabWidget with \a parent.
95  */
96 QxtTabWidget::QxtTabWidget(QWidget* parent) : QTabWidget(parent)
97 {
98     QXT_INIT_PRIVATE(QxtTabWidget);
99 }
100
101 /*!
102     Destructs the tab widget.
103  */
104 QxtTabWidget::~QxtTabWidget()
105 {}
106
107 /*!
108     \property QxtTabWidget::tabContextMenuPolicy
109     \brief This property holds how the tab specific context menus are handled.
110
111     The default value of this property is \b Qt::DefaultContextMenu,
112     which means that the tabContextMenuEvent() handler is called.
113     Other values are \b Qt::NoContextMenu, \b Qt::PreventContextMenu
114     (since Qt 4.2), \b Qt::ActionsContextMenu, and \b Qt::CustomContextMenu.
115     With \b Qt::CustomContextMenu, the signal tabContextMenuRequested() is
116     emitted.
117
118     \sa tabContextMenuEvent(), tabContextMenuRequested(), tabActions()
119  */
120 Qt::ContextMenuPolicy QxtTabWidget::tabContextMenuPolicy() const
121 {
122     return qxt_d().policy;
123 }
124
125 void QxtTabWidget::setTabContextMenuPolicy(Qt::ContextMenuPolicy policy)
126 {
127     qxt_d().policy = policy;
128 }
129
130 /*!
131     Appends the \a action to the list of actions of the
132     tab at \a index.
133
134     \sa removeTabAction(), insertTabAction(), tabActions()
135  */
136 void QxtTabWidget::addTabAction(int index, QAction* action)
137 {
138     insertTabAction(index, 0, action);
139 }
140
141 /*!
142     This convenience function creates a new action with \a text. The function
143     adds the newly created action to the list of actions of the tab at
144     \a index, and returns it.
145
146     \sa addTabAction()
147  */
148 QAction* QxtTabWidget::addTabAction(int index, const QString& text)
149 {
150     return addTabAction(index, QIcon(), text, 0, 0);
151 }
152
153 /*!
154     This convenience function creates a new action with \a icon and \a text.
155     The function adds the newly created action to the list of actions of the
156     tab at \a index, and returns it.
157
158     \sa addTabAction()
159  */
160 QAction* QxtTabWidget::addTabAction(int index, const QIcon& icon, const QString& text)
161 {
162     return addTabAction(index, icon, text, 0, 0);
163 }
164
165 /*!
166     This convenience function creates a new action with \a text and
167     an optional \a shortcut. The action's triggered() signal is
168     connected to the \a receiver's \a member slot. The function adds
169     the newly created action to the list of actions of the tab at
170     \a index, and returns it.
171
172     \note In order to make it possible for the shortcut to work even
173     when the context menu is not open, the action must be added to
174     a visible widget. The corresponding tab is a good alternative.
175
176     \code
177     QWidget* tab = createNewTab();
178     tabWidget->addTab(tab, title);
179     QAction* action = tabWidget->addTabAction(index, tr("Close"), this, SLOT(closeCurrentTab()), tr("Ctrl+W"));
180     tab->addAction(act);
181     \endcode
182
183     \sa addTabAction(), QWidget::addAction()
184  */
185 QAction* QxtTabWidget::addTabAction(int index, const QString& text, const QObject* receiver, const char* member, const QKeySequence& shortcut)
186 {
187     return addTabAction(index, QIcon(), text, receiver, member, shortcut);
188 }
189
190 /*!
191     This convenience function creates a new action with \a icon, \a text
192     and an optional \a shortcut. The action's triggered() signal is connected
193     to the \a receiver's \a member slot. The function adds the newly created
194     action to the list of actions of the tab at \a index, and returns it.
195
196     \sa addTabAction()
197  */
198 QAction* QxtTabWidget::addTabAction(int index, const QIcon& icon, const QString& text, const QObject* receiver, const char* member, const QKeySequence& shortcut)
199 {
200     QAction* action = new QAction(icon, text, this);
201     addTabAction(index, action);
202     if (receiver && member)
203         connect(action, SIGNAL(triggered()), receiver, member);
204     action->setShortcut(shortcut);
205     return action;
206 }
207
208 /*!
209     Appends the \a actions to the list of actions of the
210     tab at \a index.
211
212     \sa removeTabAction(), addTabAction()
213  */
214 void QxtTabWidget::addTabActions(int index, QList<QAction*> actions)
215 {
216     foreach (QAction* action, actions)
217     {
218         insertTabAction(index, 0, action);
219     }
220 }
221
222 /*!
223     Clears the list of actions of the tab at \a index.
224
225     \note Only actions owned by the tab widget are deleted.
226
227     \sa removeTabAction(), addTabAction()
228  */
229 void QxtTabWidget::clearTabActions(int index)
230 {
231     Q_ASSERT(index >= 0 && index < qxt_d().actions.count());
232
233     while (qxt_d().actions[index].count())
234     {
235         QAction* action = qxt_d().actions[index].last();
236         removeTabAction(index, action);
237         if (action->parent() == this)
238             delete action;
239     }
240 }
241
242 /*!
243     Inserts the \a action to the list of actions of the
244     tab at \a index, before the action \a before. It appends
245     the action if \a before is \c 0.
246
247     \sa removeTabAction(), addTabAction(), tabContextMenuPolicy, tabActions()
248  */
249 void QxtTabWidget::insertTabAction(int index, QAction* before, QAction* action)
250 {
251     Q_ASSERT(index >= 0 && index < qxt_d().actions.count());
252
253     if (!action)
254     {
255         qWarning("QxtTabWidget::insertTabAction: Attempt to insert a null action");
256         return;
257     }
258
259     const Actions& actions = qxt_d().actions.at(index);
260     if (actions.contains(action))
261         removeTabAction(index, action);
262
263     int pos = actions.indexOf(before);
264     if (pos < 0)
265     {
266         before = 0;
267         pos = actions.count();
268     }
269     qxt_d().actions[index].insert(pos, action);
270
271     QActionEvent e(QEvent::ActionAdded, action, before);
272     QApplication::sendEvent(this, &e);
273 }
274
275 /*!
276     Inserts the \a actions to the list of actions of the
277     tab at \a index, before the action \a before. It appends
278     the action if \a before is \c 0.
279
280     \sa removeAction(), QMenu, insertAction(), contextMenuPolicy
281  */
282 void QxtTabWidget::insertTabActions(int index, QAction* before, QList<QAction*> actions)
283 {
284     foreach (QAction* action, actions)
285     {
286         insertTabAction(index, before, action);
287     }
288 }
289
290 /*!
291     Removes the action \a action from the list of actions of the
292     tab at \a index.
293
294     \note The removed action is not deleted.
295
296     \sa insertTabAction(), tabActions(), insertTabAction()
297  */
298 void QxtTabWidget::removeTabAction(int index, QAction* action)
299 {
300     Q_ASSERT(index >= 0 && index < qxt_d().actions.count());
301
302     if (!action)
303     {
304         qWarning("QxtTabWidget::removeTabAction: Attempt to remove a null action");
305         return;
306     }
307
308     if (qxt_d().actions[index].removeAll(action))
309     {
310         QActionEvent e(QEvent::ActionRemoved, action);
311         QApplication::sendEvent(this, &e);
312     }
313 }
314
315 /*!
316     Returns the (possibly empty) list of actions for the
317     tab at \a index.
318
319     \sa tabContextMenuPolicy, insertTabAction(), removeTabAction()
320  */
321 QList<QAction*> QxtTabWidget::tabActions(int index) const
322 {
323     Q_ASSERT(index >= 0 && index < qxt_d().actions.count());
324     return qxt_d().actions.at(index);
325 }
326
327 void QxtTabWidget::tabInserted(int index)
328 {
329     Q_ASSERT(index >= 0 && index <= qxt_d().actions.count());
330     qxt_d().actions.insert(index, Actions());
331 }
332
333 void QxtTabWidget::tabRemoved(int index)
334 {
335     Q_ASSERT(index >= 0 && index < qxt_d().actions.count());
336     qxt_d().actions.removeAt(index);
337 }
338
339 void QxtTabWidget::contextMenuEvent(QContextMenuEvent* event)
340 {
341     const QPoint& pos = event->pos();
342     if (!tabBar()->geometry().contains(pos))
343         return QTabWidget::contextMenuEvent(event);
344
345     const int index = qxt_d().tabIndexAt(event->pos());
346     switch (qxt_d().policy)
347     {
348     case Qt::NoContextMenu:
349         event->ignore();
350         break;
351
352 #if QT_VERSION >= 0x040200
353     case Qt::PreventContextMenu:
354         event->accept();
355         break;
356 #endif // QT_VERSION
357
358     case Qt::ActionsContextMenu:
359         if (index != -1 && qxt_d().actions.at(index).count())
360         {
361             QMenu::exec(qxt_d().actions.at(index), event->globalPos());
362         }
363         break;
364
365     case Qt::CustomContextMenu:
366         if (index != -1)
367         {
368             emit tabContextMenuRequested(index, event->globalPos());
369         }
370         break;
371
372     case Qt::DefaultContextMenu:
373     default:
374         if (index != -1)
375         {
376             tabContextMenuEvent(index, event);
377         }
378         break;
379     }
380 }
381
382 /*!
383     This event handler, for event \a event, can be reimplemented in a
384     subclass to receive context menu events for the tab at \a index.
385
386     The handler is called when \b tabContextMenuPolicy is
387     \b Qt::DefaultContextMenu.
388
389     The default implementation ignores the context menu event.
390
391     \sa tabContextMenuPolicy, tabContextMenuRequested()
392  */
393 void QxtTabWidget::tabContextMenuEvent(int index, QContextMenuEvent* event)
394 {
395     Q_UNUSED(index);
396     event->ignore();
397 }