File indexing completed on 2024-05-19 05:21:21

0001 /*******************************************************************
0002  KNotes -- Notes for the KDE project
0003 
0004  SPDX-FileCopyrightText: 1997-2013 The KNotes Developers
0005 
0006  SPDX-License-Identifier: GPL-2.0-or-later
0007 *******************************************************************/
0008 
0009 #include "knote.h"
0010 #include "alarms/notealarmdialog.h"
0011 #include "attributes/notealarmattribute.h"
0012 #include "attributes/notedisplayattribute.h"
0013 #include "attributes/notelockattribute.h"
0014 #include "configdialog/knotesimpleconfigdialog.h"
0015 #include "knotedisplaysettings.h"
0016 #include "knoteedit.h"
0017 #include "knotes_debug.h"
0018 #include "knotesglobalconfig.h"
0019 #include "notes/knotebutton.h"
0020 #include "noteutils.h"
0021 #include "print/knoteprinter.h"
0022 #include "print/knoteprintobject.h"
0023 #include "print/knoteprintselectthemedialog.h"
0024 #include "utils/knoteutils.h"
0025 
0026 #include <Akonadi/ItemModifyJob>
0027 
0028 #include <Debug/akonadisearchdebugdialog.h>
0029 
0030 #include <KMime/KMimeMessage>
0031 
0032 #include <KActionCollection>
0033 #include <KComboBox>
0034 #include <KFileCustomDialog>
0035 #include <KIconEffect>
0036 #include <KLocalizedString>
0037 #include <KMessageBox>
0038 #include <KToggleAction>
0039 #include <KToolBar>
0040 #include <KWindowSystem>
0041 #include <KXMLGUIBuilder>
0042 #include <KXMLGUIFactory>
0043 
0044 #include <QApplication>
0045 #include <QCheckBox>
0046 #include <QInputDialog>
0047 #include <QLabel>
0048 #include <QMenu>
0049 #include <QMimeData>
0050 #include <QPointer>
0051 #include <QScreen>
0052 #include <QSizeGrip>
0053 #include <QTextEdit>
0054 #include <QVBoxLayout>
0055 #include <QWindow>
0056 
0057 #if KDEPIM_HAVE_X11
0058 #include <KWindowInfo>
0059 #include <KX11Extras>
0060 #include <NETWM>
0061 #include <fixx11h.h>
0062 #endif
0063 
0064 //#define DEBUG_SAVE_NOTE 1
0065 
0066 KNote::KNote(const QDomDocument &buildDoc, const Akonadi::Item &item, bool allowAkonadiSearchDebug, QWidget *parent)
0067     : QFrame(parent, Qt::FramelessWindowHint)
0068     , mItem(item)
0069     , m_kwinConf(KSharedConfig::openConfig(QStringLiteral("kwinrc")))
0070     , mDisplayAttribute(new KNoteDisplaySettings)
0071     , mAllowDebugAkonadiSearch(allowAkonadiSearchDebug)
0072 {
0073     if (mItem.hasAttribute<NoteShared::NoteDisplayAttribute>()) {
0074         mDisplayAttribute->setDisplayAttribute(mItem.attribute<NoteShared::NoteDisplayAttribute>());
0075     } else {
0076         setDisplayDefaultValue();
0077         // save default display value
0078     }
0079     setAcceptDrops(true);
0080     setAttribute(Qt::WA_DeleteOnClose);
0081     setDOMDocument(buildDoc);
0082     setXMLFile(componentName() + QLatin1StringView("ui.rc"), false, false);
0083 
0084     // create the main layout
0085     m_noteLayout = new QVBoxLayout(this);
0086     m_noteLayout->setContentsMargins(0, 0, 0, 0);
0087     createActions();
0088 
0089     buildGui();
0090     prepare();
0091 }
0092 
0093 KNote::~KNote()
0094 {
0095     delete mDisplayAttribute;
0096 }
0097 
0098 void KNote::setDisplayDefaultValue()
0099 {
0100     KNoteUtils::setDefaultValue(mItem);
0101     auto job = new Akonadi::ItemModifyJob(mItem);
0102 #ifdef DEBUG_SAVE_NOTE
0103     qCDebug(KNOTES_LOG) << "setDisplayDefaultValue slotNoteSaved(KJob*)";
0104 #endif
0105     connect(job, &Akonadi::ItemModifyJob::result, this, &KNote::slotNoteSaved);
0106 }
0107 
0108 void KNote::setChangeItem(const Akonadi::Item &item, const QSet<QByteArray> &set)
0109 {
0110     mItem = item;
0111     if (item.hasAttribute<NoteShared::NoteDisplayAttribute>()) {
0112         mDisplayAttribute->setDisplayAttribute(item.attribute<NoteShared::NoteDisplayAttribute>());
0113     }
0114     if (set.contains("KJotsLockAttribute")) {
0115         m_editor->setReadOnly(item.hasAttribute<NoteShared::NoteLockAttribute>());
0116     }
0117     if (set.contains("PLD:RFC822")) {
0118         loadNoteContent(item);
0119     }
0120     if (set.contains("NoteDisplayAttribute")) {
0121         qCDebug(KNOTES_LOG) << " ATR:NoteDisplayAttribute";
0122         slotApplyConfig();
0123     }
0124     // TODO update display/content etc.
0125     updateLabelAlignment();
0126 }
0127 
0128 void KNote::slotKill(bool force)
0129 {
0130     if (!force
0131         && KMessageBox::warningContinueCancel(this,
0132                                               i18n("<qt>Do you really want to delete note <b>%1</b>?</qt>", m_label->text()),
0133                                               i18n("Confirm Delete"),
0134                                               KGuiItem(i18n("&Delete"), QStringLiteral("edit-delete")),
0135                                               KStandardGuiItem::cancel(),
0136                                               QStringLiteral("ConfirmDeleteNote"))
0137             != KMessageBox::Continue) {
0138         return;
0139     }
0140 
0141     Q_EMIT sigKillNote(mItem.id());
0142 }
0143 
0144 // -------------------- public member functions -------------------- //
0145 
0146 void KNote::saveNote(bool force, bool sync)
0147 {
0148     if (!force && !m_editor->document()->isModified()) {
0149         return;
0150     }
0151     bool needToSave = false;
0152     auto attribute = mItem.attribute<NoteShared::NoteDisplayAttribute>(Akonadi::Item::AddIfMissing);
0153     const QPoint notePosition = pos();
0154     if (attribute->position() != notePosition) {
0155         needToSave = true;
0156         attribute->setPosition(notePosition);
0157     }
0158     const QSize currentSize(QSize(width(), height()));
0159     if (attribute->size() != currentSize) {
0160         needToSave = true;
0161         attribute->setSize(currentSize);
0162     }
0163 #if KDEPIM_HAVE_X11
0164     KWindowInfo info(winId(), NET::WMDesktop);
0165     const int count = KX11Extras::numberOfDesktops();
0166     for (int n = 1; n <= count; ++n) {
0167         if (info.isOnDesktop(n)) {
0168             if (attribute->desktop() != n) {
0169                 needToSave = true;
0170                 attribute->setDesktop(n);
0171                 break;
0172             }
0173         }
0174     }
0175 #endif
0176     if (m_editor->document()->isModified()) {
0177         needToSave = true;
0178         saveNoteContent();
0179     }
0180     if (needToSave) {
0181 #ifdef DEBUG_SAVE_NOTE
0182         qCDebug(KNOTES_LOG) << "save Note slotClose() slotNoteSaved(KJob*) : sync" << sync;
0183 #endif
0184         auto job = new Akonadi::ItemModifyJob(mItem);
0185         if (sync) {
0186             job->exec();
0187         } else {
0188 #ifdef DEBUG_SAVE_NOTE
0189             qCDebug(KNOTES_LOG) << "save Note slotClose() slotNoteSaved(KJob*)";
0190 #endif
0191             connect(job, &Akonadi::ItemModifyJob::result, this, &KNote::slotNoteSaved);
0192         }
0193     }
0194 }
0195 
0196 void KNote::slotNoteSaved(KJob *job)
0197 {
0198     qCDebug(KNOTES_LOG) << " void KNote::slotNoteSaved(KJob *job)";
0199     if (job->error()) {
0200         qCDebug(KNOTES_LOG) << " problem during save note:" << job->errorString();
0201     } else {
0202         m_editor->document()->setModified(false);
0203     }
0204 }
0205 
0206 Akonadi::Item::Id KNote::noteId() const
0207 {
0208     return mItem.id();
0209 }
0210 
0211 QString KNote::name() const
0212 {
0213     return m_label->text();
0214 }
0215 
0216 QString KNote::text() const
0217 {
0218     return m_editor->text();
0219 }
0220 
0221 void KNote::setName(const QString &name)
0222 {
0223     m_label->setText(name);
0224     updateLabelAlignment();
0225 
0226     if (m_editor) { // not called from CTOR?
0227         saveNote();
0228     }
0229     setWindowTitle(name);
0230 
0231     Q_EMIT sigNameChanged(name);
0232 }
0233 
0234 void KNote::setText(const QString &text)
0235 {
0236     m_editor->setText(text);
0237 
0238     saveNote();
0239 }
0240 
0241 bool KNote::isDesktopAssigned() const
0242 {
0243     return mDisplayAttribute->rememberDesktop();
0244 }
0245 
0246 bool KNote::isModified() const
0247 {
0248     return m_editor->document()->isModified();
0249 }
0250 
0251 // ------------------ private slots (menu actions) ------------------ //
0252 
0253 void KNote::slotRename()
0254 {
0255     // pop up dialog to get the new name
0256     bool ok;
0257     const QString oldName = m_label->text();
0258     const QString newName = QInputDialog::getText(this, QString(), i18n("Please enter the new name:"), QLineEdit::Normal, m_label->text(), &ok);
0259     if (!ok || (oldName == newName)) { // handle cancel
0260         return;
0261     }
0262 
0263     setName(newName);
0264 }
0265 
0266 void KNote::slotUpdateReadOnly()
0267 {
0268     const bool readOnly = m_readOnly->isChecked();
0269 
0270     m_editor->setReadOnly(readOnly);
0271 
0272     if (mItem.hasAttribute<NoteShared::NoteLockAttribute>()) {
0273         if (!readOnly) {
0274             mItem.removeAttribute<NoteShared::NoteLockAttribute>();
0275         }
0276     } else {
0277         if (readOnly) {
0278             mItem.attribute<NoteShared::NoteLockAttribute>(Akonadi::Item::AddIfMissing);
0279         }
0280     }
0281     if (!mBlockSave) {
0282         updateAllAttributes();
0283         auto job = new Akonadi::ItemModifyJob(mItem);
0284 #ifdef DEBUG_SAVE_NOTE
0285         qCDebug(KNOTES_LOG) << " void KNote::slotUpdateReadOnly() slotNoteSaved(KJob*)";
0286 #endif
0287         connect(job, &Akonadi::ItemModifyJob::result, this, &KNote::slotNoteSaved);
0288     }
0289 
0290     // enable/disable actions accordingly
0291     actionCollection()->action(QStringLiteral("configure_note"))->setEnabled(!readOnly);
0292     actionCollection()->action(QStringLiteral("delete_note"))->setEnabled(!readOnly);
0293     actionCollection()->action(QStringLiteral("format_bold"))->setEnabled(!readOnly);
0294     actionCollection()->action(QStringLiteral("format_italic"))->setEnabled(!readOnly);
0295     actionCollection()->action(QStringLiteral("format_underline"))->setEnabled(!readOnly);
0296     actionCollection()->action(QStringLiteral("format_strikeout"))->setEnabled(!readOnly);
0297     actionCollection()->action(QStringLiteral("format_alignleft"))->setEnabled(!readOnly);
0298     actionCollection()->action(QStringLiteral("format_aligncenter"))->setEnabled(!readOnly);
0299     actionCollection()->action(QStringLiteral("format_alignright"))->setEnabled(!readOnly);
0300     actionCollection()->action(QStringLiteral("format_alignblock"))->setEnabled(!readOnly);
0301     actionCollection()->action(QStringLiteral("format_list"))->setEnabled(!readOnly);
0302     actionCollection()->action(QStringLiteral("format_super"))->setEnabled(!readOnly);
0303     actionCollection()->action(QStringLiteral("format_sub"))->setEnabled(!readOnly);
0304     actionCollection()->action(QStringLiteral("format_increaseindent"))->setEnabled(!readOnly);
0305     actionCollection()->action(QStringLiteral("format_decreaseindent"))->setEnabled(!readOnly);
0306     actionCollection()->action(QStringLiteral("text_background_color"))->setEnabled(!readOnly);
0307     actionCollection()->action(QStringLiteral("format_size"))->setEnabled(!readOnly);
0308     actionCollection()->action(QStringLiteral("format_color"))->setEnabled(!readOnly);
0309     actionCollection()->action(QStringLiteral("rename_note"))->setEnabled(!readOnly);
0310     actionCollection()->action(QStringLiteral("set_alarm"))->setEnabled(!readOnly);
0311     m_keepAbove->setEnabled(!readOnly);
0312     m_keepBelow->setEnabled(!readOnly);
0313 
0314 #if KDEPIM_HAVE_X11
0315     m_toDesktop->setEnabled(!readOnly);
0316 #endif
0317 
0318     updateFocus();
0319 }
0320 
0321 void KNote::updateAllAttributes()
0322 {
0323     auto attribute = mItem.attribute<NoteShared::NoteDisplayAttribute>(Akonadi::Item::AddIfMissing);
0324 #if KDEPIM_HAVE_X11
0325     KWindowInfo info(winId(), NET::WMDesktop);
0326     const int count = KX11Extras::numberOfDesktops();
0327     for (int n = 1; n <= count; ++n) {
0328         if (info.isOnDesktop(n)) {
0329             attribute->setDesktop(n);
0330         }
0331     }
0332 #endif
0333     saveNoteContent();
0334     attribute->setIsHidden(true);
0335     attribute->setPosition(pos());
0336     const QSize currentSize(QSize(width(), height()));
0337     if (attribute->size() != currentSize) {
0338         attribute->setSize(currentSize);
0339     }
0340 }
0341 
0342 void KNote::slotClose()
0343 {
0344     updateAllAttributes();
0345     m_editor->clearFocus();
0346     auto job = new Akonadi::ItemModifyJob(mItem);
0347 #ifdef DEBUG_SAVE_NOTE
0348     qCDebug(KNOTES_LOG) << "slotClose() slotNoteSaved(KJob*)";
0349 #endif
0350     connect(job, &Akonadi::ItemModifyJob::result, this, &KNote::slotNoteSaved);
0351     hide();
0352 }
0353 
0354 void KNote::slotSetAlarm()
0355 {
0356     QPointer<NoteShared::NoteAlarmDialog> dlg = new NoteShared::NoteAlarmDialog(name(), this);
0357     if (mItem.hasAttribute<NoteShared::NoteAlarmAttribute>()) {
0358         dlg->setAlarm(mItem.attribute<NoteShared::NoteAlarmAttribute>()->dateTime());
0359     }
0360     if (dlg->exec()) {
0361         bool needToModify = true;
0362         QDateTime dateTime = dlg->alarm();
0363         if (dateTime.isValid()) {
0364             auto attribute = mItem.attribute<NoteShared::NoteAlarmAttribute>(Akonadi::Item::AddIfMissing);
0365             attribute->setDateTime(dateTime);
0366         } else {
0367             if (mItem.hasAttribute<NoteShared::NoteAlarmAttribute>()) {
0368                 mItem.removeAttribute<NoteShared::NoteAlarmAttribute>();
0369             } else {
0370                 needToModify = false;
0371             }
0372         }
0373         if (needToModify) {
0374             // Verify it!
0375             saveNoteContent();
0376             auto job = new Akonadi::ItemModifyJob(mItem);
0377 #ifdef DEBUG_SAVE_NOTE
0378             qCDebug(KNOTES_LOG) << "setAlarm() slotNoteSaved(KJob*)";
0379 #endif
0380             connect(job, &Akonadi::ItemModifyJob::result, this, &KNote::slotNoteSaved);
0381         }
0382     }
0383     delete dlg;
0384 }
0385 
0386 void KNote::saveNoteContent()
0387 {
0388     auto message = mItem.payload<KMime::Message::Ptr>();
0389     const QByteArray encoding("utf-8");
0390     message->subject(true)->fromUnicodeString(name(), encoding);
0391     message->contentType(true)->setMimeType(m_editor->acceptRichText() ? "text/html" : "text/plain");
0392     message->contentType()->setCharset(encoding);
0393     message->contentTransferEncoding(true)->setEncoding(KMime::Headers::CEquPr);
0394     message->date(true)->setDateTime(QDateTime::currentDateTime());
0395     message->mainBodyPart()->fromUnicodeString(text().isEmpty() ? QStringLiteral(" ") : text());
0396 
0397     auto header = new KMime::Headers::Generic("X-Cursor-Position");
0398     header->fromUnicodeString(QString::number(m_editor->cursorPositionFromStart()), "utf-8");
0399     message->setHeader(header);
0400 
0401     message->assemble();
0402 
0403     mItem.setPayload(message);
0404 }
0405 
0406 void KNote::slotPreferences()
0407 {
0408     // create a new preferences dialog...
0409     QPointer<KNoteSimpleConfigDialog> dialog = new KNoteSimpleConfigDialog(name(), this);
0410     auto attribute = mItem.attribute<NoteShared::NoteDisplayAttribute>(Akonadi::Item::AddIfMissing);
0411     attribute->setSize(QSize(width(), height()));
0412 
0413     dialog->load(mItem, m_editor->acceptRichText());
0414     connect(this, &KNote::sigNameChanged, dialog.data(), &KNoteSimpleConfigDialog::slotUpdateCaption);
0415     if (dialog->exec()) {
0416         bool isRichText;
0417         dialog->save(mItem, isRichText);
0418         m_editor->setAcceptRichText(isRichText);
0419         saveNoteContent();
0420         auto job = new Akonadi::ItemModifyJob(mItem);
0421 #ifdef DEBUG_SAVE_NOTE
0422         qCDebug(KNOTES_LOG) << "slotPreference slotNoteSaved(KJob*)";
0423 #endif
0424         connect(job, &Akonadi::ItemModifyJob::result, this, &KNote::slotNoteSaved);
0425     }
0426     delete dialog;
0427 }
0428 
0429 void KNote::slotSend()
0430 {
0431     NoteShared::NoteUtils noteUtils;
0432     noteUtils.sendToNetwork(this, name(), text());
0433 }
0434 
0435 void KNote::slotMail()
0436 {
0437     NoteShared::NoteUtils noteUtils;
0438     noteUtils.sendToMail(this, m_label->text(), m_editor->toPlainText());
0439 }
0440 
0441 void KNote::slotPrint()
0442 {
0443     print(false);
0444 }
0445 
0446 void KNote::slotPrintPreview()
0447 {
0448     print(true);
0449 }
0450 
0451 void KNote::print(bool preview)
0452 {
0453     if (isModified()) {
0454         saveNote();
0455     }
0456 
0457     KNotesGlobalConfig *globalConfig = KNotesGlobalConfig::self();
0458     QString printingTheme = globalConfig->theme();
0459     if (printingTheme.isEmpty()) {
0460         QPointer<KNotePrintSelectThemeDialog> dlg = new KNotePrintSelectThemeDialog(this);
0461         if (dlg->exec()) {
0462             printingTheme = dlg->selectedTheme();
0463         }
0464         delete dlg;
0465     }
0466     if (!printingTheme.isEmpty()) {
0467         KNotePrinter printer(this);
0468         QList<KNotePrintObject *> lst;
0469         lst.append(new KNotePrintObject(mItem));
0470         printer.setDefaultFont(mDisplayAttribute->font());
0471         printer.printNotes(lst, printingTheme, preview);
0472         qDeleteAll(lst);
0473     }
0474 }
0475 
0476 void KNote::slotSaveAs()
0477 {
0478     // TODO: where to put pdf file support? In the printer??!??!
0479     QUrl url;
0480     QPointer<KFileCustomDialog> dlg = new KFileCustomDialog(this);
0481     QCheckBox *convert = nullptr;
0482     if (m_editor->acceptRichText()) {
0483         convert = new QCheckBox(dlg.data());
0484         convert->setText(i18n("Save note as plain text"));
0485     }
0486     if (convert) {
0487         dlg->setCustomWidget(convert);
0488     }
0489     dlg->setUrl(url);
0490     dlg->setOperationMode(KFileWidget::Saving);
0491     dlg->setWindowTitle(i18nc("@title:window", "Save As"));
0492     if (!dlg->exec()) {
0493         delete dlg;
0494         return;
0495     }
0496 
0497     const QString fileName = dlg->fileWidget()->selectedFile();
0498     const bool htmlFormatAndSaveAsHtml = (convert && !convert->isChecked());
0499     delete dlg;
0500     if (fileName.isEmpty()) {
0501         return;
0502     }
0503 
0504     QFile file(fileName);
0505 
0506     if (file.exists()
0507         && KMessageBox::warningContinueCancel(this,
0508                                               i18n("<qt>A file named <b>%1</b> already exists.<br />"
0509                                                    "Are you sure you want to overwrite it?</qt>",
0510                                                    QFileInfo(file).fileName()))
0511             != KMessageBox::Continue) {
0512         return;
0513     }
0514 
0515     if (file.open(QIODevice::WriteOnly)) {
0516         QTextStream stream(&file);
0517         if (htmlFormatAndSaveAsHtml) {
0518             QString htmlStr = m_editor->toHtml();
0519             htmlStr.replace(QStringLiteral("meta name=\"qrichtext\" content=\"1\""),
0520                             QStringLiteral("meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\""));
0521             stream << htmlStr;
0522         } else {
0523             stream << m_editor->toPlainText();
0524         }
0525     }
0526 }
0527 
0528 void KNote::slotPopupActionToDesktop(QAction *act)
0529 {
0530     const int id = act->data().toInt();
0531     toDesktop(id); // compensate for the menu separator, -1 == all desktops
0532 }
0533 
0534 // ------------------ private slots (configuration) ------------------ //
0535 
0536 void KNote::slotApplyConfig()
0537 {
0538     m_label->setFont(mDisplayAttribute->titleFont());
0539     m_editor->setTextFont(mDisplayAttribute->font());
0540     m_editor->setTabStop(mDisplayAttribute->tabSize());
0541     m_editor->setAutoIndentMode(mDisplayAttribute->autoIndent());
0542 
0543     setColor(mDisplayAttribute->foregroundColor(), mDisplayAttribute->backgroundColor());
0544 
0545     updateLayout();
0546 #if KDEPIM_HAVE_X11
0547     slotUpdateShowInTaskbar();
0548 #endif
0549     resize(mDisplayAttribute->size());
0550 }
0551 
0552 void KNote::slotKeepAbove()
0553 {
0554     if (m_keepBelow->isChecked()) {
0555         m_keepBelow->setChecked(false);
0556     }
0557     updateKeepAboveBelow();
0558 }
0559 
0560 void KNote::slotKeepBelow()
0561 {
0562     if (m_keepAbove->isChecked()) {
0563         m_keepAbove->setChecked(false);
0564     }
0565     updateKeepAboveBelow();
0566 }
0567 
0568 void KNote::updateKeepAboveBelow(bool save)
0569 {
0570     auto attribute = mItem.attribute<NoteShared::NoteDisplayAttribute>(Akonadi::Item::AddIfMissing);
0571     if (m_keepAbove->isChecked()) {
0572         attribute->setKeepAbove(true);
0573         attribute->setKeepBelow(false);
0574         windowHandle()->setFlag(Qt::WindowStaysOnTopHint, true);
0575     } else if (m_keepBelow->isChecked()) {
0576         attribute->setKeepAbove(false);
0577         attribute->setKeepBelow(true);
0578         windowHandle()->setFlag(Qt::WindowStaysOnBottomHint, true);
0579     } else {
0580         attribute->setKeepAbove(false);
0581         attribute->setKeepBelow(false);
0582         windowHandle()->setFlag(Qt::WindowStaysOnTopHint, false);
0583         windowHandle()->setFlag(Qt::WindowStaysOnBottomHint, false);
0584     }
0585     if (!mBlockSave && save) {
0586         saveNoteContent();
0587         auto job = new Akonadi::ItemModifyJob(mItem);
0588 #ifdef DEBUG_SAVE_NOTE
0589         qCDebug(KNOTES_LOG) << "slotUpdateKeepAboveBelow slotNoteSaved(KJob*)";
0590 #endif
0591         connect(job, &Akonadi::ItemModifyJob::result, this, &KNote::slotNoteSaved);
0592     }
0593 }
0594 
0595 void KNote::slotUpdateShowInTaskbar()
0596 {
0597 #if KDEPIM_HAVE_X11
0598     if (KWindowSystem::isPlatformX11()) {
0599         if (!mDisplayAttribute->showInTaskbar()) {
0600             KX11Extras::setState(winId(), KWindowInfo(winId(), NET::WMState).state() | NET::SkipTaskbar);
0601         } else {
0602             KX11Extras::clearState(winId(), NET::SkipTaskbar);
0603         }
0604     }
0605 #endif
0606 }
0607 
0608 void KNote::slotUpdateDesktopActions()
0609 {
0610 #if KDEPIM_HAVE_X11
0611     m_toDesktop->clear();
0612 
0613     QAction *act = m_toDesktop->addAction(i18n("&All Desktops"));
0614     KWindowInfo info(winId(), NET::WMDesktop);
0615 
0616     if (info.onAllDesktops()) {
0617         act->setChecked(true);
0618         act->setData(NETWinInfo::OnAllDesktops);
0619     }
0620     auto separator = new QAction(m_toDesktop);
0621     separator->setSeparator(true);
0622     m_toDesktop->addAction(separator);
0623     const int count = KX11Extras::numberOfDesktops();
0624     for (int n = 1; n <= count; ++n) {
0625         QAction *desktopAct = m_toDesktop->addAction(QStringLiteral("&%1 %2").arg(n).arg(KX11Extras::desktopName(n)));
0626         desktopAct->setData(n);
0627         if (info.isOnDesktop(n)) {
0628             desktopAct->setChecked(true);
0629         }
0630     }
0631 #endif
0632 }
0633 
0634 // -------------------- private methods -------------------- //
0635 
0636 void KNote::buildGui()
0637 {
0638     createNoteHeader();
0639     createNoteEditor(QString());
0640 
0641     KXMLGUIBuilder builder(this);
0642     KXMLGUIFactory factory(&builder, this);
0643     factory.addClient(this);
0644 
0645     m_menu = qobject_cast<QMenu *>(factory.container(QStringLiteral("note_context"), this));
0646     m_tool = qobject_cast<KToolBar *>(factory.container(QStringLiteral("note_tool"), this));
0647 
0648     createNoteFooter();
0649 }
0650 
0651 void KNote::createActions()
0652 {
0653     // create the menu items for the note - not the editor...
0654     // rename, mail, print, save as, insert date, alarm, close, delete, new note
0655     auto action = new QAction(QIcon::fromTheme(QStringLiteral("document-new")), i18n("New"), this);
0656     actionCollection()->addAction(QStringLiteral("new_note"), action);
0657     connect(action, &QAction::triggered, this, &KNote::slotRequestNewNote);
0658 
0659     action = new QAction(QIcon::fromTheme(QStringLiteral("edit-rename")), i18n("Rename..."), this);
0660     actionCollection()->addAction(QStringLiteral("rename_note"), action);
0661     connect(action, &QAction::triggered, this, &KNote::slotRename);
0662 
0663     m_readOnly = new KToggleAction(QIcon::fromTheme(QStringLiteral("object-locked")), i18n("Lock"), this);
0664     actionCollection()->addAction(QStringLiteral("lock_note"), m_readOnly);
0665     connect(m_readOnly, &KToggleAction::triggered, this, &KNote::slotUpdateReadOnly);
0666     m_readOnly->setCheckedState(KGuiItem(i18n("Unlock"), QStringLiteral("object-unlocked")));
0667 
0668     action = new QAction(QIcon::fromTheme(QStringLiteral("window-close")), i18n("Hide"), this);
0669     actionCollection()->addAction(QStringLiteral("hide_note"), action);
0670     connect(action, &QAction::triggered, this, &KNote::slotClose);
0671     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::Key_Escape));
0672 
0673     action = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18n("Delete"), this);
0674     actionCollection()->addAction(QStringLiteral("delete_note"), action);
0675     connect(action, &QAction::triggered, this, &KNote::slotKill);
0676 
0677     action = new QAction(QIcon::fromTheme(QStringLiteral("knotes_alarm")), i18n("Set Alarm..."), this);
0678     actionCollection()->addAction(QStringLiteral("set_alarm"), action);
0679     connect(action, &QAction::triggered, this, &KNote::slotSetAlarm);
0680 
0681     action = new QAction(QIcon::fromTheme(QStringLiteral("network-wired")), i18n("Send..."), this);
0682     actionCollection()->addAction(QStringLiteral("send_note"), action);
0683     connect(action, &QAction::triggered, this, &KNote::slotSend);
0684 
0685     action = new QAction(QIcon::fromTheme(QStringLiteral("mail-send")), i18n("Mail..."), this);
0686     actionCollection()->addAction(QStringLiteral("mail_note"), action);
0687     connect(action, &QAction::triggered, this, &KNote::slotMail);
0688 
0689     action = new QAction(QIcon::fromTheme(QStringLiteral("document-save-as")), i18n("Save As..."), this);
0690     actionCollection()->addAction(QStringLiteral("save_note"), action);
0691     connect(action, &QAction::triggered, this, &KNote::slotSaveAs);
0692     action = actionCollection()->addAction(KStandardAction::Print, QStringLiteral("print_note"));
0693     connect(action, &QAction::triggered, this, &KNote::slotPrint);
0694 
0695     action = actionCollection()->addAction(KStandardAction::PrintPreview, QStringLiteral("print_preview_note"));
0696     connect(action, &QAction::triggered, this, &KNote::slotPrintPreview);
0697 
0698     action = new QAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Preferences..."), this);
0699     actionCollection()->addAction(QStringLiteral("configure_note"), action);
0700     connect(action, &QAction::triggered, this, &KNote::slotPreferences);
0701 
0702     m_keepAbove = new KToggleAction(QIcon::fromTheme(QStringLiteral("go-up")), i18n("Keep Above Others"), this);
0703     actionCollection()->addAction(QStringLiteral("keep_above"), m_keepAbove);
0704     connect(m_keepAbove, &KToggleAction::triggered, this, &KNote::slotKeepAbove);
0705 
0706     m_keepBelow = new KToggleAction(QIcon::fromTheme(QStringLiteral("go-down")), i18n("Keep Below Others"), this);
0707     actionCollection()->addAction(QStringLiteral("keep_below"), m_keepBelow);
0708     connect(m_keepBelow, &KToggleAction::triggered, this, &KNote::slotKeepBelow);
0709 
0710 #if KDEPIM_HAVE_X11
0711     m_toDesktop = new KSelectAction(i18n("To Desktop"), this);
0712     actionCollection()->addAction(QStringLiteral("to_desktop"), m_toDesktop);
0713     connect(m_toDesktop, &KSelectAction::actionTriggered, this, &KNote::slotPopupActionToDesktop);
0714     connect(m_toDesktop->menu(), &QMenu::aboutToShow, this, &KNote::slotUpdateDesktopActions);
0715     // initially populate it, otherwise stays disabled
0716     slotUpdateDesktopActions();
0717 #endif
0718     // invisible action to walk through the notes to make this configurable
0719     action = new QAction(i18n("Walk Through Notes"), this);
0720     actionCollection()->addAction(QStringLiteral("walk_notes"), action);
0721     connect(action, &QAction::triggered, this, &KNote::sigShowNextNote);
0722     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::SHIFT | Qt::Key_Backtab));
0723 
0724     actionCollection()->addAssociatedWidget(this);
0725     const auto lst = actionCollection()->actions();
0726     for (QAction *act : lst) {
0727         act->setShortcutContext(Qt::WidgetWithChildrenShortcut);
0728     }
0729     if (mAllowDebugAkonadiSearch) {
0730         // Don't translate it it's just for debugging
0731         action = new QAction(QStringLiteral("Debug Akonadi Search..."), this);
0732         actionCollection()->addAction(QStringLiteral("debug_akonadi_search"), action);
0733         connect(action, &QAction::triggered, this, &KNote::slotDebugAkonadiSearch);
0734     }
0735 }
0736 
0737 void KNote::createNoteHeader()
0738 {
0739     // load style configuration
0740     KConfigGroup styleGroup(m_kwinConf, QStringLiteral("Style"));
0741 
0742     QBoxLayout::Direction headerLayoutDirection = QBoxLayout::LeftToRight;
0743 
0744     if (styleGroup.readEntry("CustomButtonPositions", false)) {
0745         if (styleGroup.readEntry("ButtonsOnLeft").contains(QLatin1Char('X'))) {
0746             headerLayoutDirection = QBoxLayout::RightToLeft;
0747         }
0748     }
0749 
0750     auto headerLayout = new QBoxLayout(headerLayoutDirection);
0751 
0752     // create header label
0753     m_label = new QLabel(this);
0754     headerLayout->addWidget(m_label);
0755     m_label->setFrameStyle(NoFrame);
0756     m_label->setBackgroundRole(QPalette::Base);
0757     m_label->setLineWidth(0);
0758     m_label->setAutoFillBackground(true);
0759     m_label->installEventFilter(this); // receive events ( for dragging &
0760     // action menu )
0761     m_button = new KNoteButton(QStringLiteral("knotes_close"), this);
0762     headerLayout->addWidget(m_button);
0763 
0764     connect(m_button, &KNoteButton::clicked, this, &KNote::slotClose);
0765 
0766     m_noteLayout->addLayout(headerLayout);
0767 }
0768 
0769 void KNote::createNoteEditor(const QString &configFile)
0770 {
0771     Q_UNUSED(configFile)
0772     m_editor = new KNoteEdit(actionCollection(), this);
0773     m_noteLayout->addWidget(m_editor);
0774     m_editor->setNote(this);
0775     m_editor->installEventFilter(this); // receive focus events for modified
0776     setFocusProxy(m_editor);
0777 }
0778 
0779 void KNote::slotRequestNewNote()
0780 {
0781     // Be sure to save before to request a new note
0782     saveNote();
0783     Q_EMIT sigRequestNewNote();
0784 }
0785 
0786 void KNote::createNoteFooter()
0787 {
0788     if (m_tool) {
0789         m_tool->setIconSize(QSize(10, 10));
0790         m_tool->setFixedHeight(24);
0791         m_tool->setToolButtonStyle(Qt::ToolButtonIconOnly);
0792     }
0793 
0794     // create size grip
0795     auto gripLayout = new QHBoxLayout;
0796     m_grip = new QSizeGrip(this);
0797     m_grip->setFixedSize(m_grip->sizeHint());
0798 
0799     if (m_tool) {
0800         gripLayout->addWidget(m_tool);
0801         gripLayout->setAlignment(m_tool, Qt::AlignBottom | Qt::AlignLeft);
0802         m_tool->hide();
0803     }
0804 
0805     gripLayout->addWidget(m_grip);
0806     gripLayout->setAlignment(m_grip, Qt::AlignBottom | Qt::AlignRight);
0807     m_noteLayout->addLayout(gripLayout);
0808 
0809     // if there was just a way of making KComboBox adhere the toolbar height...
0810     if (m_tool) {
0811         const auto comboboxs = m_tool->findChildren<KComboBox *>();
0812         for (KComboBox *combo : comboboxs) {
0813             QFont font = combo->font();
0814             font.setPointSize(7);
0815             combo->setFont(font);
0816             combo->setFixedHeight(14);
0817         }
0818     }
0819 }
0820 
0821 void KNote::loadNoteContent(const Akonadi::Item &item)
0822 {
0823     auto noteMessage = item.payload<KMime::Message::Ptr>();
0824     const KMime::Headers::Subject *const subject = noteMessage ? noteMessage->subject(false) : nullptr;
0825     setName(subject ? subject->asUnicodeString() : QString());
0826     if (noteMessage->contentType()->isHTMLText()) {
0827         m_editor->setAcceptRichText(true);
0828         m_editor->setAutoFormatting(QTextEdit::AutoAll);
0829         m_editor->setHtml(noteMessage->mainBodyPart()->decodedText());
0830     } else {
0831         m_editor->setAcceptRichText(false);
0832         m_editor->setAutoFormatting(QTextEdit::AutoNone);
0833         m_editor->setPlainText(noteMessage->mainBodyPart()->decodedText());
0834     }
0835     if (auto hrd = noteMessage->headerByType("X-Cursor-Position")) {
0836         m_editor->setCursorPositionFromStart(hrd->asUnicodeString().toInt());
0837     }
0838 }
0839 
0840 void KNote::prepare()
0841 {
0842     mBlockSave = true;
0843     loadNoteContent(mItem);
0844 
0845     resize(mDisplayAttribute->size());
0846     const QPoint &position = mDisplayAttribute->position();
0847     QRect desk = qApp->primaryScreen()->virtualGeometry();
0848     desk.adjust(10, 10, -10, -10);
0849     if (desk.intersects(QRect(position, mDisplayAttribute->size()))) {
0850         move(position); // do before calling show() to avoid flicker
0851     }
0852     if (mDisplayAttribute->isHidden()) {
0853         hide();
0854     } else {
0855         show();
0856     }
0857     // read configuration settings...
0858     slotApplyConfig();
0859 
0860     if (mItem.hasAttribute<NoteShared::NoteLockAttribute>()) {
0861         m_editor->setReadOnly(true);
0862         m_readOnly->setChecked(true);
0863     } else {
0864         m_readOnly->setChecked(false);
0865     }
0866     slotUpdateReadOnly();
0867     // if this is a new note put on current desktop - we can't use defaults
0868     // in KConfig XT since only _changes_ will be stored in the config file
0869     int desktop = mDisplayAttribute->desktop();
0870 
0871 #if KDEPIM_HAVE_X11
0872     if ((desktop < 0 && desktop != NETWinInfo::OnAllDesktops) || !mDisplayAttribute->rememberDesktop()) {
0873         desktop = KX11Extras::currentDesktop();
0874     }
0875 #endif
0876 
0877     // show the note if desired
0878     if (desktop != 0 && !mDisplayAttribute->isHidden()) {
0879         // to avoid flicker, call this before show()
0880         toDesktop(desktop);
0881         show();
0882 
0883         // because KWin forgets about that for hidden windows
0884 #if KDEPIM_HAVE_X11
0885         if (desktop == NETWinInfo::OnAllDesktops) {
0886             toDesktop(desktop);
0887         }
0888 #endif
0889     }
0890 
0891     if (mDisplayAttribute->keepAbove()) {
0892         m_keepAbove->setChecked(true);
0893     } else if (mDisplayAttribute->keepBelow()) {
0894         m_keepBelow->setChecked(true);
0895     } else {
0896         m_keepAbove->setChecked(false);
0897         m_keepBelow->setChecked(false);
0898     }
0899 
0900     updateKeepAboveBelow();
0901 
0902     // set up the look&feel of the note
0903     setFrameStyle(Panel | Raised);
0904     setMinimumSize(20, 20);
0905     setBackgroundRole(QPalette::Base);
0906 
0907     m_editor->setContentsMargins(0, 0, 0, 0);
0908     m_editor->setBackgroundRole(QPalette::Base);
0909     m_editor->setFrameStyle(NoFrame);
0910     m_editor->document()->setModified(false);
0911     mBlockSave = false;
0912 }
0913 
0914 void KNote::toDesktop(int desktop)
0915 {
0916     if (desktop == 0) {
0917         return;
0918     }
0919 
0920 #if KDEPIM_HAVE_X11
0921     if (desktop == NETWinInfo::OnAllDesktops) {
0922         KX11Extras::setOnAllDesktops(winId(), true);
0923     } else {
0924         KX11Extras::setOnDesktop(winId(), desktop);
0925     }
0926 #endif
0927 }
0928 
0929 void KNote::setColor(const QColor &fg, const QColor &bg)
0930 {
0931     m_editor->setColor(fg, bg);
0932     QPalette p = palette();
0933 
0934     // better: from light(150) to light(100) to light(75)
0935     // QLinearGradient g( width()/2, 0, width()/2, height() );
0936     // g.setColorAt( 0, bg );
0937     // g.setColorAt( 1, bg.darker(150) );
0938 
0939     p.setColor(QPalette::Window, bg);
0940     // p.setBrush( QPalette::Window,     g );
0941     p.setColor(QPalette::Base, bg);
0942     // p.setBrush( QPalette::Base,       g );
0943 
0944     p.setColor(QPalette::WindowText, fg);
0945     p.setColor(QPalette::Text, fg);
0946 
0947     p.setColor(QPalette::Button, bg.darker(116));
0948     p.setColor(QPalette::ButtonText, fg);
0949 
0950     // p.setColor( QPalette::Highlight,  bg );
0951     // p.setColor( QPalette::HighlightedText, fg );
0952 
0953     // order: Light, Midlight, Button, Mid, Dark, Shadow
0954 
0955     // the shadow
0956     p.setColor(QPalette::Light, bg.lighter(180));
0957     p.setColor(QPalette::Midlight, bg.lighter(150));
0958     p.setColor(QPalette::Mid, bg.lighter(150));
0959     p.setColor(QPalette::Dark, bg.darker(108));
0960     p.setColor(QPalette::Shadow, bg.darker(116));
0961 
0962     setPalette(p);
0963 
0964     // darker values for the active label
0965     p.setColor(QPalette::Active, QPalette::Base, bg.darker(116));
0966 
0967     m_label->setPalette(p);
0968 
0969     // set the text color
0970     m_editor->setTextColor(fg);
0971 
0972     // update the color of the title
0973     updateFocus();
0974     Q_EMIT sigColorChanged();
0975 }
0976 
0977 void KNote::updateLabelAlignment()
0978 {
0979     // if the name is too long to fit, left-align it, otherwise center it (#59028)
0980     const QString labelText = m_label->text();
0981     if (m_label->fontMetrics().boundingRect(labelText).width() > m_label->width()) {
0982         m_label->setAlignment(Qt::AlignLeft);
0983     } else {
0984         m_label->setAlignment(Qt::AlignHCenter);
0985     }
0986 }
0987 
0988 void KNote::updateFocus()
0989 {
0990     if (hasFocus()) {
0991         if (!m_editor->isReadOnly()) {
0992             if (m_tool && m_tool->isHidden() && m_editor->acceptRichText()) {
0993                 m_tool->show();
0994                 updateLayout();
0995             }
0996             m_grip->show();
0997         } else {
0998             if (m_tool && !m_tool->isHidden()) {
0999                 m_tool->hide();
1000                 updateLayout(); // to update the minimum height
1001             }
1002             m_grip->hide();
1003         }
1004     } else {
1005         m_grip->hide();
1006 
1007         if (m_tool && !m_tool->isHidden()) {
1008             m_tool->hide();
1009             updateLayout(); // to update the minimum height
1010         }
1011     }
1012 }
1013 
1014 void KNote::updateLayout()
1015 {
1016     // TODO: remove later if no longer needed.
1017     updateLabelAlignment();
1018 }
1019 
1020 // -------------------- protected methods -------------------- //
1021 
1022 void KNote::contextMenuEvent(QContextMenuEvent *e)
1023 {
1024     if (m_menu) {
1025         m_menu->popup(e->globalPos());
1026     }
1027 }
1028 
1029 void KNote::showEvent(QShowEvent *)
1030 {
1031     if (mDisplayAttribute->isHidden()) {
1032         // KWin does not preserve these properties for hidden windows
1033         updateKeepAboveBelow(false);
1034 #if KDEPIM_HAVE_X11
1035         slotUpdateShowInTaskbar();
1036 #endif
1037         toDesktop(mDisplayAttribute->desktop());
1038         move(mDisplayAttribute->position());
1039         auto attr = mItem.attribute<NoteShared::NoteDisplayAttribute>(Akonadi::Item::AddIfMissing);
1040         saveNoteContent();
1041         attr->setIsHidden(false);
1042         if (!mBlockSave) {
1043             auto job = new Akonadi::ItemModifyJob(mItem);
1044 #ifdef DEBUG_SAVE_NOTE
1045             qCDebug(KNOTES_LOG) << "showEvent slotNoteSaved(KJob*)";
1046 #endif
1047             connect(job, &Akonadi::ItemModifyJob::result, this, &KNote::slotNoteSaved);
1048         }
1049     }
1050 }
1051 
1052 void KNote::resizeEvent(QResizeEvent *qre)
1053 {
1054     QFrame::resizeEvent(qre);
1055     updateLayout();
1056 }
1057 
1058 void KNote::closeEvent(QCloseEvent *event)
1059 {
1060     if (qApp->isSavingSession()) {
1061         return;
1062     }
1063     event->ignore(); // We don't want to close (and delete the widget). Just hide it
1064     slotClose();
1065 }
1066 
1067 void KNote::dragEnterEvent(QDragEnterEvent *e)
1068 {
1069     if (!m_editor->isReadOnly()) {
1070         e->setAccepted(e->mimeData()->hasColor());
1071     }
1072 }
1073 
1074 void KNote::dropEvent(QDropEvent *e)
1075 {
1076     if (m_editor->isReadOnly()) {
1077         return;
1078     }
1079 
1080     const QMimeData *md = e->mimeData();
1081     if (md->hasColor()) {
1082         const auto bg = qvariant_cast<QColor>(md->colorData());
1083 
1084         auto attr = mItem.attribute<NoteShared::NoteDisplayAttribute>(Akonadi::Item::AddIfMissing);
1085         saveNoteContent();
1086         attr->setBackgroundColor(bg);
1087         auto job = new Akonadi::ItemModifyJob(mItem);
1088 #ifdef DEBUG_SAVE_NOTE
1089         qCDebug(KNOTES_LOG) << "dropEvent slotNoteSaved(KJob*)";
1090 #endif
1091         connect(job, &Akonadi::ItemModifyJob::result, this, &KNote::slotNoteSaved);
1092     }
1093 }
1094 
1095 bool KNote::event(QEvent *ev)
1096 {
1097     if (ev->type() == QEvent::LayoutRequest) {
1098         updateLayout();
1099         return true;
1100     } else {
1101         return QFrame::event(ev);
1102     }
1103 }
1104 
1105 bool KNote::eventFilter(QObject *o, QEvent *ev)
1106 {
1107     if (ev->type() == QEvent::DragEnter && static_cast<QDragEnterEvent *>(ev)->mimeData()->hasColor()) {
1108         dragEnterEvent(static_cast<QDragEnterEvent *>(ev));
1109         return true;
1110     }
1111 
1112     if (ev->type() == QEvent::Drop && static_cast<QDropEvent *>(ev)->mimeData()->hasColor()) {
1113         dropEvent(static_cast<QDropEvent *>(ev));
1114         return true;
1115     }
1116 
1117     if (o == m_label) {
1118         auto e = (QMouseEvent *)ev;
1119 
1120         if (ev->type() == QEvent::MouseButtonDblClick) {
1121             if (!m_editor->isReadOnly()) {
1122                 slotRename();
1123             }
1124         }
1125 
1126         if (ev->type() == QEvent::MouseButtonPress
1127             && ((e->buttons() & Qt::LeftButton) == Qt::LeftButton || (e->buttons() & Qt::MiddleButton) == Qt::MiddleButton)) {
1128             mOrigPos = e->pos();
1129             return false;
1130         }
1131 
1132         if (ev->type() == QEvent::MouseMove && ((e->buttons() & Qt::LeftButton) == Qt::LeftButton || (e->buttons() & Qt::MiddleButton) == Qt::MiddleButton)) {
1133             QPoint newPos = e->globalPosition().toPoint() - mOrigPos - QPoint(1, 1);
1134             move(newPos);
1135             return true;
1136         }
1137 
1138         if (ev->type() == QEvent::MouseButtonRelease
1139             && ((e->buttons() & Qt::LeftButton) == Qt::LeftButton || (e->buttons() & Qt::MiddleButton) == Qt::MiddleButton)) {
1140             QPoint newPos = e->globalPosition().toPoint() - mOrigPos - QPoint(1, 1);
1141             move(newPos);
1142             return false;
1143         }
1144         return false;
1145     }
1146 
1147     if (o == m_editor) {
1148         if (ev->type() == QEvent::FocusOut) {
1149             auto fe = static_cast<QFocusEvent *>(ev);
1150             if (fe->reason() != Qt::PopupFocusReason && fe->reason() != Qt::MouseFocusReason) {
1151                 updateFocus();
1152                 if (!mBlockSave) {
1153                     saveNote(true);
1154                 }
1155             }
1156         } else if (ev->type() == QEvent::FocusIn) {
1157             updateFocus();
1158         }
1159 
1160         return false;
1161     }
1162 
1163     return false;
1164 }
1165 
1166 Akonadi::Item KNote::item() const
1167 {
1168     return mItem;
1169 }
1170 
1171 void KNote::slotDebugAkonadiSearch()
1172 {
1173     QPointer<Akonadi::Search::AkonadiSearchDebugDialog> dlg = new Akonadi::Search::AkonadiSearchDebugDialog;
1174     dlg->setAkonadiId(mItem.id());
1175     dlg->setAttribute(Qt::WA_DeleteOnClose);
1176     dlg->setSearchType(Akonadi::Search::AkonadiSearchDebugSearchPathComboBox::Notes);
1177     dlg->doSearch();
1178     dlg->show();
1179 }
1180 
1181 #include "moc_knote.cpp"