We now have a current svn snapshot of libqxt in our contrib dir, and
[quassel.git] / src / contrib / libqxt-2007-10-24 / src / gui / qxtconfigdialog.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 "qxtconfigdialog.h"
25 #include "qxtconfigdialog_p.h"
26 #if QT_VERSION >= 0x040200
27 #include <QDialogButtonBox>
28 #else // QT_VERSION >= 0x040200
29 #include <QHBoxLayout>
30 #include <QPushButton>
31 #endif // QT_VERSION
32 #include <QStackedWidget>
33 #include <QGridLayout>
34 #include <QListWidget>
35 #include <QPainter>
36
37 QxtConfigListWidget::QxtConfigListWidget(QWidget* parent) : QListWidget(parent)
38 {
39     setItemDelegate(new QxtConfigDelegate(this));
40     viewport()->setAttribute(Qt::WA_Hover, true);
41 }
42
43 void QxtConfigListWidget::invalidate()
44 {
45     hint = QSize();
46     updateGeometry();
47 }
48
49 QSize QxtConfigListWidget::minimumSizeHint() const
50 {
51     return sizeHint();
52 }
53
54 QSize QxtConfigListWidget::sizeHint() const
55 {
56     if (!hint.isValid())
57     {
58         const QStyleOptionViewItem options = viewOptions();
59         const bool vertical = (flow() == QListView::TopToBottom);
60         for (int i = 0; i < count(); ++i)
61         {
62             const QSize size = itemDelegate()->sizeHint(options, model()->index(i, 0));
63             if (i != 0)
64                 hint = hint.expandedTo(size);
65             if (vertical)
66                 hint += QSize(0, size.height());
67             else
68                 hint += QSize(size.width(), 0);
69         }
70         hint += QSize(2 * frameWidth(), 2 * frameWidth());
71     }
72     return hint;
73 }
74
75 bool QxtConfigListWidget::hasHoverEffect() const
76 {
77     return static_cast<QxtConfigDelegate*>(itemDelegate())->hover;
78 }
79
80 void QxtConfigListWidget::setHoverEffect(bool enabled)
81 {
82     static_cast<QxtConfigDelegate*>(itemDelegate())->hover = enabled;
83 }
84
85 void QxtConfigListWidget::scrollContentsBy(int dx, int dy)
86 {
87     // prevent scrolling
88     Q_UNUSED(dx);
89     Q_UNUSED(dy);
90 }
91
92 QxtConfigDelegate::QxtConfigDelegate(QObject* parent)
93         : QItemDelegate(parent), hover(true)
94 {}
95
96 void QxtConfigDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
97 {
98     QStyleOptionViewItem opt = option;
99     if (hover)
100     {
101         QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled;
102         if (cg == QPalette::Normal && !(option.state & QStyle::State_Active))
103             cg = QPalette::Inactive;
104
105         if (option.state & QStyle::State_Selected)
106         {
107             painter->fillRect(option.rect, option.palette.brush(cg, QPalette::Highlight));
108         }
109         else if ((option.state & QStyle::State_MouseOver) && (option.state & QStyle::State_Enabled))
110         {
111             QColor color = option.palette.color(cg, QPalette::Highlight).light();
112             if (color == option.palette.color(cg, QPalette::Base))
113                 color = option.palette.color(cg, QPalette::AlternateBase);
114             painter->fillRect(option.rect, color);
115         }
116
117         opt.showDecorationSelected = false;
118         opt.state &= ~QStyle::State_HasFocus;
119     }
120     QItemDelegate::paint(painter, opt, index);
121 }
122
123 void QxtConfigDialogPrivate::init(QxtConfigDialog::IconPosition position)
124 {
125     QxtConfigDialog* p = &qxt_p();
126     grid = new QGridLayout(p);
127     list = new QxtConfigListWidget(p);
128     stack = new QStackedWidget(p);
129     pos = position;
130     QObject::connect(list, SIGNAL(currentRowChanged(int)), stack, SLOT(setCurrentIndex(int)));
131     QObject::connect(stack, SIGNAL(currentChanged(int)), p, SIGNAL(currentIndexChanged(int)));
132
133 #if QT_VERSION >= 0x040200
134     buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, p);
135     QObject::connect(buttons, SIGNAL(accepted()), p, SLOT(accept()));
136     QObject::connect(buttons, SIGNAL(rejected()), p, SLOT(reject()));
137 #else // QT_VERSION >= 0x040200
138     buttons = new QWidget(p);
139     QHBoxLayout* layout = new QHBoxLayout(buttons);
140     QPushButton* okButton = new QPushButton(QxtConfigDialog::tr("&OK"));
141     QPushButton* cancelButton = new QPushButton(QxtConfigDialog::tr("&Cancel"));
142     QObject::connect(okButton, SIGNAL(clicked()), p, SLOT(accept()));
143     QObject::connect(cancelButton, SIGNAL(clicked()), p, SLOT(reject()));
144     layout->addStretch();
145     layout->addWidget(okButton);
146     layout->addWidget(cancelButton);
147 #endif
148
149     initList();
150     relayout();
151 }
152
153 void QxtConfigDialogPrivate::initList()
154 {
155     // no scroll bars
156     list->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
157     list->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
158     // prevent editing
159     list->setEditTriggers(QAbstractItemView::NoEditTriggers);
160     // convenient navigation
161     list->setTabKeyNavigation(true);
162     // no dnd
163     list->setAcceptDrops(false);
164     list->setDragEnabled(false);
165     // list fine tuning
166     list->setMovement(QListView::Static);
167     list->setWrapping(false);
168     list->setResizeMode(QListView::Fixed);
169     list->setViewMode(QListView::IconMode);
170     // list->setWordWrap(false); 4.2
171     // list->setSortingEnabled(false); 4.2
172 }
173
174 void QxtConfigDialogPrivate::relayout()
175 {
176     // freeze
177     grid->setEnabled(false);
178
179     // clear
180     while (grid->takeAt(0));
181
182     // relayout
183     switch (pos)
184     {
185     case QxtConfigDialog::North:
186         // +-----------+
187         // |   Icons   |
188         // +-----------|
189         // |   Stack   |
190         // +-----------|
191         // |  Buttons  |
192         // +-----------+
193         grid->addWidget(list, 0, 0);
194         grid->addWidget(stack, 1, 0);
195         grid->addWidget(buttons, 3, 0);
196         break;
197
198     case QxtConfigDialog::West:
199         // +---+-------+
200         // | I |       |
201         // | c |       |
202         // | o | Stack |
203         // | n |       |
204         // | s |       |
205         // +---+-------+
206         // |  Buttons  |
207         // +-----------+
208         grid->addWidget(list, 0, 0);
209         grid->addWidget(stack, 0, 1);
210         grid->addWidget(buttons, 2, 0, 1, 2);
211         break;
212
213     case QxtConfigDialog::East:
214         // +-------+---+
215         // |       | I |
216         // |       | c |
217         // | Stack | o |
218         // |       | n |
219         // |       | s |
220         // +-------+---+
221         // |  Buttons  |
222         // +-----------+
223         grid->addWidget(stack, 0, 0);
224         grid->addWidget(list, 0, 1);
225         grid->addWidget(buttons, 2, 0, 1, 2);
226         break;
227
228     default:
229         qWarning("QxtConfigDialogPrivate::relayout(): unknown position");
230         break;
231     }
232
233     if (pos == QxtConfigDialog::North)
234     {
235         list->setFlow(QListView::LeftToRight);
236         list->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
237     }
238     else
239     {
240         list->setFlow(QListView::TopToBottom);
241         list->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
242     }
243     list->invalidate();
244
245     // defrost
246     grid->setEnabled(true);
247 }
248
249 /*!
250     \class QxtConfigDialog QxtConfigDialog
251     \ingroup QxtGui
252     \brief A configuration dialog.
253
254     QxtConfigDialog provides a convenient interface for building
255         common configuration dialogs. QxtConfigDialog consists of a
256         list of icons and a stack of pages.
257
258         Example usage:
259         \code
260         QxtConfigDialog dialog;
261     dialog.addPage(new ConfigurationPage(&dialog), QIcon(":/images/config.png"), tr("Configuration"));
262         dialog.addPage(new UpdatePage(&dialog), QIcon(":/images/update.png"), tr("Update"));
263         dialog.addPage(new QueryPage(&dialog), QIcon(":/images/query.png"), tr("Query"));
264         dialog.exec();
265         \endcode
266
267     \image html qxtconfigdialog.png "QxtConfigDialog with page icons on the left (QxtConfigDialog::West)."
268  */
269
270 /*!
271     \enum IconPosition::IconPosition
272
273     This enum describes the page icon position.
274
275     \sa QxtCheckComboBox::iconPosition
276  */
277
278 /*!
279     \var QxtConfigDialog::IconPosition QxtConfigDialog::North
280
281     The icons are located above the pages.
282  */
283
284 /*!
285     \var QxtConfigDialog::IconPosition QxtConfigDialog::West
286
287     The icons are located to the left of the pages.
288  */
289
290 /*!
291     \var QxtConfigDialog::IconPosition QxtConfigDialog::East
292
293     The icons are located to the right of the pages.
294  */
295
296 /*!
297     \fn QxtConfigDialog::currentIndexChanged(int index)
298
299     This signal is emitted whenever the current page \a index changes.
300
301     \sa currentIndex()
302  */
303
304 /*!
305     Constructs a new QxtConfigDialog with \a parent and \a flags.
306  */
307 QxtConfigDialog::QxtConfigDialog(QWidget* parent, Qt::WindowFlags flags)
308         : QDialog(parent, flags)
309 {
310     QXT_INIT_PRIVATE(QxtConfigDialog);
311     qxt_d().init();
312 }
313
314 /*!
315     Constructs a new QxtConfigDialog with icon \a position, \a parent and \a flags.
316  */
317 QxtConfigDialog::QxtConfigDialog(QxtConfigDialog::IconPosition position, QWidget* parent, Qt::WindowFlags flags)
318         : QDialog(parent, flags)
319 {
320     QXT_INIT_PRIVATE(QxtConfigDialog);
321     qxt_d().init(position);
322 }
323
324 /*!
325     Destructs the config dialog.
326  */
327 QxtConfigDialog::~QxtConfigDialog()
328 {}
329
330 /*!
331     \return The dialog button box.
332
333     The default buttons are \b QDialogButtonBox::Ok and \b QDialogButtonBox::Cancel.
334
335     \note QDialogButtonBox is available in Qt 4.2 or newer.
336
337     \sa setDialogButtonBox()
338 */
339 #if QT_VERSION >= 0x040200
340 QDialogButtonBox* QxtConfigDialog::dialogButtonBox() const
341 {
342     return qxt_d().buttons;
343 }
344 #endif // QT_VERSION
345
346 /*!
347     Sets the dialog \a buttonBox.
348
349     \sa dialogButtonBox()
350 */
351 #if QT_VERSION >= 0x040200
352 void QxtConfigDialog::setDialogButtonBox(QDialogButtonBox* buttonBox)
353 {
354     if (qxt_d().buttons != buttonBox)
355     {
356         if (qxt_d().buttons && qxt_d().buttons->parent() == this)
357         {
358             delete qxt_d().buttons;
359         }
360         qxt_d().buttons = buttonBox;
361         qxt_d().relayout();
362     }
363 }
364 #endif // QT_VERSION
365
366 /*!
367     \property QxtConfigDialog::hoverEffect
368     \brief This property holds whether a hover effect is shown for page icons
369
370     The default value is \b true.
371
372     \note Hovered (but not selected) icons are highlighted with lightened \b QPalette::Highlight
373     (whereas selected icons are highlighted with \b QPalette::Highlight). In case lightened
374     \b QPalette::Highlight ends up same as \b QPalette::Base, \b QPalette::AlternateBase is used
375     as a fallback color for the hover effect. This usually happens when \b QPalette::Highlight
376     already is a light color (eg. light gray).
377  */
378 bool QxtConfigDialog::hasHoverEffect() const
379 {
380     return qxt_d().list->hasHoverEffect();
381 }
382
383 void QxtConfigDialog::setHoverEffect(bool enabled)
384 {
385     qxt_d().list->setHoverEffect(enabled);
386 }
387
388 /*!
389     \property QxtConfigDialog::iconPosition
390     \brief This property holds the position of page icons
391  */
392 QxtConfigDialog::IconPosition QxtConfigDialog::iconPosition() const
393 {
394     return qxt_d().pos;
395 }
396
397 void QxtConfigDialog::setIconPosition(QxtConfigDialog::IconPosition position)
398 {
399     if (qxt_d().pos != position)
400     {
401         qxt_d().pos = position;
402         qxt_d().relayout();
403     }
404 }
405
406 /*!
407     \property QxtConfigDialog::iconSize
408     \brief This property holds the size of page icons
409  */
410 QSize QxtConfigDialog::iconSize() const
411 {
412     return qxt_d().list->iconSize();
413 }
414
415 void QxtConfigDialog::setIconSize(const QSize& size)
416 {
417     qxt_d().list->setIconSize(size);
418 }
419
420 /*!
421     Adds a \a page with \a icon and \a title.
422
423         In case \a title is an empty string, \b QWidget::windowTitle is used.
424
425         \return The index of added page.
426
427     \warning Adding and removing pages dynamically at run time might cause flicker.
428
429     \sa insertPage()
430 */
431 int QxtConfigDialog::addPage(QWidget* page, const QIcon& icon, const QString& title)
432 {
433     return insertPage(-1, page, icon, title);
434 }
435
436 /*!
437     Inserts a \a page with \a icon and \a title.
438
439         In case \a title is an empty string, \b QWidget::windowTitle is used.
440
441         \return The index of inserted page.
442
443     \warning Inserting and removing pages dynamically at run time might cause flicker.
444
445     \sa addPage()
446 */
447 int QxtConfigDialog::insertPage(int index, QWidget* page, const QIcon& icon, const QString& title)
448 {
449     if (!page)
450     {
451         qWarning("QxtConfigDialog::insertPage(): Attempt to insert null page");
452         return -1;
453     }
454
455     index = qxt_d().stack->insertWidget(index, page);
456     const QString label = !title.isEmpty() ? title : page->windowTitle();
457     if (label.isEmpty())
458         qWarning("QxtConfigDialog::insertPage(): Inserting a page with an empty title");
459     QListWidgetItem* item = new QListWidgetItem(icon, label);
460     qxt_d().list->insertItem(index, item);
461     qxt_d().list->invalidate();
462     return index;
463 }
464
465 /*!
466    Removes the page at \a index.
467
468    \note Does not delete the page widget.
469 */
470 void QxtConfigDialog::removePage(int index)
471 {
472     if (QWidget* page = qxt_d().stack->widget(index))
473     {
474         qxt_d().stack->removeWidget(page);
475         delete qxt_d().list->takeItem(index);
476         qxt_d().list->invalidate();
477     }
478     else
479     {
480         qWarning("QxtConfigDialog::removePage(): Unknown index");
481     }
482 }
483
484 /*!
485     \property QxtConfigDialog::count
486     \brief This property holds the number of pages
487 */
488 int QxtConfigDialog::count() const
489 {
490     return qxt_d().stack->count();
491 }
492
493 /*!
494     \property QxtConfigDialog::currentIndex
495     \brief This property holds the index of current page
496 */
497 int QxtConfigDialog::currentIndex() const
498 {
499     return qxt_d().stack->currentIndex();
500 }
501
502 void QxtConfigDialog::setCurrentIndex(int index)
503 {
504     qxt_d().list->setCurrentRow(index);
505     qxt_d().stack->setCurrentIndex(index);
506 }
507
508 /*!
509     \return The current page.
510
511     \sa currentIndex(), setCurrentPage()
512 */
513 QWidget* QxtConfigDialog::currentPage() const
514 {
515     return qxt_d().stack->currentWidget();
516 }
517
518 /*!
519     Sets the current \a page.
520
521     \sa currentPage(), currentIndex()
522 */
523 void QxtConfigDialog::setCurrentPage(QWidget* page)
524 {
525     setCurrentIndex(qxt_d().stack->indexOf(page));
526 }
527
528 /*!
529     \return The index of \a page or \b -1 if the page is unknown.
530 */
531 int QxtConfigDialog::indexOf(QWidget* page) const
532 {
533     return qxt_d().stack->indexOf(page);
534 }
535
536 /*!
537     \return The page at \a index or \b 0 if the \a index is out of range.
538 */
539 QWidget* QxtConfigDialog::page(int index) const
540 {
541     return qxt_d().stack->widget(index);
542 }
543
544 /*!
545     \return \b true if the page at \a index is enabled; otherwise \b false.
546
547     \sa setPageEnabled(), QWidget::isEnabled()
548 */
549 bool QxtConfigDialog::isPageEnabled(int index) const
550 {
551     const QListWidgetItem* item = qxt_d().list->item(index);
552     return (item && (item->flags() & Qt::ItemIsEnabled));
553 }
554
555 /*!
556     Sets the page at \a index \a enabled. The corresponding
557         page icon is also \a enabled.
558
559     \sa isPageEnabled(), QWidget::setEnabled()
560 */
561 void QxtConfigDialog::setPageEnabled(int index, bool enabled)
562 {
563     QWidget* page = qxt_d().stack->widget(index);
564     QListWidgetItem* item = qxt_d().list->item(index);
565     if (page && item)
566     {
567         page->setEnabled(enabled);
568         if (enabled)
569             item->setFlags(item->flags() | Qt::ItemIsEnabled);
570         else
571             item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
572     }
573     else
574     {
575         qWarning("QxtConfigDialog::setPageEnabled(): Unknown index");
576     }
577 }
578
579 /*!
580     \return \b true if the page at \a index is hidden; otherwise \b false.
581
582     \sa setPageHidden(), QWidget::isVisible()
583 */
584 bool QxtConfigDialog::isPageHidden(int index) const
585 {
586     const QListWidgetItem* item = qxt_d().list->item(index);
587 #if QT_VERSION >= 0x040200
588     return (item && item->isHidden());
589 #else // QT_VERSION
590     return (item && qxt_d().list->isItemHidden(item));
591 #endif // QT_VERSION
592 }
593
594 /*!
595     Sets the page at \a index \a hidden. The corresponding
596         page icon is also \a hidden.
597
598     \sa isPageHidden(), QWidget::setVisible()
599 */
600 void QxtConfigDialog::setPageHidden(int index, bool hidden)
601 {
602     QListWidgetItem* item = qxt_d().list->item(index);
603     if (item)
604     {
605 #if QT_VERSION >= 0x040200
606         item->setHidden(hidden);
607 #else
608         qxt_d().list->setItemHidden(item, hidden);
609 #endif // QT_VERSION
610     }
611     else
612     {
613         qWarning("QxtConfigDialog::setPageHidden(): Unknown index");
614     }
615 }
616
617 /*!
618     \return The icon of page at \a index.
619
620     \sa setPageIcon()
621 */
622 QIcon QxtConfigDialog::pageIcon(int index) const
623 {
624     const QListWidgetItem* item = qxt_d().list->item(index);
625     return (item ? item->icon() : QIcon());
626 }
627
628 /*!
629     Sets the \a icon of page at \a index.
630
631     \sa pageIcon()
632 */
633 void QxtConfigDialog::setPageIcon(int index, const QIcon& icon)
634 {
635     QListWidgetItem* item = qxt_d().list->item(index);
636     if (item)
637     {
638         item->setIcon(icon);
639     }
640     else
641     {
642         qWarning("QxtConfigDialog::setPageIcon(): Unknown index");
643     }
644 }
645
646 /*!
647     \return The title of page at \a index.
648
649     \sa setPageTitle()
650 */
651 QString QxtConfigDialog::pageTitle(int index) const
652 {
653     const QListWidgetItem* item = qxt_d().list->item(index);
654     return (item ? item->text() : QString());
655 }
656
657 /*!
658     Sets the \a title of page at \a index.
659
660     \sa pageTitle()
661 */
662 void QxtConfigDialog::setPageTitle(int index, const QString& title)
663 {
664     QListWidgetItem* item = qxt_d().list->item(index);
665     if (item)
666     {
667         item->setText(title);
668     }
669     else
670     {
671         qWarning("QxtConfigDialog::setPageTitle(): Unknown index");
672     }
673 }
674
675 /*!
676     \return The tooltip of page at \a index.
677
678     \sa setPageToolTip()
679 */
680 QString QxtConfigDialog::pageToolTip(int index) const
681 {
682     const QListWidgetItem* item = qxt_d().list->item(index);
683     return (item ? item->toolTip() : QString());
684 }
685
686 /*!
687     Sets the \a tooltip of page at \a index.
688
689     \sa pageToolTip()
690 */
691 void QxtConfigDialog::setPageToolTip(int index, const QString& tooltip)
692 {
693     QListWidgetItem* item = qxt_d().list->item(index);
694     if (item)
695     {
696         item->setToolTip(tooltip);
697     }
698     else
699     {
700         qWarning("QxtConfigDialog::setPageToolTip(): Unknown index");
701     }
702 }
703
704 /*!
705     \return The what's this of page at \a index.
706
707     \sa setPageWhatsThis()
708 */
709 QString QxtConfigDialog::pageWhatsThis(int index) const
710 {
711     const QListWidgetItem* item = qxt_d().list->item(index);
712     return (item ? item->whatsThis() : QString());
713 }
714
715 /*!
716     Sets the \a whatsthis of page at \a index.
717
718     \sa pageWhatsThis()
719 */
720 void QxtConfigDialog::setPageWhatsThis(int index, const QString& whatsthis)
721 {
722     QListWidgetItem* item = qxt_d().list->item(index);
723     if (item)
724     {
725         item->setWhatsThis(whatsthis);
726     }
727     else
728     {
729         qWarning("QxtConfigDialog::setPageWhatsThis(): Unknown index");
730     }
731 }
732
733 /*!
734     \return The internal list widget used for showing page icons.
735
736     \sa stackedWidget()
737 */
738 QListWidget* QxtConfigDialog::listWidget() const
739 {
740     return qxt_d().list;
741 }
742
743 /*!
744     \return The internal stacked widget used for stacking pages.
745
746     \sa listWidget()
747 */
748 QStackedWidget* QxtConfigDialog::stackedWidget() const
749 {
750     return qxt_d().stack;
751 }