1 | /* |
2 | This file is part of the KDE libraries |
3 | SPDX-FileCopyrightText: 2020 Ahmad Samir <a.samirh78@gmail.com> |
4 | |
5 | SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL |
6 | */ |
7 | |
8 | #include "kmessagedialog.h" |
9 | #include "kmessagebox_p.h" |
10 | |
11 | #include "loggingcategory.h" |
12 | |
13 | #include <QApplication> |
14 | #include <QCheckBox> |
15 | #include <QDebug> |
16 | #include <QDialogButtonBox> |
17 | #include <QHBoxLayout> |
18 | #include <QLabel> |
19 | #include <QListWidget> |
20 | #include <QPushButton> |
21 | #include <QScreen> |
22 | #include <QScrollArea> |
23 | #include <QScrollBar> |
24 | #include <QShowEvent> |
25 | #include <QStyle> |
26 | #include <QStyleOption> |
27 | #include <QTextBrowser> |
28 | #include <QVBoxLayout> |
29 | #include <QWindow> |
30 | |
31 | #include <KCollapsibleGroupBox> |
32 | #include <KSqueezedTextLabel> |
33 | |
34 | static const Qt::TextInteractionFlags s_textFlags = Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard; |
35 | |
36 | // TODO KF6 remove QObject inheritance again |
37 | class KMessageDialogPrivate : public QObject |
38 | { |
39 | Q_OBJECT |
40 | |
41 | public: |
42 | explicit KMessageDialogPrivate(KMessageDialog::Type type, KMessageDialog *qq) |
43 | : m_type(type) |
44 | , q(qq) |
45 | { |
46 | } |
47 | |
48 | KMessageDialog::Type m_type; |
49 | KMessageDialog *const q; |
50 | |
51 | QVBoxLayout *m_topLayout = nullptr; |
52 | QWidget *m_mainWidget = nullptr; |
53 | QLabel *m_iconLabel = nullptr; |
54 | QLabel *m_messageLabel = nullptr; |
55 | QListWidget *m_listWidget = nullptr; |
56 | QLabel *m_detailsLabel = nullptr; |
57 | QTextBrowser *m_detailsTextEdit = nullptr; |
58 | KCollapsibleGroupBox *m_detailsGroup = nullptr; |
59 | QCheckBox *m_dontAskAgainCB = nullptr; |
60 | QDialogButtonBox *m_buttonBox = nullptr; |
61 | QMetaObject::Connection m_buttonBoxConnection; |
62 | bool m_notifyEnabled = true; |
63 | }; |
64 | |
65 | KMessageDialog::KMessageDialog(KMessageDialog::Type type, const QString &text, QWidget *parent) |
66 | : QDialog(parent) |
67 | , d(new KMessageDialogPrivate(type, this)) |
68 | { |
69 | // Dialog top-level layout |
70 | d->m_topLayout = new QVBoxLayout(this); |
71 | d->m_topLayout->setSizeConstraint(QLayout::SetFixedSize); |
72 | |
73 | // Main widget |
74 | d->m_mainWidget = new QWidget(this); |
75 | d->m_topLayout->addWidget(d->m_mainWidget); |
76 | |
77 | // Layout for the main widget |
78 | auto *mainLayout = new QVBoxLayout(d->m_mainWidget); |
79 | QStyle *widgetStyle = d->m_mainWidget->style(); |
80 | // Provide extra spacing |
81 | mainLayout->setSpacing(widgetStyle->pixelMetric(metric: QStyle::PM_LayoutVerticalSpacing) * 2); |
82 | mainLayout->setContentsMargins(left: 0, top: 0, right: 0, bottom: 0); |
83 | |
84 | auto *hLayout = new QHBoxLayout{}; |
85 | mainLayout->addLayout(layout: hLayout, stretch: 5); |
86 | |
87 | // Icon |
88 | auto *iconLayout = new QVBoxLayout{}; |
89 | hLayout->addLayout(layout: iconLayout, stretch: 0); |
90 | |
91 | d->m_iconLabel = new QLabel(d->m_mainWidget); |
92 | d->m_iconLabel->setVisible(false); |
93 | iconLayout->addWidget(d->m_iconLabel); |
94 | hLayout->addSpacing(size: widgetStyle->pixelMetric(metric: QStyle::PM_LayoutHorizontalSpacing)); |
95 | |
96 | const QRect desktop = screen()->geometry(); |
97 | const auto desktopWidth = desktop.width(); |
98 | // Main message text |
99 | d->m_messageLabel = new QLabel(text, d->m_mainWidget); |
100 | if (d->m_messageLabel->sizeHint().width() > (desktopWidth * 0.5)) { |
101 | // Enable automatic wrapping of messages which are longer than 50% of screen width |
102 | d->m_messageLabel->setWordWrap(true); |
103 | // Use a squeezed label if text is still too wide |
104 | const bool usingSqueezedLabel = d->m_messageLabel->sizeHint().width() > (desktopWidth * 0.85); |
105 | if (usingSqueezedLabel) { |
106 | delete d->m_messageLabel; |
107 | d->m_messageLabel = new KSqueezedTextLabel(text, d->m_mainWidget); |
108 | } |
109 | } |
110 | |
111 | d->m_messageLabel->setTextInteractionFlags(s_textFlags); |
112 | |
113 | const bool usingScrollArea = (desktop.height() / 3) < d->m_messageLabel->sizeHint().height(); |
114 | if (usingScrollArea) { |
115 | QScrollArea *messageScrollArea = new QScrollArea(d->m_mainWidget); |
116 | messageScrollArea->setWidget(d->m_messageLabel); |
117 | messageScrollArea->setFrameShape(QFrame::NoFrame); |
118 | messageScrollArea->setWidgetResizable(true); |
119 | hLayout->addWidget(messageScrollArea, stretch: 5); |
120 | } else { |
121 | hLayout->addWidget(d->m_messageLabel, stretch: 5); |
122 | } |
123 | |
124 | // List widget, will be populated by setListWidgetItems() |
125 | d->m_listWidget = new QListWidget(d->m_mainWidget); |
126 | mainLayout->addWidget(d->m_listWidget, stretch: usingScrollArea ? 10 : 50); |
127 | d->m_listWidget->setVisible(false); |
128 | |
129 | // DontAskAgain checkbox, will be set up by setDontAskAgainText() |
130 | d->m_dontAskAgainCB = new QCheckBox(d->m_mainWidget); |
131 | mainLayout->addWidget(d->m_dontAskAgainCB); |
132 | d->m_dontAskAgainCB->setVisible(false); |
133 | |
134 | // Details widget, text will be added by setDetails() |
135 | auto *detailsHLayout = new QHBoxLayout{}; |
136 | d->m_topLayout->addLayout(layout: detailsHLayout); |
137 | |
138 | d->m_detailsGroup = new KCollapsibleGroupBox(); |
139 | d->m_detailsGroup->setVisible(false); |
140 | d->m_detailsGroup->setTitle(QApplication::translate(context: "KMessageDialog" , key: "Details" )); |
141 | QVBoxLayout *detailsLayout = new QVBoxLayout(d->m_detailsGroup); |
142 | |
143 | d->m_detailsLabel = new QLabel(); |
144 | d->m_detailsLabel->setTextInteractionFlags(s_textFlags); |
145 | d->m_detailsLabel->setWordWrap(true); |
146 | detailsLayout->addWidget(d->m_detailsLabel); |
147 | |
148 | d->m_detailsTextEdit = new QTextBrowser{}; |
149 | d->m_detailsTextEdit->setMinimumHeight(d->m_detailsTextEdit->fontMetrics().lineSpacing() * 11); |
150 | detailsLayout->addWidget(d->m_detailsTextEdit, stretch: 50); |
151 | |
152 | detailsHLayout->addWidget(d->m_detailsGroup); |
153 | |
154 | // Button box |
155 | d->m_buttonBox = new QDialogButtonBox(this); |
156 | d->m_topLayout->addWidget(d->m_buttonBox); |
157 | |
158 | // Default buttons |
159 | if ((d->m_type == KMessageDialog::Information) || (d->m_type != KMessageDialog::Error)) { |
160 | // set Ok button |
161 | setButtons(); |
162 | } else if (d->m_type == KMessageDialog::WarningContinueCancel) { |
163 | // set Continue & Cancel buttons |
164 | setButtons(primaryAction: KStandardGuiItem::cont(), secondaryAction: KGuiItem(), cancelAction: KStandardGuiItem::cancel()); |
165 | } |
166 | |
167 | setNotifyEnabled(true); |
168 | |
169 | // If the dialog is rejected, e.g. by pressing Esc, done() signal connected to the button box |
170 | // won't be emitted |
171 | connect(sender: this, signal: &QDialog::rejected, context: this, slot: [this]() { |
172 | done(KMessageDialog::Cancel); |
173 | }); |
174 | } |
175 | |
176 | // This method has been copied from KWindowSystem to avoid depending on it |
177 | static void setMainWindow(QDialog *dialog, WId mainWindowId) |
178 | { |
179 | #ifdef Q_OS_OSX |
180 | if (!QWidget::find(mainWindowId)) { |
181 | return; |
182 | } |
183 | #endif |
184 | // Set the WA_NativeWindow attribute to force the creation of the QWindow. |
185 | // Without this QWidget::windowHandle() returns 0. |
186 | dialog->setAttribute(Qt::WA_NativeWindow, on: true); |
187 | QWindow *subWindow = dialog->windowHandle(); |
188 | Q_ASSERT(subWindow); |
189 | |
190 | QWindow *mainWindow = QWindow::fromWinId(id: mainWindowId); |
191 | if (!mainWindow) { |
192 | // foreign windows not supported on all platforms |
193 | return; |
194 | } |
195 | // mainWindow is not the child of any object, so make sure it gets deleted at some point |
196 | QObject::connect(sender: dialog, signal: &QObject::destroyed, context: mainWindow, slot: &QObject::deleteLater); |
197 | subWindow->setTransientParent(mainWindow); |
198 | } |
199 | |
200 | KMessageDialog::KMessageDialog(KMessageDialog::Type type, const QString &text, WId parent_id) |
201 | : KMessageDialog(type, text) |
202 | { |
203 | QWidget *parent = QWidget::find(parent_id); |
204 | setParent(parent); |
205 | if (!parent && parent_id) { |
206 | setMainWindow(dialog: this, mainWindowId: parent_id); |
207 | } |
208 | } |
209 | |
210 | KMessageDialog::~KMessageDialog() |
211 | { |
212 | removeEventFilter(obj: d.get()); |
213 | } |
214 | |
215 | void KMessageDialog::setCaption(const QString &caption) |
216 | { |
217 | if (!caption.isEmpty()) { |
218 | setWindowTitle(caption); |
219 | return; |
220 | } |
221 | |
222 | QString title; |
223 | switch (d->m_type) { // Get a title based on the dialog Type |
224 | case KMessageDialog::QuestionTwoActions: |
225 | case KMessageDialog::QuestionTwoActionsCancel: |
226 | title = QApplication::translate(context: "KMessageDialog" , key: "Question" ); |
227 | break; |
228 | case KMessageDialog::WarningTwoActions: |
229 | case KMessageDialog::WarningTwoActionsCancel: |
230 | case KMessageDialog::WarningContinueCancel: |
231 | title = QApplication::translate(context: "KMessageDialog" , key: "Warning" ); |
232 | break; |
233 | case KMessageDialog::Information: |
234 | title = QApplication::translate(context: "KMessageDialog" , key: "Information" ); |
235 | break; |
236 | case KMessageDialog::Error: { |
237 | title = QApplication::translate(context: "KMessageDialog" , key: "Error" ); |
238 | break; |
239 | } |
240 | default: |
241 | break; |
242 | } |
243 | |
244 | setWindowTitle(title); |
245 | } |
246 | |
247 | void KMessageDialog::setIcon(const QIcon &icon) |
248 | { |
249 | QIcon effectiveIcon(icon); |
250 | if (effectiveIcon.isNull()) { // Fallback to an icon based on the dialog Type |
251 | QStyle *style = this->style(); |
252 | switch (d->m_type) { |
253 | case KMessageDialog::QuestionTwoActions: |
254 | case KMessageDialog::QuestionTwoActionsCancel: |
255 | effectiveIcon = style->standardIcon(standardIcon: QStyle::SP_MessageBoxQuestion, option: nullptr, widget: this); |
256 | break; |
257 | case KMessageDialog::WarningTwoActions: |
258 | case KMessageDialog::WarningTwoActionsCancel: |
259 | case KMessageDialog::WarningContinueCancel: |
260 | effectiveIcon = style->standardIcon(standardIcon: QStyle::SP_MessageBoxWarning, option: nullptr, widget: this); |
261 | break; |
262 | case KMessageDialog::Information: |
263 | effectiveIcon = style->standardIcon(standardIcon: QStyle::SP_MessageBoxInformation, option: nullptr, widget: this); |
264 | break; |
265 | case KMessageDialog::Error: |
266 | effectiveIcon = style->standardIcon(standardIcon: QStyle::SP_MessageBoxCritical, option: nullptr, widget: this); |
267 | break; |
268 | default: |
269 | break; |
270 | } |
271 | } |
272 | |
273 | if (effectiveIcon.isNull()) { |
274 | qCWarning(KWidgetsAddonsLog) << "Neither the requested icon nor a generic one based on the " |
275 | "dialog type could be found." ; |
276 | return; |
277 | } |
278 | |
279 | d->m_iconLabel->setVisible(true); |
280 | |
281 | QStyleOption option; |
282 | option.initFrom(w: d->m_mainWidget); |
283 | QStyle *widgetStyle = d->m_mainWidget->style(); |
284 | const int size = widgetStyle->pixelMetric(metric: QStyle::PM_MessageBoxIconSize, option: &option, widget: d->m_mainWidget); |
285 | d->m_iconLabel->setPixmap(effectiveIcon.pixmap(extent: size)); |
286 | } |
287 | |
288 | void KMessageDialog::setListWidgetItems(const QStringList &strlist) |
289 | { |
290 | const bool isEmpty = strlist.isEmpty(); |
291 | d->m_listWidget->setVisible(!isEmpty); |
292 | if (isEmpty) { |
293 | return; |
294 | } |
295 | |
296 | // Enable automatic wrapping since the listwidget already has a good initial width |
297 | d->m_messageLabel->setWordWrap(true); |
298 | d->m_listWidget->addItems(labels: strlist); |
299 | |
300 | QStyleOptionViewItem styleOption; |
301 | styleOption.initFrom(w: d->m_listWidget); |
302 | QFontMetrics fm(styleOption.font); |
303 | int listWidth = d->m_listWidget->width(); |
304 | for (const QString &str : strlist) { |
305 | listWidth = qMax(a: listWidth, b: fm.boundingRect(text: str).width()); |
306 | } |
307 | const int borderWidth = (d->m_listWidget->width() - d->m_listWidget->viewport()->width() // |
308 | + d->m_listWidget->verticalScrollBar()->height()); |
309 | listWidth += borderWidth; |
310 | const auto deskWidthPortion = screen()->geometry().width() * 0.85; |
311 | if (listWidth > deskWidthPortion) { // Limit the list widget size to 85% of screen width |
312 | listWidth = qRound(d: deskWidthPortion); |
313 | } |
314 | d->m_listWidget->setMinimumWidth(listWidth); |
315 | d->m_listWidget->setSelectionMode(QListWidget::NoSelection); |
316 | d->m_messageLabel->setSizePolicy(hor: QSizePolicy::Preferred, ver: QSizePolicy::Minimum); |
317 | } |
318 | |
319 | void KMessageDialog::setDetails(const QString &details) |
320 | { |
321 | d->m_detailsGroup->setVisible(!details.isEmpty()); |
322 | |
323 | if (details.length() < 512) { // random number KMessageBox uses. |
324 | d->m_detailsLabel->setText(details); |
325 | d->m_detailsLabel->show(); |
326 | |
327 | d->m_detailsTextEdit->setText(QString()); |
328 | d->m_detailsTextEdit->hide(); |
329 | } else { |
330 | d->m_detailsLabel->setText(QString()); |
331 | d->m_detailsLabel->hide(); |
332 | |
333 | d->m_detailsTextEdit->setText(details); |
334 | d->m_detailsTextEdit->show(); |
335 | } |
336 | } |
337 | |
338 | void KMessageDialog::setButtons(const KGuiItem &primaryAction, const KGuiItem &secondaryAction, const KGuiItem &cancelAction) |
339 | { |
340 | switch (d->m_type) { |
341 | case KMessageDialog::QuestionTwoActions: { |
342 | d->m_buttonBox->setStandardButtons(QDialogButtonBox::Yes | QDialogButtonBox::No); |
343 | auto *buttonYes = d->m_buttonBox->button(which: QDialogButtonBox::Yes); |
344 | KGuiItem::assign(button: buttonYes, item: primaryAction); |
345 | buttonYes->setFocus(); |
346 | KGuiItem::assign(button: d->m_buttonBox->button(which: QDialogButtonBox::No), item: secondaryAction); |
347 | break; |
348 | } |
349 | case KMessageDialog::QuestionTwoActionsCancel: { |
350 | d->m_buttonBox->setStandardButtons(QDialogButtonBox::Yes | QDialogButtonBox::No | QDialogButtonBox::Cancel); |
351 | auto *buttonYes = d->m_buttonBox->button(which: QDialogButtonBox::Yes); |
352 | KGuiItem::assign(button: buttonYes, item: primaryAction); |
353 | buttonYes->setFocus(); |
354 | KGuiItem::assign(button: d->m_buttonBox->button(which: QDialogButtonBox::No), item: secondaryAction); |
355 | KGuiItem::assign(button: d->m_buttonBox->button(which: QDialogButtonBox::Cancel), item: cancelAction); |
356 | break; |
357 | } |
358 | case KMessageDialog::WarningTwoActions: { |
359 | d->m_buttonBox->setStandardButtons(QDialogButtonBox::Yes | QDialogButtonBox::No); |
360 | KGuiItem::assign(button: d->m_buttonBox->button(which: QDialogButtonBox::Yes), item: primaryAction); |
361 | |
362 | auto *noBtn = d->m_buttonBox->button(which: QDialogButtonBox::No); |
363 | KGuiItem::assign(button: noBtn, item: secondaryAction); |
364 | noBtn->setDefault(true); |
365 | noBtn->setFocus(); |
366 | break; |
367 | } |
368 | case KMessageDialog::WarningTwoActionsCancel: { |
369 | d->m_buttonBox->setStandardButtons(QDialogButtonBox::Yes | QDialogButtonBox::No | QDialogButtonBox::Cancel); |
370 | KGuiItem::assign(button: d->m_buttonBox->button(which: QDialogButtonBox::Yes), item: primaryAction); |
371 | KGuiItem::assign(button: d->m_buttonBox->button(which: QDialogButtonBox::No), item: secondaryAction); |
372 | |
373 | auto *cancelButton = d->m_buttonBox->button(which: QDialogButtonBox::Cancel); |
374 | KGuiItem::assign(button: cancelButton, item: cancelAction); |
375 | cancelButton->setDefault(true); |
376 | cancelButton->setFocus(); |
377 | break; |
378 | } |
379 | case KMessageDialog::WarningContinueCancel: { |
380 | d->m_buttonBox->setStandardButtons(QDialogButtonBox::Yes | QDialogButtonBox::Cancel); |
381 | |
382 | KGuiItem::assign(button: d->m_buttonBox->button(which: QDialogButtonBox::Yes), item: primaryAction); |
383 | |
384 | auto *cancelButton = d->m_buttonBox->button(which: QDialogButtonBox::Cancel); |
385 | KGuiItem::assign(button: cancelButton, item: cancelAction); |
386 | cancelButton->setDefault(true); |
387 | cancelButton->setFocus(); |
388 | break; |
389 | } |
390 | case KMessageDialog::Information: |
391 | case KMessageDialog::Error: { |
392 | d->m_buttonBox->setStandardButtons(QDialogButtonBox::Ok); |
393 | auto *okButton = d->m_buttonBox->button(which: QDialogButtonBox::Ok); |
394 | KGuiItem::assign(button: okButton, item: KStandardGuiItem::ok()); |
395 | okButton->setFocus(); |
396 | break; |
397 | } |
398 | default: |
399 | break; |
400 | } |
401 | |
402 | // Button connections |
403 | if (!d->m_buttonBoxConnection) { |
404 | d->m_buttonBoxConnection = connect(sender: d->m_buttonBox, signal: &QDialogButtonBox::clicked, context: this, slot: [this](QAbstractButton *button) { |
405 | QDialogButtonBox::StandardButton code = d->m_buttonBox->standardButton(button); |
406 | const int result = (code == QDialogButtonBox::Ok) ? KMessageDialog::Ok |
407 | : (code == QDialogButtonBox::Cancel) ? KMessageDialog::Cancel |
408 | : (code == QDialogButtonBox::Yes) ? KMessageDialog::PrimaryAction |
409 | : (code == QDialogButtonBox::No) ? KMessageDialog::SecondaryAction |
410 | : |
411 | /* else */ -1; |
412 | if (result != -1) { |
413 | done(result); |
414 | } |
415 | }); |
416 | } |
417 | } |
418 | |
419 | void KMessageDialog::setDontAskAgainText(const QString &dontAskAgainText) |
420 | { |
421 | d->m_dontAskAgainCB->setVisible(!dontAskAgainText.isEmpty()); |
422 | d->m_dontAskAgainCB->setText(dontAskAgainText); |
423 | } |
424 | |
425 | void KMessageDialog::setDontAskAgainChecked(bool isChecked) |
426 | { |
427 | if (d->m_dontAskAgainCB->text().isEmpty()) { |
428 | qCWarning(KWidgetsAddonsLog) << "setDontAskAgainChecked() method was called on a dialog that doesn't " |
429 | "appear to have a checkbox; you need to use setDontAskAgainText() " |
430 | "to add a checkbox to the dialog first." ; |
431 | return; |
432 | } |
433 | |
434 | d->m_dontAskAgainCB->setChecked(isChecked); |
435 | } |
436 | |
437 | bool KMessageDialog::isDontAskAgainChecked() const |
438 | { |
439 | if (d->m_dontAskAgainCB->text().isEmpty()) { |
440 | qCWarning(KWidgetsAddonsLog) << "isDontAskAgainChecked() method was called on a dialog that doesn't " |
441 | "appear to have a checkbox; you need to use setDontAskAgainText() " |
442 | "to add a checkbox to the dialog first." ; |
443 | return false; |
444 | } |
445 | |
446 | return d->m_dontAskAgainCB->isChecked(); |
447 | } |
448 | |
449 | void KMessageDialog::setOpenExternalLinks(bool isAllowed) |
450 | { |
451 | d->m_messageLabel->setOpenExternalLinks(isAllowed); |
452 | d->m_detailsLabel->setOpenExternalLinks(isAllowed); |
453 | d->m_detailsTextEdit->setOpenExternalLinks(isAllowed); |
454 | } |
455 | |
456 | bool KMessageDialog::isNotifyEnabled() const |
457 | { |
458 | return d->m_notifyEnabled; |
459 | } |
460 | |
461 | void KMessageDialog::setNotifyEnabled(bool enable) |
462 | { |
463 | d->m_notifyEnabled = enable; |
464 | } |
465 | |
466 | void KMessageDialog::showEvent(QShowEvent *event) |
467 | { |
468 | if (d->m_notifyEnabled && !event->spontaneous()) { |
469 | // TODO include m_listWidget items |
470 | beep(type: d->m_type, text: d->m_messageLabel->text(), dialog: topLevelWidget()); |
471 | } |
472 | QDialog::showEvent(event); |
473 | } |
474 | |
475 | void KMessageDialog::beep(Type type, const QString &text, QWidget *widget) |
476 | { |
477 | #ifndef Q_OS_WIN // FIXME problems with KNotify on Windows |
478 | QMessageBox::Icon notifyType = QMessageBox::NoIcon; |
479 | switch (type) { |
480 | case KMessageDialog::QuestionTwoActions: |
481 | case KMessageDialog::QuestionTwoActionsCancel: |
482 | notifyType = QMessageBox::Question; |
483 | break; |
484 | case KMessageDialog::WarningTwoActions: |
485 | case KMessageDialog::WarningTwoActionsCancel: |
486 | case KMessageDialog::WarningContinueCancel: |
487 | notifyType = QMessageBox::Warning; |
488 | break; |
489 | case KMessageDialog::Information: |
490 | notifyType = QMessageBox::Information; |
491 | break; |
492 | case KMessageDialog::Error: |
493 | notifyType = QMessageBox::Critical; |
494 | break; |
495 | } |
496 | |
497 | KMessageBox::notifyInterface()->sendNotification(notificationType: notifyType, message: text, parent: widget); |
498 | #endif |
499 | } |
500 | |
501 | #include "kmessagedialog.moc" |
502 | #include "moc_kmessagedialog.cpp" |
503 | |