1
/* This file is part of the KDE Libraries
2
* Copyright (C) 1998 Thomas Tanghus (tanghus@earthling.net)
3
* Additions 1999-2000 by Espen Sand (espen@kde.org)
4
* by Holger Freyther <freyther@kde.org>
5
* 2005-2009 by Olivier Goffart (ogoffart at kde.org)
7
* This library is free software; you can redistribute it and/or
8
* modify it under the terms of the GNU Library General Public
9
* License as published by the Free Software Foundation; either
10
* version 2 of the License, or (at your option) any later version.
12
* This library 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 GNU
15
* Library General Public License for more details.
17
* You should have received a copy of the GNU Library General Public License
18
* along with this library; see the file COPYING.LIB. If not, write to
19
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20
* Boston, MA 02110-1301, USA.
24
#include "KoDialog_p.h"
26
#include <QApplication>
27
#include <QDesktopWidget>
28
#include <QDialogButtonBox>
29
#include <QHBoxLayout>
34
#include <QVBoxLayout>
37
#include <QPushButton>
40
#include <klocalizedstring.h>
42
#include <kseparator.h>
43
#include <kstandardguiitem.h>
44
#include <khelpclient.h>
45
#include <kurllabel.h>
46
#include <kwindowconfig.h>
48
void KoDialogPrivate::setupLayout()
52
QMetaObject::invokeMethod(q, "queuedLayoutUpdate", Qt::QueuedConnection);
57
void KoDialogPrivate::queuedLayoutUpdate()
67
// Don't lose the focus widget when re-creating the layout.
68
// Testcase: KOrganizer's "Select Categories" dialog
69
QPointer<QWidget> focusWidget = mMainWidget ? mMainWidget->focusWidget() : 0;
71
if (q->layout() && q->layout() != mTopLayout) {
72
qWarning() << q->metaObject()->className() << "created with a layout; don't do that, KoDialog takes care of it, use mainWidget or setMainWidget instead";
78
if (mButtonOrientation == Qt::Horizontal) {
79
mTopLayout = new QVBoxLayout(q);
81
mTopLayout = new QHBoxLayout(q);
85
mTopLayout->addWidget(mUrlHelp, 0, Qt::AlignRight);
89
mTopLayout->addWidget(mMainWidget, 10);
93
mTopLayout->addWidget(mDetailsWidget);
96
if (mActionSeparator) {
97
mTopLayout->addWidget(mActionSeparator);
101
mButtonBox->setOrientation(mButtonOrientation);
102
mTopLayout->addWidget(mButtonBox);
106
focusWidget->setFocus();
110
void KoDialogPrivate::appendButton(KoDialog::ButtonCode key, const KGuiItem &item)
114
QDialogButtonBox::ButtonRole role = QDialogButtonBox::InvalidRole;
117
case KoDialog::Details:
118
role = QDialogButtonBox::HelpRole;
120
case KoDialog::Default:
121
case KoDialog::Reset:
122
role = QDialogButtonBox::ResetRole;
125
role = QDialogButtonBox::AcceptRole;
127
case KoDialog::Apply:
128
role = QDialogButtonBox::ApplyRole;
132
role = QDialogButtonBox::YesRole;
134
case KoDialog::Close:
135
case KoDialog::Cancel:
136
role = QDialogButtonBox::RejectRole;
139
role = QDialogButtonBox::NoRole;
141
case KoDialog::User1:
142
case KoDialog::User2:
143
case KoDialog::User3:
144
role = QDialogButtonBox::ActionRole;
147
role = QDialogButtonBox::InvalidRole;
151
if (role == QDialogButtonBox::InvalidRole) {
155
QPushButton *button = new QPushButton;
156
KGuiItem::assign(button, item);
157
mButtonBox->addButton(button, role);
159
mButtonList.insert(key, button);
160
mButtonSignalMapper.setMapping(button, key);
162
QObject::connect(button, SIGNAL(clicked()),
163
&mButtonSignalMapper, SLOT(map()));
165
if (key == mDefaultButton) {
166
// Now that it exists, set it as default
167
q->setDefaultButton(mDefaultButton);
171
void KoDialogPrivate::init(KoDialog *q)
177
q->setButtons(KoDialog::Ok | KoDialog::Cancel);
178
q->setDefaultButton(KoDialog::Ok);
180
q->connect(&mButtonSignalMapper, SIGNAL(mapped(int)), q, SLOT(slotButtonClicked(int)));
182
q->setPlainCaption(qApp->applicationDisplayName()); // set appropriate initial window title for case it gets not set later
185
void KoDialogPrivate::helpLinkClicked()
187
q_ptr->slotButtonClicked(KoDialog::Help);
190
KoDialog::KoDialog(QWidget *parent, Qt::WindowFlags flags)
191
: QDialog(parent, flags)
192
, d_ptr(new KoDialogPrivate)
197
KoDialog::KoDialog(KoDialogPrivate &dd, QWidget *parent, Qt::WindowFlags flags)
198
: QDialog(parent, flags)
204
KoDialog::~KoDialog()
209
void KoDialog::setButtons(ButtonCodes buttonMask)
213
d->mButtonList.clear();
215
delete d->mButtonBox;
219
if (buttonMask & Cancel) {
220
buttonMask &= ~Close;
223
if (buttonMask & Apply) {
227
if (buttonMask & Details) {
228
buttonMask &= ~Default;
231
if (buttonMask == None) {
233
return; // When we want no button box
236
d->mEscapeButton = (buttonMask & Cancel) ? Cancel : Close;
237
d->mButtonBox = new QDialogButtonBox(this);
239
if (buttonMask & Help) {
240
d->appendButton(Help, KStandardGuiItem::help());
242
if (buttonMask & Default) {
243
d->appendButton(Default, KStandardGuiItem::defaults());
245
if (buttonMask & Reset) {
246
d->appendButton(Reset, KStandardGuiItem::reset());
248
if (buttonMask & User3) {
249
d->appendButton(User3, KGuiItem());
251
if (buttonMask & User2) {
252
d->appendButton(User2, KGuiItem());
254
if (buttonMask & User1) {
255
d->appendButton(User1, KGuiItem());
257
if (buttonMask & Ok) {
258
d->appendButton(Ok, KStandardGuiItem::ok());
260
if (buttonMask & Apply) {
261
d->appendButton(Apply, KStandardGuiItem::apply());
263
if (buttonMask & Try) {
264
d->appendButton(Try, KGuiItem(i18n("&Try")));
266
if (buttonMask & Cancel) {
267
d->appendButton(Cancel, KStandardGuiItem::cancel());
269
if (buttonMask & Close) {
270
d->appendButton(Close, KStandardGuiItem::close());
272
if (buttonMask & Yes) {
273
d->appendButton(Yes, KStandardGuiItem::yes());
275
if (buttonMask & No) {
276
d->appendButton(No, KStandardGuiItem::no());
278
if (buttonMask & Details) {
279
d->appendButton(Details, KGuiItem(QString(), "help-about"));
280
setDetailsWidgetVisible(false);
286
void KoDialog::setButtonsOrientation(Qt::Orientation orientation)
289
if (d->mButtonOrientation != orientation) {
290
d->mButtonOrientation = orientation;
292
if (d->mActionSeparator) {
293
d->mActionSeparator->setOrientation(d->mButtonOrientation);
296
if (d->mButtonOrientation == Qt::Vertical) {
297
enableLinkedHelp(false); // 2000-06-18 Espen: No support for this yet.
302
void KoDialog::setEscapeButton(ButtonCode id)
304
d_func()->mEscapeButton = id;
307
void KoDialog::setDefaultButton(ButtonCode newDefaultButton)
311
if (newDefaultButton == None) {
312
newDefaultButton = NoDefault; // #148969
315
const KoDialog::ButtonCode oldDefault = defaultButton();
317
bool oldDefaultHadFocus = false;
319
if (oldDefault != NoDefault) {
320
QPushButton *old = button(oldDefault);
322
oldDefaultHadFocus = (focusWidget() == old);
323
old->setDefault(false);
327
if (newDefaultButton != NoDefault) {
328
QPushButton *b = button(newDefaultButton);
331
if (focusWidget() == 0 || oldDefaultHadFocus) {
332
// No widget had focus yet, or the old default button had
333
// -> ok to give focus to the new default button, so that it's
334
// really default (Enter triggers it).
335
// But we don't do this if the kdialog user gave focus to a
336
// specific widget in the dialog.
341
d->mDefaultButton = newDefaultButton;
342
Q_ASSERT(defaultButton() == newDefaultButton);
345
KoDialog::ButtonCode KoDialog::defaultButton() const
348
QHashIterator<int, QPushButton *> it(d->mButtonList);
349
while (it.hasNext()) {
351
if (it.value()->isDefault()) {
352
return (ButtonCode)it.key();
356
return d->mDefaultButton;
359
void KoDialog::setMainWidget(QWidget *widget)
362
if (d->mMainWidget == widget) {
365
d->mMainWidget = widget;
366
if (d->mMainWidget && d->mMainWidget->layout()) {
367
// Avoid double-margin problem
368
d->mMainWidget->layout()->setMargin(0);
373
QWidget *KoDialog::mainWidget()
376
if (!d->mMainWidget) {
377
setMainWidget(new QWidget(this));
379
return d->mMainWidget;
382
QSize KoDialog::sizeHint() const
386
if (!d->mMinSize.isEmpty()) {
387
return d->mMinSize.expandedTo(minimumSizeHint()) + d->mIncSize;
390
const_cast<KoDialogPrivate *>(d)->queuedLayoutUpdate();
392
return QDialog::sizeHint() + d->mIncSize;
396
QSize KoDialog::minimumSizeHint() const
401
const_cast<KoDialogPrivate *>(d)->queuedLayoutUpdate();
403
return QDialog::minimumSizeHint() + d->mIncSize;
407
// Grab QDialogs keypresses if non-modal.
409
void KoDialog::keyPressEvent(QKeyEvent *event)
412
if (event->modifiers() == 0) {
413
if (event->key() == Qt::Key_F1) {
414
QPushButton *button = this->button(Help);
417
button->animateClick();
423
if (event->key() == Qt::Key_Escape) {
424
QPushButton *button = this->button(d->mEscapeButton);
427
button->animateClick();
433
} else if (event->key() == Qt::Key_F1 && event->modifiers() == Qt::ShiftModifier) {
434
QWhatsThis::enterWhatsThisMode();
437
} else if (event->modifiers() == Qt::ControlModifier &&
438
(event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)) {
439
// accept the dialog when Ctrl-Return is pressed
440
QPushButton *button = this->button(Ok);
443
button->animateClick();
449
QDialog::keyPressEvent(event);
452
int KoDialog::marginHint()
454
return QApplication::style()->pixelMetric(QStyle::PM_DefaultChildMargin);
457
int KoDialog::spacingHint()
459
return QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing);
462
int KoDialog::groupSpacingHint()
464
return QApplication::fontMetrics().lineSpacing();
467
QString KoDialog::makeStandardCaption(const QString &userCaption,
472
QString caption = qApp->applicationDisplayName();
473
QString captionString = userCaption.isEmpty() ? caption : userCaption;
475
// If the document is modified, add '[modified]'.
476
if (flags & ModifiedCaption) {
477
captionString += QString::fromUtf8(" [") + i18n("modified") + QString::fromUtf8("]");
480
if (!userCaption.isEmpty()) {
481
// Add the application name if:
482
// User asked for it, it's not a duplication and the app name (caption()) is not empty
483
if (flags & AppNameCaption &&
484
!caption.isEmpty() &&
485
!userCaption.endsWith(caption)) {
486
// TODO: check to see if this is a transient/secondary window before trying to add the app name
487
// on platforms that need this
488
captionString += i18nc("Document/application separator in titlebar", " ā ") + caption;
492
return captionString;
495
void KoDialog::setCaption(const QString &_caption)
497
const QString caption = makeStandardCaption(_caption, this);
498
setPlainCaption(caption);
501
void KoDialog::setCaption(const QString &caption, bool modified)
503
CaptionFlags flags = HIGCompliantCaption;
505
// ### Qt5 TODO: port to [*], see QWidget::setWindowFilePath
507
flags |= ModifiedCaption;
510
setPlainCaption(makeStandardCaption(caption, this, flags));
513
void KoDialog::setPlainCaption(const QString &caption)
515
if (QWidget *win = window()) {
516
win->setWindowTitle(caption);
520
void KoDialog::resizeLayout(QWidget *widget, int margin, int spacing) //static
522
if (widget->layout()) {
523
resizeLayout(widget->layout(), margin, spacing);
526
if (widget->children().count() > 0) {
527
const QList<QObject *> list = widget->children();
528
foreach (QObject *object, list) {
529
if (object->isWidgetType()) {
530
resizeLayout((QWidget *)object, margin, spacing);
536
void KoDialog::resizeLayout(QLayout *layout, int margin, int spacing) //static
541
while ((child = layout->itemAt(pos))) {
542
if (child->layout()) {
543
resizeLayout(child->layout(), margin, spacing);
549
if (layout->layout()) {
550
layout->layout()->setMargin(margin);
551
layout->layout()->setSpacing(spacing);
555
static QRect screenRect(QWidget *widget, int screen)
557
QDesktopWidget *desktop = QApplication::desktop();
558
KConfig gc("kdeglobals", KConfig::NoGlobals);
559
KConfigGroup cg(&gc, "Windows");
560
if (desktop->isVirtualDesktop() &&
561
cg.readEntry("XineramaEnabled", true) &&
562
cg.readEntry("XineramaPlacementEnabled", true)) {
564
if (screen < 0 || screen >= desktop->numScreens()) {
566
screen = desktop->primaryScreen();
567
} else if (screen == -3) {
568
screen = desktop->screenNumber(QCursor::pos());
570
screen = desktop->screenNumber(widget);
574
return desktop->availableGeometry(screen);
576
return desktop->geometry();
580
void KoDialog::centerOnScreen(QWidget *widget, int screen)
586
QRect rect = screenRect(widget, screen);
588
widget->move(rect.center().x() - widget->width() / 2,
589
rect.center().y() - widget->height() / 2);
592
bool KoDialog::avoidArea(QWidget *widget, const QRect &area, int screen)
598
QRect fg = widget->frameGeometry();
599
if (!fg.intersects(area)) {
600
return true; // nothing to do.
603
const QRect scr = screenRect(widget, screen);
604
QRect avoid(area); // let's add some margin
605
avoid.translate(-5, -5);
606
avoid.setRight(avoid.right() + 10);
607
avoid.setBottom(avoid.bottom() + 10);
609
if (qMax(fg.top(), avoid.top()) <= qMin(fg.bottom(), avoid.bottom())) {
610
// We need to move the widget up or down
611
int spaceAbove = qMax(0, avoid.top() - scr.top());
612
int spaceBelow = qMax(0, scr.bottom() - avoid.bottom());
613
if (spaceAbove > spaceBelow) // where's the biggest side?
614
if (fg.height() <= spaceAbove) { // big enough?
615
fg.setY(avoid.top() - fg.height());
619
else if (fg.height() <= spaceBelow) { // big enough?
620
fg.setY(avoid.bottom());
626
if (qMax(fg.left(), avoid.left()) <= qMin(fg.right(), avoid.right())) {
627
// We need to move the widget left or right
628
const int spaceLeft = qMax(0, avoid.left() - scr.left());
629
const int spaceRight = qMax(0, scr.right() - avoid.right());
630
if (spaceLeft > spaceRight) // where's the biggest side?
631
if (fg.width() <= spaceLeft) { // big enough?
632
fg.setX(avoid.left() - fg.width());
636
else if (fg.width() <= spaceRight) { // big enough?
637
fg.setX(avoid.right());
643
widget->move(fg.x(), fg.y());
648
void KoDialog::showButtonSeparator(bool state)
651
if ((d->mActionSeparator != 0) == state) {
655
if (d->mActionSeparator) {
659
d->mActionSeparator = new KSeparator(this);
660
d->mActionSeparator->setOrientation(d->mButtonOrientation);
662
delete d->mActionSeparator;
663
d->mActionSeparator = 0;
669
void KoDialog::setInitialSize(const QSize &size)
671
d_func()->mMinSize = size;
675
void KoDialog::incrementInitialSize(const QSize &size)
677
d_func()->mIncSize = size;
681
QPushButton *KoDialog::button(ButtonCode id) const
684
return d->mButtonList.value(id, 0);
687
void KoDialog::enableButton(ButtonCode id, bool state)
689
QPushButton *button = this->button(id);
691
button->setEnabled(state);
695
bool KoDialog::isButtonEnabled(ButtonCode id) const
697
QPushButton *button = this->button(id);
699
return button->isEnabled();
705
void KoDialog::enableButtonOk(bool state)
707
enableButton(Ok, state);
710
void KoDialog::enableButtonApply(bool state)
712
enableButton(Apply, state);
715
void KoDialog::enableButtonCancel(bool state)
717
enableButton(Cancel, state);
720
void KoDialog::showButton(ButtonCode id, bool state)
722
QPushButton *button = this->button(id);
724
state ? button->show() : button->hide();
728
void KoDialog::setButtonGuiItem(ButtonCode id, const KGuiItem &item)
730
QPushButton *button = this->button(id);
735
KGuiItem::assign(button, item);
738
void KoDialog::setButtonText(ButtonCode id, const QString &text)
741
if (!d->mSettingDetails && (id == Details)) {
742
d->mDetailsButtonText = text;
743
setDetailsWidgetVisible(d->mDetailsVisible);
747
QPushButton *button = this->button(id);
749
button->setText(text);
753
QString KoDialog::buttonText(ButtonCode id) const
755
QPushButton *button = this->button(id);
757
return button->text();
763
void KoDialog::setButtonIcon(ButtonCode id, const QIcon &icon)
765
QPushButton *button = this->button(id);
767
button->setIcon(icon);
771
QIcon KoDialog::buttonIcon(ButtonCode id) const
773
QPushButton *button = this->button(id);
775
return button->icon();
781
void KoDialog::setButtonToolTip(ButtonCode id, const QString &text)
783
QPushButton *button = this->button(id);
785
if (text.isEmpty()) {
786
button->setToolTip(QString());
788
button->setToolTip(text);
793
QString KoDialog::buttonToolTip(ButtonCode id) const
795
QPushButton *button = this->button(id);
797
return button->toolTip();
803
void KoDialog::setButtonWhatsThis(ButtonCode id, const QString &text)
805
QPushButton *button = this->button(id);
807
if (text.isEmpty()) {
808
button->setWhatsThis(QString());
810
button->setWhatsThis(text);
815
QString KoDialog::buttonWhatsThis(ButtonCode id) const
817
QPushButton *button = this->button(id);
819
return button->whatsThis();
825
void KoDialog::setButtonFocus(ButtonCode id)
827
QPushButton *button = this->button(id);
833
void KoDialog::setDetailsWidget(QWidget *detailsWidget)
836
if (d->mDetailsWidget == detailsWidget) {
839
delete d->mDetailsWidget;
840
d->mDetailsWidget = detailsWidget;
842
if (d->mDetailsWidget->parentWidget() != this) {
843
d->mDetailsWidget->setParent(this);
846
d->mDetailsWidget->hide();
849
if (!d->mSettingDetails) {
850
setDetailsWidgetVisible(d->mDetailsVisible);
854
bool KoDialog::isDetailsWidgetVisible() const
856
return d_func()->mDetailsVisible;
859
void KoDialog::setDetailsWidgetVisible(bool visible)
862
if (d->mDetailsButtonText.isEmpty()) {
863
d->mDetailsButtonText = i18n("&Details");
866
d->mSettingDetails = true;
867
d->mDetailsVisible = visible;
868
if (d->mDetailsVisible) {
869
emit aboutToShowDetails();
870
setButtonText(Details, d->mDetailsButtonText + " <<");
871
if (d->mDetailsWidget) {
873
layout()->setEnabled(false);
876
d->mDetailsWidget->show();
881
layout()->activate();
882
layout()->setEnabled(true);
886
setButtonText(Details, d->mDetailsButtonText + " >>");
887
if (d->mDetailsWidget) {
888
d->mDetailsWidget->hide();
892
layout()->activate();
898
d->mSettingDetails = false;
901
void KoDialog::delayedDestruct()
910
void KoDialog::slotButtonClicked(int button)
913
emit buttonClicked(static_cast<KoDialog::ButtonCode>(button));
944
emit cancelClicked();
949
done(Close); // KDE5: call reject() instead; more QDialog-like.
953
if (!d->mAnchor.isEmpty() || !d->mHelpApp.isEmpty()) {
954
KHelpClient::invokeHelp(d->mAnchor, d->mHelpApp);
958
emit defaultClicked();
964
setDetailsWidgetVisible(!d->mDetailsVisible);
968
// If we're here from the closeEvent, and auto-delete is on, well, auto-delete now.
969
if (d->mDeferredDelete) {
970
d->mDeferredDelete = false;
975
void KoDialog::enableLinkedHelp(bool state)
978
if ((d->mUrlHelp != 0) == state) {
986
d->mUrlHelp = new KUrlLabel(this);
987
d->mUrlHelp->setText(helpLinkText());
988
d->mUrlHelp->setFloatEnabled(true);
989
d->mUrlHelp->setUnderline(true);
990
d->mUrlHelp->setMinimumHeight(fontMetrics().height() + marginHint());
991
connect(d->mUrlHelp, SIGNAL(leftClickedUrl()), SLOT(helpLinkClicked()));
1002
void KoDialog::setHelp(const QString &anchor, const QString &appname)
1005
d->mAnchor = anchor;
1006
d->mHelpApp = appname;
1009
void KoDialog::setHelpLinkText(const QString &text)
1012
d->mHelpLinkText = text;
1014
d->mUrlHelp->setText(helpLinkText());
1018
QString KoDialog::helpLinkText() const
1020
Q_D(const KoDialog);
1021
return (d->mHelpLinkText.isEmpty() ? i18n("Get help...") : d->mHelpLinkText);
1024
void KoDialog::updateGeometry()
1028
void KoDialog::hideEvent(QHideEvent *event)
1032
if (!event->spontaneous()) {
1037
void KoDialog::closeEvent(QCloseEvent *event)
1040
QPushButton *button = this->button(d->mEscapeButton);
1041
if (button && !isHidden()) {
1042
button->animateClick();
1044
if (testAttribute(Qt::WA_DeleteOnClose)) {
1045
// Don't let QWidget::close do a deferred delete just yet, wait for the click first
1046
d->mDeferredDelete = true;
1047
setAttribute(Qt::WA_DeleteOnClose, false);
1050
QDialog::closeEvent(event);
1054
#include "moc_KoDialog.cpp"