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