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 | |
53 | static bool sAllowEmbeddingInGraphicsView = false; |
54 | |
55 | void KDialogPrivate::setupLayout() |
56 | { |
57 | Q_Q(KDialog); |
58 | if (!dirty) { |
59 | QMetaObject::invokeMethod( q, "queuedLayoutUpdate" , Qt::QueuedConnection ); |
60 | dirty = true; |
61 | } |
62 | } |
63 | |
64 | void 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 | |
111 | void 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 | |
170 | void 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 | |
184 | void KDialogPrivate::helpLinkClicked() |
185 | { |
186 | q_ptr->slotButtonClicked(KDialog::Help); |
187 | } |
188 | |
189 | KDialog::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 | |
195 | KDialog::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 | |
201 | KDialog::~KDialog() |
202 | { |
203 | delete d_ptr; |
204 | } |
205 | |
206 | void 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 | |
268 | void 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 | |
282 | void KDialog::setEscapeButton( ButtonCode id ) |
283 | { |
284 | d_func()->mEscapeButton = id; |
285 | } |
286 | |
287 | void 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 | |
324 | KDialog::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 | |
338 | void 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 | |
351 | QWidget *KDialog::mainWidget() |
352 | { |
353 | Q_D(KDialog); |
354 | if (!d->mMainWidget) |
355 | setMainWidget( new QWidget(this) ); |
356 | return d->mMainWidget; |
357 | } |
358 | |
359 | QSize 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 | |
372 | QSize 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 | // |
384 | void 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 | |
427 | int KDialog::marginHint() |
428 | { |
429 | return QApplication::style()->pixelMetric( QStyle::PM_DefaultChildMargin ); |
430 | } |
431 | |
432 | int KDialog::spacingHint() |
433 | { |
434 | return QApplication::style()->pixelMetric( QStyle::PM_DefaultLayoutSpacing ); |
435 | } |
436 | |
437 | int KDialog::groupSpacingHint() |
438 | { |
439 | return QApplication::fontMetrics().lineSpacing(); |
440 | } |
441 | |
442 | QString 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 | |
469 | void KDialog::setCaption( const QString &_caption ) |
470 | { |
471 | const QString caption = makeStandardCaption( _caption, this ); |
472 | setPlainCaption( caption ); |
473 | } |
474 | |
475 | void 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 | |
488 | void 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 | |
499 | void 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 | |
513 | void 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 | |
531 | static 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 | |
554 | void 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 | |
572 | bool 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 | |
624 | void 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 | |
643 | void KDialog::setInitialSize( const QSize &size ) |
644 | { |
645 | d_func()->mMinSize = size; |
646 | adjustSize(); |
647 | } |
648 | |
649 | void KDialog::incrementInitialSize( const QSize &size ) |
650 | { |
651 | d_func()->mIncSize = size; |
652 | adjustSize(); |
653 | } |
654 | |
655 | KPushButton *KDialog::button( ButtonCode id ) const |
656 | { |
657 | Q_D(const KDialog); |
658 | return d->mButtonList.value( id, 0 ); |
659 | } |
660 | |
661 | void KDialog::enableButton( ButtonCode id, bool state ) |
662 | { |
663 | KPushButton *button = this->button( id ); |
664 | if ( button ) |
665 | button->setEnabled( state ); |
666 | } |
667 | |
668 | bool 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 | |
677 | void KDialog::enableButtonOk( bool state ) |
678 | { |
679 | enableButton( Ok, state ); |
680 | } |
681 | |
682 | void KDialog::enableButtonApply( bool state ) |
683 | { |
684 | enableButton( Apply, state ); |
685 | } |
686 | |
687 | void KDialog::enableButtonCancel( bool state ) |
688 | { |
689 | enableButton( Cancel, state ); |
690 | } |
691 | |
692 | void KDialog::showButton( ButtonCode id, bool state ) |
693 | { |
694 | KPushButton *button = this->button( id ); |
695 | if ( button ) |
696 | state ? button->show() : button->hide(); |
697 | } |
698 | |
699 | void 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 | |
708 | void KDialog::( ButtonCode id, QMenu *, ButtonPopupMode ) |
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 | |
719 | void 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 | |
733 | QString 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 | |
742 | void KDialog::setButtonIcon( ButtonCode id, const KIcon &icon ) |
743 | { |
744 | KPushButton *button = this->button( id ); |
745 | if ( button ) |
746 | button->setIcon( icon ); |
747 | } |
748 | |
749 | KIcon 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 | |
758 | void 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 | |
769 | QString 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 | |
778 | void 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 | |
789 | QString 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 | |
798 | void KDialog::setButtonFocus( ButtonCode id ) |
799 | { |
800 | KPushButton *button = this->button( id ); |
801 | if ( button ) { |
802 | button->setFocus(); |
803 | } |
804 | } |
805 | |
806 | void 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 | |
824 | bool KDialog::isDetailsWidgetVisible() const |
825 | { |
826 | return d_func()->mDetailsVisible; |
827 | } |
828 | |
829 | void 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 | |
868 | void KDialog::delayedDestruct() |
869 | { |
870 | if ( isVisible() ) |
871 | hide(); |
872 | |
873 | deleteLater(); |
874 | } |
875 | |
876 | |
877 | void 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 | |
941 | void 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 | |
967 | void KDialog::setHelp( const QString &anchor, const QString &appname ) |
968 | { |
969 | Q_D(KDialog); |
970 | d->mAnchor = anchor; |
971 | d->mHelpApp = appname; |
972 | } |
973 | |
974 | |
975 | void 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 | |
983 | QString KDialog::helpLinkText() const |
984 | { |
985 | Q_D(const KDialog); |
986 | return ( d->mHelpLinkText.isEmpty() ? i18n( "Get help..." ) : d->mHelpLinkText ); |
987 | } |
988 | |
989 | void KDialog::updateGeometry() |
990 | { |
991 | } |
992 | |
993 | void KDialog::hideEvent( QHideEvent *event ) |
994 | { |
995 | emit hidden(); |
996 | |
997 | if ( !event->spontaneous() ) |
998 | emit finished(); |
999 | } |
1000 | |
1001 | void 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 | |
1018 | void 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 | |
1033 | void 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 | |
1044 | void KDialog::setAllowEmbeddingInGraphicsView( bool allowEmbedding ) |
1045 | { |
1046 | sAllowEmbeddingInGraphicsView = allowEmbedding; |
1047 | } |
1048 | |
1049 | |
1050 | class 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 | |
1063 | KDialogQueue* KDialogQueue::self() |
1064 | { |
1065 | K_GLOBAL_STATIC(KDialogQueue, _self) |
1066 | return _self; |
1067 | } |
1068 | |
1069 | KDialogQueue::KDialogQueue() |
1070 | : d( new Private(this) ) |
1071 | { |
1072 | d->busy = false; |
1073 | } |
1074 | |
1075 | KDialogQueue::~KDialogQueue() |
1076 | { |
1077 | delete d; |
1078 | } |
1079 | |
1080 | // static |
1081 | void 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 | |
1089 | void 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 | |