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)
6 *
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.
11 *
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.
16 *
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.
21 */
22
23#include "kdialog.h"
24#include "kdialog_p.h"
25#include <kdebug.h>
26#include "kdialogqueue_p.h"
27
28#include <config.h>
29
30#include <QApplication>
31#include <QDesktopWidget>
32#include <QDialogButtonBox>
33#include <QHBoxLayout>
34#include <QHideEvent>
35#include <QPointer>
36#include <QStyle>
37#include <QTimer>
38#include <QVBoxLayout>
39#include <QWhatsThis>
40
41#include <klocale.h>
42#include <kpushbutton.h>
43#include <kseparator.h>
44#include <kstandardguiitem.h>
45#include <ktoolinvocation.h>
46#include <kurllabel.h>
47
48#ifdef Q_WS_X11
49#include <qx11info_x11.h>
50#include <netwm.h>
51#endif
52
53static bool sAllowEmbeddingInGraphicsView = false;
54
55void KDialogPrivate::setupLayout()
56{
57 Q_Q(KDialog);
58 if (!dirty) {
59 QMetaObject::invokeMethod( q, "queuedLayoutUpdate", Qt::QueuedConnection );
60 dirty = true;
61 }
62}
63
64void KDialogPrivate::queuedLayoutUpdate()
65{
66 if (!dirty)
67 return;
68
69 dirty = false;
70
71 Q_Q(KDialog);
72
73 // Don't lose the focus widget when re-creating the layout.
74 // Testcase: KOrganizer's "Select Categories" dialog
75 QPointer<QWidget> focusWidget = mMainWidget ? mMainWidget->focusWidget() : 0;
76
77 if (q->layout() && q->layout() != mTopLayout) {
78 kWarning(240) << q->metaObject()->className() << "created with a layout; don't do that, KDialog takes care of it, use mainWidget or setMainWidget instead";
79 delete q->layout();
80 }
81
82 delete mTopLayout;
83
84 if ( mButtonOrientation == Qt::Horizontal )
85 mTopLayout = new QVBoxLayout(q);
86 else
87 mTopLayout = new QHBoxLayout(q);
88
89 if ( mUrlHelp )
90 mTopLayout->addWidget( mUrlHelp, 0, Qt::AlignRight );
91
92 if ( mMainWidget )
93 mTopLayout->addWidget( mMainWidget, 10 );
94
95 if ( mDetailsWidget )
96 mTopLayout->addWidget( mDetailsWidget );
97
98 if ( mActionSeparator )
99 mTopLayout->addWidget( mActionSeparator );
100
101 if ( mButtonBox ) {
102 mButtonBox->setOrientation( mButtonOrientation );
103 mTopLayout->addWidget( mButtonBox );
104 }
105
106 if (focusWidget) {
107 focusWidget->setFocus();
108 }
109}
110
111void KDialogPrivate::appendButton(KDialog::ButtonCode key, const KGuiItem &item)
112{
113 Q_Q(KDialog);
114
115 QDialogButtonBox::ButtonRole role = QDialogButtonBox::InvalidRole;
116 switch ( key ) {
117 case KDialog::Help:
118 case KDialog::Details:
119 role = QDialogButtonBox::HelpRole;
120 break;
121 case KDialog::Default:
122 case KDialog::Reset:
123 role = QDialogButtonBox::ResetRole;
124 break;
125 case KDialog::Ok:
126 role = QDialogButtonBox::AcceptRole;
127 break;
128 case KDialog::Apply:
129 role = QDialogButtonBox::ApplyRole;
130 break;
131 case KDialog::Try:
132 case KDialog::Yes:
133 role = QDialogButtonBox::YesRole;
134 break;
135 case KDialog::Close:
136 case KDialog::Cancel:
137 role = QDialogButtonBox::RejectRole;
138 break;
139 case KDialog::No:
140 role = QDialogButtonBox::NoRole;
141 break;
142 case KDialog::User1:
143 case KDialog::User2:
144 case KDialog::User3:
145 role = QDialogButtonBox::ActionRole;
146 break;
147 default:
148 role = QDialogButtonBox::InvalidRole;
149 break;
150 }
151
152 if ( role == QDialogButtonBox::InvalidRole )
153 return;
154
155 KPushButton *button = new KPushButton( item );
156 mButtonBox->addButton( button, role );
157
158 mButtonList.insert( key, button );
159 mButtonSignalMapper.setMapping( button, key );
160
161 QObject::connect(button, SIGNAL(clicked()),
162 &mButtonSignalMapper, SLOT(map()) );
163
164 if (key == mDefaultButton) {
165 // Now that it exists, set it as default
166 q->setDefaultButton(mDefaultButton);
167 }
168}
169
170void KDialogPrivate::init(KDialog *q)
171{
172 q_ptr = q;
173
174 dirty = false;
175
176 q->setButtons(KDialog::Ok | KDialog::Cancel);
177 q->setDefaultButton(KDialog::Ok);
178
179 q->connect(&mButtonSignalMapper, SIGNAL(mapped(int)), q, SLOT(slotButtonClicked(int)));
180
181 q->setPlainCaption(KGlobal::caption()); // set appropriate initial window title for case it gets not set later
182}
183
184void KDialogPrivate::helpLinkClicked()
185{
186 q_ptr->slotButtonClicked(KDialog::Help);
187}
188
189KDialog::KDialog( QWidget *parent, Qt::WindowFlags flags )
190 : QDialog(parent, sAllowEmbeddingInGraphicsView ? flags : flags | Qt::BypassGraphicsProxyWidget ), d_ptr(new KDialogPrivate)
191{
192 d_ptr->init(this);
193}
194
195KDialog::KDialog(KDialogPrivate &dd, QWidget *parent, Qt::WindowFlags flags)
196 : QDialog(parent, sAllowEmbeddingInGraphicsView ? flags : flags | Qt::BypassGraphicsProxyWidget), d_ptr(&dd)
197{
198 d_ptr->init(this);
199}
200
201KDialog::~KDialog()
202{
203 delete d_ptr;
204}
205
206void KDialog::setButtons( ButtonCodes buttonMask )
207{
208 Q_D(KDialog);
209 if ( d->mButtonBox ) {
210 d->mButtonList.clear();
211
212 delete d->mButtonBox;
213 d->mButtonBox = 0;
214 }
215
216 if ( buttonMask & Cancel )
217 buttonMask &= ~Close;
218
219 if ( buttonMask & Apply )
220 buttonMask &= ~Try;
221
222 if ( buttonMask & Details )
223 buttonMask &= ~Default;
224
225 if ( buttonMask == None ) {
226 d->setupLayout();
227 return; // When we want no button box
228 }
229
230 d->mEscapeButton = (buttonMask & Cancel) ? Cancel : Close;
231 d->mButtonBox = new QDialogButtonBox( this );
232
233 if ( buttonMask & Help )
234 d->appendButton( Help, KStandardGuiItem::help() );
235 if ( buttonMask & Default )
236 d->appendButton( Default, KStandardGuiItem::defaults() );
237 if ( buttonMask & Reset )
238 d->appendButton( Reset, KStandardGuiItem::reset() );
239 if ( buttonMask & User3 )
240 d->appendButton( User3, KGuiItem() );
241 if ( buttonMask & User2 )
242 d->appendButton( User2, KGuiItem() );
243 if ( buttonMask & User1 )
244 d->appendButton( User1, KGuiItem() );
245 if ( buttonMask & Ok )
246 d->appendButton( Ok, KStandardGuiItem::ok() );
247 if ( buttonMask & Apply )
248 d->appendButton( Apply, KStandardGuiItem::apply() );
249 if ( buttonMask & Try )
250 d->appendButton( Try, KGuiItem(i18n( "&Try" )) );
251 if ( buttonMask & Cancel )
252 d->appendButton( Cancel, KStandardGuiItem::cancel() );
253 if ( buttonMask & Close )
254 d->appendButton( Close, KStandardGuiItem::close() );
255 if ( buttonMask & Yes )
256 d->appendButton( Yes, KStandardGuiItem::yes() );
257 if ( buttonMask & No )
258 d->appendButton( No, KStandardGuiItem::no() );
259 if ( buttonMask & Details ) {
260 d->appendButton( Details, KGuiItem(QString(), "help-about") );
261 setDetailsWidgetVisible( false );
262 }
263
264 d->setupLayout();
265}
266
267
268void KDialog::setButtonsOrientation( Qt::Orientation orientation )
269{
270 Q_D(KDialog);
271 if ( d->mButtonOrientation != orientation ) {
272 d->mButtonOrientation = orientation;
273
274 if ( d->mActionSeparator )
275 d->mActionSeparator->setOrientation( d->mButtonOrientation );
276
277 if ( d->mButtonOrientation == Qt::Vertical )
278 enableLinkedHelp( false ); // 2000-06-18 Espen: No support for this yet.
279 }
280}
281
282void KDialog::setEscapeButton( ButtonCode id )
283{
284 d_func()->mEscapeButton = id;
285}
286
287void KDialog::setDefaultButton( ButtonCode newDefaultButton )
288{
289 Q_D(KDialog);
290
291 if (newDefaultButton == None)
292 newDefaultButton = NoDefault; // #148969
293
294 const KDialog::ButtonCode oldDefault = defaultButton();
295
296 bool oldDefaultHadFocus = false;
297
298 if (oldDefault != NoDefault) {
299 KPushButton *old = button(oldDefault);
300 if (old) {
301 oldDefaultHadFocus = (focusWidget() == old);
302 old->setDefault(false);
303 }
304 }
305
306 if (newDefaultButton != NoDefault) {
307 KPushButton *b = button(newDefaultButton);
308 if (b) {
309 b->setDefault(true);
310 if (focusWidget() == 0 || oldDefaultHadFocus) {
311 // No widget had focus yet, or the old default button had
312 // -> ok to give focus to the new default button, so that it's
313 // really default (Enter triggers it).
314 // But we don't do this if the kdialog user gave focus to a
315 // specific widget in the dialog.
316 b->setFocus();
317 }
318 }
319 }
320 d->mDefaultButton = newDefaultButton;
321 Q_ASSERT(defaultButton() == newDefaultButton);
322}
323
324KDialog::ButtonCode KDialog::defaultButton() const
325{
326 Q_D(const KDialog);
327 QHashIterator<int, KPushButton*> it( d->mButtonList );
328 while ( it.hasNext() ) {
329 it.next();
330 if (it.value()->isDefault()) {
331 return (ButtonCode)it.key();
332 }
333 }
334
335 return d->mDefaultButton;
336}
337
338void KDialog::setMainWidget( QWidget *widget )
339{
340 Q_D(KDialog);
341 if ( d->mMainWidget == widget )
342 return;
343 d->mMainWidget = widget;
344 if (d->mMainWidget && d->mMainWidget->layout()) {
345 // Avoid double-margin problem
346 d->mMainWidget->layout()->setMargin(0);
347 }
348 d->setupLayout();
349}
350
351QWidget *KDialog::mainWidget()
352{
353 Q_D(KDialog);
354 if (!d->mMainWidget)
355 setMainWidget( new QWidget(this) );
356 return d->mMainWidget;
357}
358
359QSize KDialog::sizeHint() const
360{
361 Q_D(const KDialog);
362
363 if (!d->mMinSize.isEmpty())
364 return d->mMinSize.expandedTo( minimumSizeHint() ) + d->mIncSize;
365 else {
366 if (d->dirty)
367 const_cast<KDialogPrivate*>(d)->queuedLayoutUpdate();
368 return QDialog::sizeHint() + d->mIncSize;
369 }
370}
371
372QSize KDialog::minimumSizeHint() const
373{
374 Q_D(const KDialog);
375
376 if (d->dirty)
377 const_cast<KDialogPrivate*>(d)->queuedLayoutUpdate();
378 return QDialog::minimumSizeHint() + d->mIncSize;
379}
380
381//
382// Grab QDialogs keypresses if non-modal.
383//
384void KDialog::keyPressEvent( QKeyEvent *event )
385{
386 Q_D(KDialog);
387 if ( event->modifiers() == 0 ) {
388 if ( event->key() == Qt::Key_F1 ) {
389 KPushButton *button = this->button( Help );
390
391 if ( button ) {
392 button->animateClick();
393 event->accept();
394 return;
395 }
396 }
397
398 if ( event->key() == Qt::Key_Escape ) {
399 KPushButton *button = this->button( d->mEscapeButton );
400
401 if ( button ) {
402 button->animateClick();
403 event->accept();
404 return;
405 }
406
407 }
408 } else if ( event->key() == Qt::Key_F1 && event->modifiers() == Qt::ShiftModifier ) {
409 QWhatsThis::enterWhatsThisMode();
410 event->accept();
411 return;
412 } else if ( event->modifiers() == Qt::ControlModifier &&
413 ( event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter ) ) {
414 // accept the dialog when Ctrl-Return is pressed
415 KPushButton *button = this->button( Ok );
416
417 if ( button ) {
418 button->animateClick();
419 event->accept();
420 return;
421 }
422 }
423
424 QDialog::keyPressEvent( event );
425}
426
427int KDialog::marginHint()
428{
429 return QApplication::style()->pixelMetric( QStyle::PM_DefaultChildMargin );
430}
431
432int KDialog::spacingHint()
433{
434 return QApplication::style()->pixelMetric( QStyle::PM_DefaultLayoutSpacing );
435}
436
437int KDialog::groupSpacingHint()
438{
439 return QApplication::fontMetrics().lineSpacing();
440}
441
442QString KDialog::makeStandardCaption( const QString &userCaption,
443 QWidget* window,
444 CaptionFlags flags )
445{
446 Q_UNUSED(window);
447 QString caption = KGlobal::caption();
448 QString captionString = userCaption.isEmpty() ? caption : userCaption;
449
450 // If the document is modified, add '[modified]'.
451 if (flags & ModifiedCaption)
452 captionString += QString::fromUtf8(" [") + i18n("modified") + QString::fromUtf8("]");
453
454 if ( !userCaption.isEmpty() ) {
455 // Add the application name if:
456 // User asked for it, it's not a duplication and the app name (caption()) is not empty
457 if ( flags & AppNameCaption &&
458 !caption.isEmpty() &&
459 !userCaption.endsWith(caption) ) {
460 // TODO: check to see if this is a transient/secondary window before trying to add the app name
461 // on platforms that need this
462 captionString += i18nc("Document/application separator in titlebar", " – ") + caption;
463 }
464 }
465
466 return captionString;
467}
468
469void KDialog::setCaption( const QString &_caption )
470{
471 const QString caption = makeStandardCaption( _caption, this );
472 setPlainCaption( caption );
473}
474
475void KDialog::setCaption( const QString &caption, bool modified )
476{
477 CaptionFlags flags = HIGCompliantCaption;
478
479 if ( modified )
480 {
481 flags |= ModifiedCaption;
482 }
483
484 setPlainCaption( makeStandardCaption(caption, this, flags) );
485}
486
487
488void KDialog::setPlainCaption( const QString &caption )
489{
490 if (QWidget *win = window()) {
491 win->setWindowTitle( caption );
492#ifdef Q_WS_X11
493 NETWinInfo info( QX11Info::display(), win->winId(), QX11Info::appRootWindow(), 0 );
494 info.setName( caption.toUtf8().constData() );
495#endif
496 }
497}
498
499void KDialog::resizeLayout( QWidget *widget, int margin, int spacing ) //static
500{
501 if ( widget->layout() )
502 resizeLayout( widget->layout(), margin, spacing );
503
504 if ( widget->children().count() > 0 ) {
505 const QList<QObject*> list = widget->children();
506 foreach ( QObject *object, list ) {
507 if ( object->isWidgetType() )
508 resizeLayout( (QWidget*)object, margin, spacing );
509 }
510 }
511}
512
513void KDialog::resizeLayout( QLayout *layout, int margin, int spacing ) //static
514{
515 QLayoutItem *child;
516 int pos = 0;
517
518 while ( (child = layout->itemAt( pos ) ) ) {
519 if ( child->layout() )
520 resizeLayout( child->layout(), margin, spacing );
521
522 ++pos;
523 }
524
525 if ( layout->layout() ) {
526 layout->layout()->setMargin( margin );
527 layout->layout()->setSpacing( spacing );
528 }
529}
530
531static QRect screenRect( QWidget *widget, int screen )
532{
533 QDesktopWidget *desktop = QApplication::desktop();
534 KConfig gc( "kdeglobals", KConfig::NoGlobals );
535 KConfigGroup cg(&gc, "Windows" );
536 if ( desktop->isVirtualDesktop() &&
537 cg.readEntry( "XineramaEnabled", true ) &&
538 cg.readEntry( "XineramaPlacementEnabled", true ) ) {
539
540 if ( screen < 0 || screen >= desktop->numScreens() ) {
541 if ( screen == -1 )
542 screen = desktop->primaryScreen();
543 else if ( screen == -3 )
544 screen = desktop->screenNumber( QCursor::pos() );
545 else
546 screen = desktop->screenNumber( widget );
547 }
548
549 return desktop->availableGeometry( screen );
550 } else
551 return desktop->geometry();
552}
553
554void KDialog::centerOnScreen( QWidget *widget, int screen )
555{
556 if ( !widget )
557 return;
558
559#ifdef Q_WS_X11
560 if( !( widget->windowFlags() & Qt::X11BypassWindowManagerHint ) && widget->windowType() != Qt::Popup
561 && NETRootInfo( QX11Info::display(), NET::Supported ).isSupported( NET::WM2FullPlacement )) {
562 return; // the WM can handle placement much better
563 }
564#endif
565
566 QRect rect = screenRect( widget, screen );
567
568 widget->move( rect.center().x() - widget->width() / 2,
569 rect.center().y() - widget->height() / 2 );
570}
571
572bool KDialog::avoidArea( QWidget *widget, const QRect& area, int screen )
573{
574 if ( !widget )
575 return false;
576
577 QRect fg = widget->frameGeometry();
578 if ( !fg.intersects( area ) )
579 return true; // nothing to do.
580
581 const QRect scr = screenRect( widget, screen );
582 QRect avoid( area ); // let's add some margin
583 avoid.translate( -5, -5 );
584 avoid.setRight( avoid.right() + 10 );
585 avoid.setBottom( avoid.bottom() + 10 );
586
587 if ( qMax( fg.top(), avoid.top() ) <= qMin( fg.bottom(), avoid.bottom() ) ) {
588 // We need to move the widget up or down
589 int spaceAbove = qMax( 0, avoid.top() - scr.top() );
590 int spaceBelow = qMax( 0, scr.bottom() - avoid.bottom() );
591 if ( spaceAbove > spaceBelow ) // where's the biggest side?
592 if ( fg.height() <= spaceAbove ) // big enough?
593 fg.setY( avoid.top() - fg.height() );
594 else
595 return false;
596 else
597 if ( fg.height() <= spaceBelow ) // big enough?
598 fg.setY( avoid.bottom() );
599 else
600 return false;
601 }
602
603 if ( qMax( fg.left(), avoid.left() ) <= qMin( fg.right(), avoid.right() ) ) {
604 // We need to move the widget left or right
605 const int spaceLeft = qMax( 0, avoid.left() - scr.left() );
606 const int spaceRight = qMax( 0, scr.right() - avoid.right() );
607 if ( spaceLeft > spaceRight ) // where's the biggest side?
608 if ( fg.width() <= spaceLeft ) // big enough?
609 fg.setX( avoid.left() - fg.width() );
610 else
611 return false;
612 else
613 if ( fg.width() <= spaceRight ) // big enough?
614 fg.setX( avoid.right() );
615 else
616 return false;
617 }
618
619 widget->move( fg.x(), fg.y() );
620
621 return true;
622}
623
624void KDialog::showButtonSeparator( bool state )
625{
626 Q_D(KDialog);
627 if ( ( d->mActionSeparator != 0 ) == state )
628 return;
629 if ( state ) {
630 if ( d->mActionSeparator )
631 return;
632
633 d->mActionSeparator = new KSeparator( this );
634 d->mActionSeparator->setOrientation( d->mButtonOrientation );
635 } else {
636 delete d->mActionSeparator;
637 d->mActionSeparator = 0;
638 }
639
640 d->setupLayout();
641}
642
643void KDialog::setInitialSize( const QSize &size )
644{
645 d_func()->mMinSize = size;
646 adjustSize();
647}
648
649void KDialog::incrementInitialSize( const QSize &size )
650{
651 d_func()->mIncSize = size;
652 adjustSize();
653}
654
655KPushButton *KDialog::button( ButtonCode id ) const
656{
657 Q_D(const KDialog);
658 return d->mButtonList.value( id, 0 );
659}
660
661void KDialog::enableButton( ButtonCode id, bool state )
662{
663 KPushButton *button = this->button( id );
664 if ( button )
665 button->setEnabled( state );
666}
667
668bool KDialog::isButtonEnabled( ButtonCode id ) const
669{
670 KPushButton *button = this->button( id );
671 if ( button )
672 return button->isEnabled();
673
674 return false;
675}
676
677void KDialog::enableButtonOk( bool state )
678{
679 enableButton( Ok, state );
680}
681
682void KDialog::enableButtonApply( bool state )
683{
684 enableButton( Apply, state );
685}
686
687void KDialog::enableButtonCancel( bool state )
688{
689 enableButton( Cancel, state );
690}
691
692void KDialog::showButton( ButtonCode id, bool state )
693{
694 KPushButton *button = this->button( id );
695 if ( button )
696 state ? button->show() : button->hide();
697}
698
699void KDialog::setButtonGuiItem( ButtonCode id, const KGuiItem &item )
700{
701 KPushButton *button = this->button( id );
702 if ( !button )
703 return;
704
705 button->setGuiItem( item );
706}
707
708void KDialog::setButtonMenu( ButtonCode id, QMenu *menu, ButtonPopupMode popupmode)
709{
710 KPushButton *button = this->button( id );
711 if ( button ) {
712 if (popupmode==InstantPopup)
713 button->setMenu( menu );
714 else
715 button->setDelayedMenu(menu);
716 }
717}
718
719void KDialog::setButtonText( ButtonCode id, const QString &text )
720{
721 Q_D(KDialog);
722 if ( !d->mSettingDetails && (id == Details) ) {
723 d->mDetailsButtonText = text;
724 setDetailsWidgetVisible( d->mDetailsVisible );
725 return;
726 }
727
728 KPushButton *button = this->button( id );
729 if ( button )
730 button->setText( text );
731}
732
733QString KDialog::buttonText( ButtonCode id ) const
734{
735 KPushButton *button = this->button( id );
736 if ( button )
737 return button->text();
738 else
739 return QString();
740}
741
742void KDialog::setButtonIcon( ButtonCode id, const KIcon &icon )
743{
744 KPushButton *button = this->button( id );
745 if ( button )
746 button->setIcon( icon );
747}
748
749KIcon KDialog::buttonIcon( ButtonCode id ) const
750{
751 KPushButton *button = this->button( id );
752 if ( button )
753 return KIcon(button->icon());
754 else
755 return KIcon();
756}
757
758void KDialog::setButtonToolTip( ButtonCode id, const QString &text )
759{
760 KPushButton *button = this->button( id );
761 if ( button ) {
762 if ( text.isEmpty() )
763 button->setToolTip( QString() );
764 else
765 button->setToolTip( text );
766 }
767}
768
769QString KDialog::buttonToolTip( ButtonCode id ) const
770{
771 KPushButton *button = this->button( id );
772 if ( button )
773 return button->toolTip();
774 else
775 return QString();
776}
777
778void KDialog::setButtonWhatsThis( ButtonCode id, const QString &text )
779{
780 KPushButton *button = this->button( id );
781 if ( button ) {
782 if ( text.isEmpty() )
783 button->setWhatsThis( QString() );
784 else
785 button->setWhatsThis( text );
786 }
787}
788
789QString KDialog::buttonWhatsThis( ButtonCode id ) const
790{
791 KPushButton *button = this->button( id );
792 if ( button )
793 return button->whatsThis();
794 else
795 return QString();
796}
797
798void KDialog::setButtonFocus( ButtonCode id )
799{
800 KPushButton *button = this->button( id );
801 if ( button ) {
802 button->setFocus();
803 }
804}
805
806void KDialog::setDetailsWidget( QWidget *detailsWidget )
807{
808 Q_D(KDialog);
809 if ( d->mDetailsWidget == detailsWidget )
810 return;
811 delete d->mDetailsWidget;
812 d->mDetailsWidget = detailsWidget;
813
814 if ( d->mDetailsWidget->parentWidget() != this )
815 d->mDetailsWidget->setParent( this );
816
817 d->mDetailsWidget->hide();
818 d->setupLayout();
819
820 if ( !d->mSettingDetails )
821 setDetailsWidgetVisible( d->mDetailsVisible );
822}
823
824bool KDialog::isDetailsWidgetVisible() const
825{
826 return d_func()->mDetailsVisible;
827}
828
829void KDialog::setDetailsWidgetVisible( bool visible )
830{
831 Q_D(KDialog);
832 if ( d->mDetailsButtonText.isEmpty() )
833 d->mDetailsButtonText = i18n( "&Details" );
834
835 d->mSettingDetails = true;
836 d->mDetailsVisible = visible;
837 if ( d->mDetailsVisible ) {
838 emit aboutToShowDetails();
839 setButtonText( Details, d->mDetailsButtonText + " <<" );
840 if ( d->mDetailsWidget ) {
841 if ( layout() )
842 layout()->setEnabled( false );
843
844 d->mDetailsWidget->show();
845
846 adjustSize();
847
848 if ( layout() ) {
849 layout()->activate();
850 layout()->setEnabled( true );
851 }
852 }
853 } else {
854 setButtonText( Details, d->mDetailsButtonText + " >>" );
855 if ( d->mDetailsWidget )
856 d->mDetailsWidget->hide();
857
858 if ( layout() ) {
859 layout()->activate();
860 adjustSize();
861 }
862
863 }
864
865 d->mSettingDetails = false;
866}
867
868void KDialog::delayedDestruct()
869{
870 if ( isVisible() )
871 hide();
872
873 deleteLater();
874}
875
876
877void KDialog::slotButtonClicked( int button )
878{
879 Q_D(KDialog);
880 emit buttonClicked( static_cast<KDialog::ButtonCode>(button) );
881
882 switch( button ) {
883 case Ok:
884 emit okClicked();
885 accept();
886 break;
887 case Apply:
888 emit applyClicked();
889 break;
890 case Try:
891 emit tryClicked();
892 break;
893 case User3:
894 emit user3Clicked();
895 break;
896 case User2:
897 emit user2Clicked();
898 break;
899 case User1:
900 emit user1Clicked();
901 break;
902 case Yes:
903 emit yesClicked();
904 done( Yes );
905 break;
906 case No:
907 emit noClicked();
908 done( No );
909 break;
910 case Cancel:
911 emit cancelClicked();
912 reject();
913 break;
914 case Close:
915 emit closeClicked();
916 done(Close); // KDE5: call reject() instead; more QDialog-like.
917 break;
918 case Help:
919 emit helpClicked();
920 if ( !d->mAnchor.isEmpty() || !d->mHelpApp.isEmpty() )
921 KToolInvocation::invokeHelp( d->mAnchor, d->mHelpApp );
922 break;
923 case Default:
924 emit defaultClicked();
925 break;
926 case Reset:
927 emit resetClicked();
928 break;
929 case Details:
930 setDetailsWidgetVisible( !d->mDetailsVisible );
931 break;
932 }
933
934 // If we're here from the closeEvent, and auto-delete is on, well, auto-delete now.
935 if (d->mDeferredDelete) {
936 d->mDeferredDelete = false;
937 delayedDestruct();
938 }
939}
940
941void KDialog::enableLinkedHelp( bool state )
942{
943 Q_D(KDialog);
944 if ( ( d->mUrlHelp != 0 ) == state )
945 return;
946 if ( state ) {
947 if ( d->mUrlHelp )
948 return;
949
950 d->mUrlHelp = new KUrlLabel( this );
951 d->mUrlHelp->setText( helpLinkText() );
952 d->mUrlHelp->setFloatEnabled( true );
953 d->mUrlHelp->setUnderline( true );
954 d->mUrlHelp->setMinimumHeight( fontMetrics().height() + marginHint() );
955 connect( d->mUrlHelp, SIGNAL(leftClickedUrl()), SLOT(helpLinkClicked()) );
956
957 d->mUrlHelp->show();
958 } else {
959 delete d->mUrlHelp;
960 d->mUrlHelp = 0;
961 }
962
963 d->setupLayout();
964}
965
966
967void KDialog::setHelp( const QString &anchor, const QString &appname )
968{
969 Q_D(KDialog);
970 d->mAnchor = anchor;
971 d->mHelpApp = appname;
972}
973
974
975void KDialog::setHelpLinkText( const QString &text )
976{
977 Q_D(KDialog);
978 d->mHelpLinkText = text;
979 if ( d->mUrlHelp )
980 d->mUrlHelp->setText( helpLinkText() );
981}
982
983QString KDialog::helpLinkText() const
984{
985 Q_D(const KDialog);
986 return ( d->mHelpLinkText.isEmpty() ? i18n( "Get help..." ) : d->mHelpLinkText );
987}
988
989void KDialog::updateGeometry()
990{
991}
992
993void KDialog::hideEvent( QHideEvent *event )
994{
995 emit hidden();
996
997 if ( !event->spontaneous() )
998 emit finished();
999}
1000
1001void KDialog::closeEvent( QCloseEvent *event )
1002{
1003 Q_D(KDialog);
1004 KPushButton *button = this->button(d->mEscapeButton);
1005 if (button && !isHidden()) {
1006 button->animateClick();
1007
1008 if (testAttribute(Qt::WA_DeleteOnClose)) {
1009 // Don't let QWidget::close do a deferred delete just yet, wait for the click first
1010 d->mDeferredDelete = true;
1011 setAttribute(Qt::WA_DeleteOnClose, false);
1012 }
1013 } else {
1014 QDialog::closeEvent(event);
1015 }
1016}
1017
1018void KDialog::restoreDialogSize( const KConfigGroup& cfg )
1019{
1020 int width, height;
1021 int scnum = QApplication::desktop()->screenNumber( parentWidget() );
1022 QRect desk = QApplication::desktop()->screenGeometry( scnum );
1023
1024 width = sizeHint().width();
1025 height = sizeHint().height();
1026
1027 width = cfg.readEntry( QString::fromLatin1( "Width %1" ).arg( desk.width() ), width );
1028 height = cfg.readEntry( QString::fromLatin1( "Height %1" ).arg( desk.height() ), height );
1029
1030 resize( width, height );
1031}
1032
1033void KDialog::saveDialogSize( KConfigGroup& config, KConfigGroup::WriteConfigFlags options ) const
1034{
1035 int scnum = QApplication::desktop()->screenNumber( parentWidget() );
1036 QRect desk = QApplication::desktop()->screenGeometry( scnum );
1037
1038 const QSize sizeToSave = size();
1039
1040 config.writeEntry( QString::fromLatin1("Width %1").arg( desk.width() ), sizeToSave.width(), options );
1041 config.writeEntry( QString::fromLatin1("Height %1").arg( desk.height() ), sizeToSave.height(), options );
1042}
1043
1044void KDialog::setAllowEmbeddingInGraphicsView( bool allowEmbedding )
1045{
1046 sAllowEmbeddingInGraphicsView = allowEmbedding;
1047}
1048
1049
1050class KDialogQueue::Private
1051{
1052 public:
1053 Private(KDialogQueue *q): q(q) {}
1054
1055 void slotShowQueuedDialog();
1056
1057 KDialogQueue *q;
1058 QList< QPointer<QDialog> > queue;
1059 bool busy;
1060
1061};
1062
1063KDialogQueue* KDialogQueue::self()
1064{
1065 K_GLOBAL_STATIC(KDialogQueue, _self)
1066 return _self;
1067}
1068
1069KDialogQueue::KDialogQueue()
1070 : d( new Private(this) )
1071{
1072 d->busy = false;
1073}
1074
1075KDialogQueue::~KDialogQueue()
1076{
1077 delete d;
1078}
1079
1080// static
1081void KDialogQueue::queueDialog( QDialog *dialog )
1082{
1083 KDialogQueue *_this = self();
1084 _this->d->queue.append( dialog );
1085
1086 QTimer::singleShot( 0, _this, SLOT(slotShowQueuedDialog()) );
1087}
1088
1089void KDialogQueue::Private::slotShowQueuedDialog()
1090{
1091 if ( busy )
1092 return;
1093
1094 QDialog *dialog;
1095 do {
1096 if ( queue.isEmpty() )
1097 return;
1098 dialog = queue.first();
1099 queue.pop_front();
1100 } while( !dialog );
1101
1102 busy = true;
1103 dialog->exec();
1104 busy = false;
1105 delete dialog;
1106
1107 if ( !queue.isEmpty() )
1108 QTimer::singleShot( 20, q, SLOT(slotShowQueuedDialog()) );
1109}
1110
1111#include "kdialog.moc"
1112#include "kdialogqueue_p.moc"
1113