1 /*******************************************************************
2  KNotes -- Notes for the KDE project
3 
4  SPDX-FileCopyrightText: 1997-2013 The KNotes Developers
5 
6  SPDX-License-Identifier: GPL-2.0-or-later
7 *******************************************************************/
8 
9 #include "knote.h"
10 #include "alarms/notealarmdialog.h"
11 #include "attributes/notealarmattribute.h"
12 #include "attributes/notedisplayattribute.h"
13 #include "attributes/notelockattribute.h"
14 #include "configdialog/knotesimpleconfigdialog.h"
15 #include "knotedisplaysettings.h"
16 #include "knoteedit.h"
17 #include "knotes_debug.h"
18 #include "knotesglobalconfig.h"
19 #include "notes/knotebutton.h"
20 #include "noteutils.h"
21 #include "print/knoteprinter.h"
22 #include "print/knoteprintobject.h"
23 #include "print/knoteprintselectthemedialog.h"
24 #include "utils/knoteutils.h"
25 
26 #include <Akonadi/ItemModifyJob>
27 
28 #include <AkonadiSearch/Debug/akonadisearchdebugdialog.h>
29 
30 #include <KMime/KMimeMessage>
31 
32 #include <KActionCollection>
33 #include <KComboBox>
34 #include <KFileCustomDialog>
35 #include <KIconEffect>
36 #include <KLocalizedString>
37 #include <KMessageBox>
38 #include <KToggleAction>
39 #include <KToolBar>
40 #include <KWindowSystem>
41 #include <KXMLGUIBuilder>
42 #include <KXMLGUIFactory>
43 
44 #include <QApplication>
45 #include <QCheckBox>
46 #include <QDesktopWidget>
47 #include <QInputDialog>
48 #include <QLabel>
49 #include <QMenu>
50 #include <QMimeData>
51 #include <QPointer>
52 #include <QSizeGrip>
53 #include <QTextEdit>
54 #include <QVBoxLayout>
55 
56 #if KDEPIM_HAVE_X11
57 #include <KWindowSystem/NETWM>
58 #include <fixx11h.h>
59 #endif
60 
61 //#define DEBUG_SAVE_NOTE 1
62 
KNote(const QDomDocument & buildDoc,const Akonadi::Item & item,bool allowAkonadiSearchDebug,QWidget * parent)63 KNote::KNote(const QDomDocument &buildDoc, const Akonadi::Item &item, bool allowAkonadiSearchDebug, QWidget *parent)
64     : QFrame(parent, Qt::FramelessWindowHint)
65     , mItem(item)
66     , m_kwinConf(KSharedConfig::openConfig(QStringLiteral("kwinrc")))
67     , mDisplayAttribute(new KNoteDisplaySettings)
68     , mAllowDebugAkonadiSearch(allowAkonadiSearchDebug)
69 {
70     if (mItem.hasAttribute<NoteShared::NoteDisplayAttribute>()) {
71         mDisplayAttribute->setDisplayAttribute(mItem.attribute<NoteShared::NoteDisplayAttribute>());
72     } else {
73         setDisplayDefaultValue();
74         // save default display value
75     }
76     setAcceptDrops(true);
77     setAttribute(Qt::WA_DeleteOnClose);
78     setDOMDocument(buildDoc);
79     setXMLFile(componentName() + QLatin1String("ui.rc"), false, false);
80 
81     // create the main layout
82     m_noteLayout = new QVBoxLayout(this);
83     m_noteLayout->setContentsMargins(0, 0, 0, 0);
84     createActions();
85 
86     buildGui();
87     prepare();
88 }
89 
~KNote()90 KNote::~KNote()
91 {
92     delete mDisplayAttribute;
93 }
94 
setDisplayDefaultValue()95 void KNote::setDisplayDefaultValue()
96 {
97     KNoteUtils::setDefaultValue(mItem);
98     auto job = new Akonadi::ItemModifyJob(mItem);
99 #ifdef DEBUG_SAVE_NOTE
100     qCDebug(KNOTES_LOG) << "setDisplayDefaultValue slotNoteSaved(KJob*)";
101 #endif
102     connect(job, &Akonadi::ItemModifyJob::result, this, &KNote::slotNoteSaved);
103 }
104 
setChangeItem(const Akonadi::Item & item,const QSet<QByteArray> & set)105 void KNote::setChangeItem(const Akonadi::Item &item, const QSet<QByteArray> &set)
106 {
107     mItem = item;
108     if (item.hasAttribute<NoteShared::NoteDisplayAttribute>()) {
109         mDisplayAttribute->setDisplayAttribute(item.attribute<NoteShared::NoteDisplayAttribute>());
110     }
111     if (set.contains("KJotsLockAttribute")) {
112         m_editor->setReadOnly(item.hasAttribute<NoteShared::NoteLockAttribute>());
113     }
114     if (set.contains("PLD:RFC822")) {
115         loadNoteContent(item);
116     }
117     if (set.contains("NoteDisplayAttribute")) {
118         qCDebug(KNOTES_LOG) << " ATR:NoteDisplayAttribute";
119         slotApplyConfig();
120     }
121     // TODO update display/content etc.
122     updateLabelAlignment();
123 }
124 
slotKill(bool force)125 void KNote::slotKill(bool force)
126 {
127     if (!force
128         && KMessageBox::warningContinueCancel(this,
129                                               i18n("<qt>Do you really want to delete note <b>%1</b>?</qt>", m_label->text()),
130                                               i18n("Confirm Delete"),
131                                               KGuiItem(i18n("&Delete"), QStringLiteral("edit-delete")),
132                                               KStandardGuiItem::cancel(),
133                                               QStringLiteral("ConfirmDeleteNote"))
134             != KMessageBox::Continue) {
135         return;
136     }
137 
138     Q_EMIT sigKillNote(mItem.id());
139 }
140 
141 // -------------------- public member functions -------------------- //
142 
saveNote(bool force,bool sync)143 void KNote::saveNote(bool force, bool sync)
144 {
145     if (!force && !m_editor->document()->isModified()) {
146         return;
147     }
148     bool needToSave = false;
149     auto attribute = mItem.attribute<NoteShared::NoteDisplayAttribute>(Akonadi::Item::AddIfMissing);
150     const QPoint notePosition = pos();
151     if (attribute->position() != notePosition) {
152         needToSave = true;
153         attribute->setPosition(notePosition);
154     }
155     const QSize currentSize(QSize(width(), height()));
156     if (attribute->size() != currentSize) {
157         needToSave = true;
158         attribute->setSize(currentSize);
159     }
160 #if KDEPIM_HAVE_X11
161     KWindowInfo info(winId(), NET::WMDesktop);
162     const int count = KWindowSystem::numberOfDesktops();
163     for (int n = 1; n <= count; ++n) {
164         if (info.isOnDesktop(n)) {
165             if (attribute->desktop() != n) {
166                 needToSave = true;
167                 attribute->setDesktop(n);
168                 break;
169             }
170         }
171     }
172 #endif
173     if (m_editor->document()->isModified()) {
174         needToSave = true;
175         saveNoteContent();
176     }
177     if (needToSave) {
178 #ifdef DEBUG_SAVE_NOTE
179         qCDebug(KNOTES_LOG) << "save Note slotClose() slotNoteSaved(KJob*) : sync" << sync;
180 #endif
181         auto job = new Akonadi::ItemModifyJob(mItem);
182         if (sync) {
183             job->exec();
184         } else {
185 #ifdef DEBUG_SAVE_NOTE
186             qCDebug(KNOTES_LOG) << "save Note slotClose() slotNoteSaved(KJob*)";
187 #endif
188             connect(job, &Akonadi::ItemModifyJob::result, this, &KNote::slotNoteSaved);
189         }
190     }
191 }
192 
slotNoteSaved(KJob * job)193 void KNote::slotNoteSaved(KJob *job)
194 {
195     qCDebug(KNOTES_LOG) << " void KNote::slotNoteSaved(KJob *job)";
196     if (job->error()) {
197         qCDebug(KNOTES_LOG) << " problem during save note:" << job->errorString();
198     } else {
199         m_editor->document()->setModified(false);
200     }
201 }
202 
noteId() const203 Akonadi::Item::Id KNote::noteId() const
204 {
205     return mItem.id();
206 }
207 
name() const208 QString KNote::name() const
209 {
210     return m_label->text();
211 }
212 
text() const213 QString KNote::text() const
214 {
215     return m_editor->text();
216 }
217 
setName(const QString & name)218 void KNote::setName(const QString &name)
219 {
220     m_label->setText(name);
221     updateLabelAlignment();
222 
223     if (m_editor) { // not called from CTOR?
224         saveNote();
225     }
226     setWindowTitle(name);
227 
228     Q_EMIT sigNameChanged(name);
229 }
230 
setText(const QString & text)231 void KNote::setText(const QString &text)
232 {
233     m_editor->setText(text);
234 
235     saveNote();
236 }
237 
isDesktopAssigned() const238 bool KNote::isDesktopAssigned() const
239 {
240     return mDisplayAttribute->rememberDesktop();
241 }
242 
isModified() const243 bool KNote::isModified() const
244 {
245     return m_editor->document()->isModified();
246 }
247 
248 // ------------------ private slots (menu actions) ------------------ //
249 
slotRename()250 void KNote::slotRename()
251 {
252     // pop up dialog to get the new name
253     bool ok;
254     const QString oldName = m_label->text();
255     const QString newName = QInputDialog::getText(this, QString(), i18n("Please enter the new name:"), QLineEdit::Normal, m_label->text(), &ok);
256     if (!ok || (oldName == newName)) { // handle cancel
257         return;
258     }
259 
260     setName(newName);
261 }
262 
slotUpdateReadOnly()263 void KNote::slotUpdateReadOnly()
264 {
265     const bool readOnly = m_readOnly->isChecked();
266 
267     m_editor->setReadOnly(readOnly);
268 
269     if (mItem.hasAttribute<NoteShared::NoteLockAttribute>()) {
270         if (!readOnly) {
271             mItem.removeAttribute<NoteShared::NoteLockAttribute>();
272         }
273     } else {
274         if (readOnly) {
275             mItem.attribute<NoteShared::NoteLockAttribute>(Akonadi::Item::AddIfMissing);
276         }
277     }
278     if (!mBlockSave) {
279         updateAllAttributes();
280         auto job = new Akonadi::ItemModifyJob(mItem);
281 #ifdef DEBUG_SAVE_NOTE
282         qCDebug(KNOTES_LOG) << " void KNote::slotUpdateReadOnly() slotNoteSaved(KJob*)";
283 #endif
284         connect(job, &Akonadi::ItemModifyJob::result, this, &KNote::slotNoteSaved);
285     }
286 
287     // enable/disable actions accordingly
288     actionCollection()->action(QStringLiteral("configure_note"))->setEnabled(!readOnly);
289     actionCollection()->action(QStringLiteral("delete_note"))->setEnabled(!readOnly);
290     actionCollection()->action(QStringLiteral("format_bold"))->setEnabled(!readOnly);
291     actionCollection()->action(QStringLiteral("format_italic"))->setEnabled(!readOnly);
292     actionCollection()->action(QStringLiteral("format_underline"))->setEnabled(!readOnly);
293     actionCollection()->action(QStringLiteral("format_strikeout"))->setEnabled(!readOnly);
294     actionCollection()->action(QStringLiteral("format_alignleft"))->setEnabled(!readOnly);
295     actionCollection()->action(QStringLiteral("format_aligncenter"))->setEnabled(!readOnly);
296     actionCollection()->action(QStringLiteral("format_alignright"))->setEnabled(!readOnly);
297     actionCollection()->action(QStringLiteral("format_alignblock"))->setEnabled(!readOnly);
298     actionCollection()->action(QStringLiteral("format_list"))->setEnabled(!readOnly);
299     actionCollection()->action(QStringLiteral("format_super"))->setEnabled(!readOnly);
300     actionCollection()->action(QStringLiteral("format_sub"))->setEnabled(!readOnly);
301     actionCollection()->action(QStringLiteral("format_increaseindent"))->setEnabled(!readOnly);
302     actionCollection()->action(QStringLiteral("format_decreaseindent"))->setEnabled(!readOnly);
303     actionCollection()->action(QStringLiteral("text_background_color"))->setEnabled(!readOnly);
304     actionCollection()->action(QStringLiteral("format_size"))->setEnabled(!readOnly);
305     actionCollection()->action(QStringLiteral("format_color"))->setEnabled(!readOnly);
306     actionCollection()->action(QStringLiteral("rename_note"))->setEnabled(!readOnly);
307     actionCollection()->action(QStringLiteral("set_alarm"))->setEnabled(!readOnly);
308     m_keepAbove->setEnabled(!readOnly);
309     m_keepBelow->setEnabled(!readOnly);
310 
311 #if KDEPIM_HAVE_X11
312     m_toDesktop->setEnabled(!readOnly);
313 #endif
314 
315     updateFocus();
316 }
317 
updateAllAttributes()318 void KNote::updateAllAttributes()
319 {
320     auto attribute = mItem.attribute<NoteShared::NoteDisplayAttribute>(Akonadi::Item::AddIfMissing);
321 #if KDEPIM_HAVE_X11
322     KWindowInfo info(winId(), NET::WMDesktop);
323     const int count = KWindowSystem::numberOfDesktops();
324     for (int n = 1; n <= count; ++n) {
325         if (info.isOnDesktop(n)) {
326             attribute->setDesktop(n);
327         }
328     }
329 #endif
330     saveNoteContent();
331     attribute->setIsHidden(true);
332     attribute->setPosition(pos());
333     const QSize currentSize(QSize(width(), height()));
334     if (attribute->size() != currentSize) {
335         attribute->setSize(currentSize);
336     }
337 }
338 
slotClose()339 void KNote::slotClose()
340 {
341     updateAllAttributes();
342     m_editor->clearFocus();
343     auto job = new Akonadi::ItemModifyJob(mItem);
344 #ifdef DEBUG_SAVE_NOTE
345     qCDebug(KNOTES_LOG) << "slotClose() slotNoteSaved(KJob*)";
346 #endif
347     connect(job, &Akonadi::ItemModifyJob::result, this, &KNote::slotNoteSaved);
348     hide();
349 }
350 
slotSetAlarm()351 void KNote::slotSetAlarm()
352 {
353     QPointer<NoteShared::NoteAlarmDialog> dlg = new NoteShared::NoteAlarmDialog(name(), this);
354     if (mItem.hasAttribute<NoteShared::NoteAlarmAttribute>()) {
355         dlg->setAlarm(mItem.attribute<NoteShared::NoteAlarmAttribute>()->dateTime());
356     }
357     if (dlg->exec()) {
358         bool needToModify = true;
359         QDateTime dateTime = dlg->alarm();
360         if (dateTime.isValid()) {
361             auto attribute = mItem.attribute<NoteShared::NoteAlarmAttribute>(Akonadi::Item::AddIfMissing);
362             attribute->setDateTime(dateTime);
363         } else {
364             if (mItem.hasAttribute<NoteShared::NoteAlarmAttribute>()) {
365                 mItem.removeAttribute<NoteShared::NoteAlarmAttribute>();
366             } else {
367                 needToModify = false;
368             }
369         }
370         if (needToModify) {
371             // Verify it!
372             saveNoteContent();
373             auto job = new Akonadi::ItemModifyJob(mItem);
374 #ifdef DEBUG_SAVE_NOTE
375             qCDebug(KNOTES_LOG) << "setAlarm() slotNoteSaved(KJob*)";
376 #endif
377             connect(job, &Akonadi::ItemModifyJob::result, this, &KNote::slotNoteSaved);
378         }
379     }
380     delete dlg;
381 }
382 
saveNoteContent()383 void KNote::saveNoteContent()
384 {
385     auto message = mItem.payload<KMime::Message::Ptr>();
386     const QByteArray encoding("utf-8");
387     message->subject(true)->fromUnicodeString(name(), encoding);
388     message->contentType(true)->setMimeType(m_editor->acceptRichText() ? "text/html" : "text/plain");
389     message->contentType()->setCharset(encoding);
390     message->contentTransferEncoding(true)->setEncoding(KMime::Headers::CEquPr);
391     message->date(true)->setDateTime(QDateTime::currentDateTime());
392     message->mainBodyPart()->fromUnicodeString(text().isEmpty() ? QStringLiteral(" ") : text());
393 
394     auto header = new KMime::Headers::Generic("X-Cursor-Position");
395     header->fromUnicodeString(QString::number(m_editor->cursorPositionFromStart()), "utf-8");
396     message->setHeader(header);
397 
398     message->assemble();
399 
400     mItem.setPayload(message);
401 }
402 
slotPreferences()403 void KNote::slotPreferences()
404 {
405     // create a new preferences dialog...
406     QPointer<KNoteSimpleConfigDialog> dialog = new KNoteSimpleConfigDialog(name(), this);
407     auto attribute = mItem.attribute<NoteShared::NoteDisplayAttribute>(Akonadi::Item::AddIfMissing);
408     attribute->setSize(QSize(width(), height()));
409 
410     dialog->load(mItem, m_editor->acceptRichText());
411     connect(this, &KNote::sigNameChanged, dialog.data(), &KNoteSimpleConfigDialog::slotUpdateCaption);
412     if (dialog->exec()) {
413         bool isRichText;
414         dialog->save(mItem, isRichText);
415         m_editor->setAcceptRichText(isRichText);
416         saveNoteContent();
417         auto job = new Akonadi::ItemModifyJob(mItem);
418 #ifdef DEBUG_SAVE_NOTE
419         qCDebug(KNOTES_LOG) << "slotPreference slotNoteSaved(KJob*)";
420 #endif
421         connect(job, &Akonadi::ItemModifyJob::result, this, &KNote::slotNoteSaved);
422     }
423     delete dialog;
424 }
425 
slotSend()426 void KNote::slotSend()
427 {
428     NoteShared::NoteUtils noteUtils;
429     noteUtils.sendToNetwork(this, name(), text());
430 }
431 
slotMail()432 void KNote::slotMail()
433 {
434     NoteShared::NoteUtils noteUtils;
435     noteUtils.sendToMail(this, m_label->text(), m_editor->toPlainText());
436 }
437 
slotPrint()438 void KNote::slotPrint()
439 {
440     print(false);
441 }
442 
slotPrintPreview()443 void KNote::slotPrintPreview()
444 {
445     print(true);
446 }
447 
print(bool preview)448 void KNote::print(bool preview)
449 {
450     if (isModified()) {
451         saveNote();
452     }
453 
454     KNotesGlobalConfig *globalConfig = KNotesGlobalConfig::self();
455     QString printingTheme = globalConfig->theme();
456     if (printingTheme.isEmpty()) {
457         QPointer<KNotePrintSelectThemeDialog> dlg = new KNotePrintSelectThemeDialog(this);
458         if (dlg->exec()) {
459             printingTheme = dlg->selectedTheme();
460         }
461         delete dlg;
462     }
463     if (!printingTheme.isEmpty()) {
464         KNotePrinter printer(this);
465         QList<KNotePrintObject *> lst;
466         lst.append(new KNotePrintObject(mItem));
467         printer.setDefaultFont(mDisplayAttribute->font());
468         printer.printNotes(lst, printingTheme, preview);
469         qDeleteAll(lst);
470     }
471 }
472 
slotSaveAs()473 void KNote::slotSaveAs()
474 {
475     // TODO: where to put pdf file support? In the printer??!??!
476     QCheckBox *convert = nullptr;
477     if (m_editor->acceptRichText()) {
478         convert = new QCheckBox(nullptr);
479         convert->setText(i18n("Save note as plain text"));
480     }
481     QUrl url;
482     QPointer<KFileCustomDialog> dlg = new KFileCustomDialog(this);
483     dlg->setCustomWidget(convert);
484     dlg->setUrl(url);
485     dlg->setOperationMode(KFileWidget::Saving);
486     dlg->setWindowTitle(i18nc("@title:window", "Save As"));
487     if (!dlg->exec()) {
488         delete dlg;
489         return;
490     }
491 
492     const QString fileName = dlg->fileWidget()->selectedFile();
493     const bool htmlFormatAndSaveAsHtml = (convert && !convert->isChecked());
494     delete dlg;
495     if (fileName.isEmpty()) {
496         return;
497     }
498 
499     QFile file(fileName);
500 
501     if (file.exists()
502         && KMessageBox::warningContinueCancel(this,
503                                               i18n("<qt>A file named <b>%1</b> already exists.<br />"
504                                                    "Are you sure you want to overwrite it?</qt>",
505                                                    QFileInfo(file).fileName()))
506             != KMessageBox::Continue) {
507         return;
508     }
509 
510     if (file.open(QIODevice::WriteOnly)) {
511         QTextStream stream(&file);
512         if (htmlFormatAndSaveAsHtml) {
513             QString htmlStr = m_editor->toHtml();
514             htmlStr.replace(QStringLiteral("meta name=\"qrichtext\" content=\"1\""),
515                             QStringLiteral("meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\""));
516             stream << htmlStr;
517         } else {
518             stream << m_editor->toPlainText();
519         }
520     }
521 }
522 
slotPopupActionToDesktop(QAction * act)523 void KNote::slotPopupActionToDesktop(QAction *act)
524 {
525     const int id = act->data().toInt();
526     toDesktop(id); // compensate for the menu separator, -1 == all desktops
527 }
528 
529 // ------------------ private slots (configuration) ------------------ //
530 
slotApplyConfig()531 void KNote::slotApplyConfig()
532 {
533     m_label->setFont(mDisplayAttribute->titleFont());
534     m_editor->setTextFont(mDisplayAttribute->font());
535     m_editor->setTabStop(mDisplayAttribute->tabSize());
536     m_editor->setAutoIndentMode(mDisplayAttribute->autoIndent());
537 
538     setColor(mDisplayAttribute->foregroundColor(), mDisplayAttribute->backgroundColor());
539 
540     updateLayout();
541 #if KDEPIM_HAVE_X11
542     slotUpdateShowInTaskbar();
543 #endif
544     resize(mDisplayAttribute->size());
545 }
546 
slotKeepAbove()547 void KNote::slotKeepAbove()
548 {
549     if (m_keepBelow->isChecked()) {
550         m_keepBelow->setChecked(false);
551     }
552     updateKeepAboveBelow();
553 }
554 
slotKeepBelow()555 void KNote::slotKeepBelow()
556 {
557     if (m_keepAbove->isChecked()) {
558         m_keepAbove->setChecked(false);
559     }
560     updateKeepAboveBelow();
561 }
562 
updateKeepAboveBelow(bool save)563 void KNote::updateKeepAboveBelow(bool save)
564 {
565 #if KDEPIM_HAVE_X11
566     NET::States state = KWindowInfo(winId(), NET::WMState).state();
567 #else
568     NET::States state = {}; // neutral state, TODO
569 #endif
570     auto attribute = mItem.attribute<NoteShared::NoteDisplayAttribute>(Akonadi::Item::AddIfMissing);
571     if (m_keepAbove->isChecked()) {
572         attribute->setKeepAbove(true);
573         attribute->setKeepBelow(false);
574         KWindowSystem::setState(winId(), state | NET::KeepAbove);
575     } else if (m_keepBelow->isChecked()) {
576         attribute->setKeepAbove(false);
577         attribute->setKeepBelow(true);
578         KWindowSystem::setState(winId(), state | NET::KeepBelow);
579     } else {
580         attribute->setKeepAbove(false);
581         attribute->setKeepBelow(false);
582         KWindowSystem::clearState(winId(), NET::KeepAbove);
583         KWindowSystem::clearState(winId(), NET::KeepBelow);
584     }
585     if (!mBlockSave && save) {
586         saveNoteContent();
587         auto job = new Akonadi::ItemModifyJob(mItem);
588 #ifdef DEBUG_SAVE_NOTE
589         qCDebug(KNOTES_LOG) << "slotUpdateKeepAboveBelow slotNoteSaved(KJob*)";
590 #endif
591         connect(job, &Akonadi::ItemModifyJob::result, this, &KNote::slotNoteSaved);
592     }
593 }
594 
slotUpdateShowInTaskbar()595 void KNote::slotUpdateShowInTaskbar()
596 {
597 #if KDEPIM_HAVE_X11
598     if (!mDisplayAttribute->showInTaskbar()) {
599         KWindowSystem::setState(winId(), KWindowInfo(winId(), NET::WMState).state() | NET::SkipTaskbar);
600     } else {
601         KWindowSystem::clearState(winId(), NET::SkipTaskbar);
602     }
603 #endif
604 }
605 
slotUpdateDesktopActions()606 void KNote::slotUpdateDesktopActions()
607 {
608 #if KDEPIM_HAVE_X11
609     m_toDesktop->clear();
610 
611     QAction *act = m_toDesktop->addAction(i18n("&All Desktops"));
612     KWindowInfo info(winId(), NET::WMDesktop);
613 
614     if (info.onAllDesktops()) {
615         act->setChecked(true);
616         act->setData(NETWinInfo::OnAllDesktops);
617     }
618     auto separator = new QAction(m_toDesktop);
619     separator->setSeparator(true);
620     m_toDesktop->addAction(separator);
621     const int count = KWindowSystem::numberOfDesktops();
622     for (int n = 1; n <= count; ++n) {
623         QAction *desktopAct = m_toDesktop->addAction(QStringLiteral("&%1 %2").arg(n).arg(KWindowSystem::desktopName(n)));
624         desktopAct->setData(n);
625         if (info.isOnDesktop(n)) {
626             desktopAct->setChecked(true);
627         }
628     }
629 #endif
630 }
631 
632 // -------------------- private methods -------------------- //
633 
buildGui()634 void KNote::buildGui()
635 {
636     createNoteHeader();
637     createNoteEditor(QString());
638 
639     KXMLGUIBuilder builder(this);
640     KXMLGUIFactory factory(&builder, this);
641     factory.addClient(this);
642 
643     m_menu = qobject_cast<QMenu *>(factory.container(QStringLiteral("note_context"), this));
644     m_tool = qobject_cast<KToolBar *>(factory.container(QStringLiteral("note_tool"), this));
645 
646     createNoteFooter();
647 }
648 
createActions()649 void KNote::createActions()
650 {
651     // create the menu items for the note - not the editor...
652     // rename, mail, print, save as, insert date, alarm, close, delete, new note
653     auto action = new QAction(QIcon::fromTheme(QStringLiteral("document-new")), i18n("New"), this);
654     actionCollection()->addAction(QStringLiteral("new_note"), action);
655     connect(action, &QAction::triggered, this, &KNote::slotRequestNewNote);
656 
657     action = new QAction(QIcon::fromTheme(QStringLiteral("edit-rename")), i18n("Rename..."), this);
658     actionCollection()->addAction(QStringLiteral("rename_note"), action);
659     connect(action, &QAction::triggered, this, &KNote::slotRename);
660 
661     m_readOnly = new KToggleAction(QIcon::fromTheme(QStringLiteral("object-locked")), i18n("Lock"), this);
662     actionCollection()->addAction(QStringLiteral("lock_note"), m_readOnly);
663     connect(m_readOnly, &KToggleAction::triggered, this, &KNote::slotUpdateReadOnly);
664     m_readOnly->setCheckedState(KGuiItem(i18n("Unlock"), QStringLiteral("object-unlocked")));
665 
666     action = new QAction(QIcon::fromTheme(QStringLiteral("window-close")), i18n("Hide"), this);
667     actionCollection()->addAction(QStringLiteral("hide_note"), action);
668     connect(action, &QAction::triggered, this, &KNote::slotClose);
669     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::Key_Escape));
670 
671     action = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18n("Delete"), this);
672     actionCollection()->addAction(QStringLiteral("delete_note"), action);
673     connect(action, &QAction::triggered, this, &KNote::slotKill);
674 
675     action = new QAction(QIcon::fromTheme(QStringLiteral("knotes_alarm")), i18n("Set Alarm..."), this);
676     actionCollection()->addAction(QStringLiteral("set_alarm"), action);
677     connect(action, &QAction::triggered, this, &KNote::slotSetAlarm);
678 
679     action = new QAction(QIcon::fromTheme(QStringLiteral("network-wired")), i18n("Send..."), this);
680     actionCollection()->addAction(QStringLiteral("send_note"), action);
681     connect(action, &QAction::triggered, this, &KNote::slotSend);
682 
683     action = new QAction(QIcon::fromTheme(QStringLiteral("mail-send")), i18n("Mail..."), this);
684     actionCollection()->addAction(QStringLiteral("mail_note"), action);
685     connect(action, &QAction::triggered, this, &KNote::slotMail);
686 
687     action = new QAction(QIcon::fromTheme(QStringLiteral("document-save-as")), i18n("Save As..."), this);
688     actionCollection()->addAction(QStringLiteral("save_note"), action);
689     connect(action, &QAction::triggered, this, &KNote::slotSaveAs);
690     action = actionCollection()->addAction(KStandardAction::Print, QStringLiteral("print_note"));
691     connect(action, &QAction::triggered, this, &KNote::slotPrint);
692 
693     action = actionCollection()->addAction(KStandardAction::PrintPreview, QStringLiteral("print_preview_note"));
694     connect(action, &QAction::triggered, this, &KNote::slotPrintPreview);
695 
696     action = new QAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Preferences..."), this);
697     actionCollection()->addAction(QStringLiteral("configure_note"), action);
698     connect(action, &QAction::triggered, this, &KNote::slotPreferences);
699 
700     m_keepAbove = new KToggleAction(QIcon::fromTheme(QStringLiteral("go-up")), i18n("Keep Above Others"), this);
701     actionCollection()->addAction(QStringLiteral("keep_above"), m_keepAbove);
702     connect(m_keepAbove, &KToggleAction::triggered, this, &KNote::slotKeepAbove);
703 
704     m_keepBelow = new KToggleAction(QIcon::fromTheme(QStringLiteral("go-down")), i18n("Keep Below Others"), this);
705     actionCollection()->addAction(QStringLiteral("keep_below"), m_keepBelow);
706     connect(m_keepBelow, &KToggleAction::triggered, this, &KNote::slotKeepBelow);
707 
708 #if KDEPIM_HAVE_X11
709     m_toDesktop = new KSelectAction(i18n("To Desktop"), this);
710     actionCollection()->addAction(QStringLiteral("to_desktop"), m_toDesktop);
711     connect(m_toDesktop, qOverload<QAction *>(&KSelectAction::triggered), this, &KNote::slotPopupActionToDesktop);
712     connect(m_toDesktop->menu(), &QMenu::aboutToShow, this, &KNote::slotUpdateDesktopActions);
713     // initially populate it, otherwise stays disabled
714     slotUpdateDesktopActions();
715 #endif
716     // invisible action to walk through the notes to make this configurable
717     action = new QAction(i18n("Walk Through Notes"), this);
718     actionCollection()->addAction(QStringLiteral("walk_notes"), action);
719     connect(action, &QAction::triggered, this, &KNote::sigShowNextNote);
720     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::SHIFT | Qt::Key_Backtab));
721 
722     actionCollection()->addAssociatedWidget(this);
723     const auto lst = actionCollection()->actions();
724     for (QAction *act : lst) {
725         act->setShortcutContext(Qt::WidgetWithChildrenShortcut);
726     }
727     if (mAllowDebugAkonadiSearch) {
728         // Don't translate it it's just for debugging
729         action = new QAction(QStringLiteral("Debug Akonadi Search..."), this);
730         actionCollection()->addAction(QStringLiteral("debug_akonadi_search"), action);
731         connect(action, &QAction::triggered, this, &KNote::slotDebugAkonadiSearch);
732     }
733 }
734 
createNoteHeader()735 void KNote::createNoteHeader()
736 {
737     // load style configuration
738     KConfigGroup styleGroup(m_kwinConf, "Style");
739 
740     QBoxLayout::Direction headerLayoutDirection = QBoxLayout::LeftToRight;
741 
742     if (styleGroup.readEntry("CustomButtonPositions", false)) {
743         if (styleGroup.readEntry("ButtonsOnLeft").contains(QLatin1Char('X'))) {
744             headerLayoutDirection = QBoxLayout::RightToLeft;
745         }
746     }
747 
748     auto headerLayout = new QBoxLayout(headerLayoutDirection);
749 
750     // create header label
751     m_label = new QLabel(this);
752     headerLayout->addWidget(m_label);
753     m_label->setFrameStyle(NoFrame);
754     m_label->setBackgroundRole(QPalette::Base);
755     m_label->setLineWidth(0);
756     m_label->setAutoFillBackground(true);
757     m_label->installEventFilter(this); // receive events ( for dragging &
758     // action menu )
759     m_button = new KNoteButton(QStringLiteral("knotes_close"), this);
760     headerLayout->addWidget(m_button);
761 
762     connect(m_button, &KNoteButton::clicked, this, &KNote::slotClose);
763 
764     m_noteLayout->addLayout(headerLayout);
765 }
766 
createNoteEditor(const QString & configFile)767 void KNote::createNoteEditor(const QString &configFile)
768 {
769     Q_UNUSED(configFile)
770     m_editor = new KNoteEdit(actionCollection(), this);
771     m_noteLayout->addWidget(m_editor);
772     m_editor->setNote(this);
773     m_editor->installEventFilter(this); // receive focus events for modified
774     setFocusProxy(m_editor);
775 }
776 
slotRequestNewNote()777 void KNote::slotRequestNewNote()
778 {
779     // Be sure to save before to request a new note
780     saveNote();
781     Q_EMIT sigRequestNewNote();
782 }
783 
createNoteFooter()784 void KNote::createNoteFooter()
785 {
786     if (m_tool) {
787         m_tool->setIconSize(QSize(10, 10));
788         m_tool->setFixedHeight(24);
789         m_tool->setToolButtonStyle(Qt::ToolButtonIconOnly);
790     }
791 
792     // create size grip
793     auto gripLayout = new QHBoxLayout;
794     m_grip = new QSizeGrip(this);
795     m_grip->setFixedSize(m_grip->sizeHint());
796 
797     if (m_tool) {
798         gripLayout->addWidget(m_tool);
799         gripLayout->setAlignment(m_tool, Qt::AlignBottom | Qt::AlignLeft);
800         m_tool->hide();
801     }
802 
803     gripLayout->addWidget(m_grip);
804     gripLayout->setAlignment(m_grip, Qt::AlignBottom | Qt::AlignRight);
805     m_noteLayout->addLayout(gripLayout);
806 
807     // if there was just a way of making KComboBox adhere the toolbar height...
808     if (m_tool) {
809         const auto comboboxs = m_tool->findChildren<KComboBox *>();
810         for (KComboBox *combo : comboboxs) {
811             QFont font = combo->font();
812             font.setPointSize(7);
813             combo->setFont(font);
814             combo->setFixedHeight(14);
815         }
816     }
817 }
818 
loadNoteContent(const Akonadi::Item & item)819 void KNote::loadNoteContent(const Akonadi::Item &item)
820 {
821     auto noteMessage = item.payload<KMime::Message::Ptr>();
822     const KMime::Headers::Subject *const subject = noteMessage ? noteMessage->subject(false) : nullptr;
823     setName(subject ? subject->asUnicodeString() : QString());
824     if (noteMessage->contentType()->isHTMLText()) {
825         m_editor->setAcceptRichText(true);
826         m_editor->setAutoFormatting(QTextEdit::AutoAll);
827         m_editor->setHtml(noteMessage->mainBodyPart()->decodedText());
828     } else {
829         m_editor->setAcceptRichText(false);
830         m_editor->setAutoFormatting(QTextEdit::AutoNone);
831         m_editor->setPlainText(noteMessage->mainBodyPart()->decodedText());
832     }
833     if (auto hrd = noteMessage->headerByType("X-Cursor-Position")) {
834         m_editor->setCursorPositionFromStart(hrd->asUnicodeString().toInt());
835     }
836 }
837 
prepare()838 void KNote::prepare()
839 {
840     mBlockSave = true;
841     loadNoteContent(mItem);
842 
843     resize(mDisplayAttribute->size());
844     const QPoint &position = mDisplayAttribute->position();
845     QRect desk = qApp->desktop()->rect();
846     desk.adjust(10, 10, -10, -10);
847     if (desk.intersects(QRect(position, mDisplayAttribute->size()))) {
848         move(position); // do before calling show() to avoid flicker
849     }
850     if (mDisplayAttribute->isHidden()) {
851         hide();
852     } else {
853         show();
854     }
855     // read configuration settings...
856     slotApplyConfig();
857 
858     if (mItem.hasAttribute<NoteShared::NoteLockAttribute>()) {
859         m_editor->setReadOnly(true);
860         m_readOnly->setChecked(true);
861     } else {
862         m_readOnly->setChecked(false);
863     }
864     slotUpdateReadOnly();
865     // if this is a new note put on current desktop - we can't use defaults
866     // in KConfig XT since only _changes_ will be stored in the config file
867     int desktop = mDisplayAttribute->desktop();
868 
869 #if KDEPIM_HAVE_X11
870     if ((desktop < 0 && desktop != NETWinInfo::OnAllDesktops) || !mDisplayAttribute->rememberDesktop()) {
871         desktop = KWindowSystem::currentDesktop();
872     }
873 #endif
874 
875     // show the note if desired
876     if (desktop != 0 && !mDisplayAttribute->isHidden()) {
877         // to avoid flicker, call this before show()
878         toDesktop(desktop);
879         show();
880 
881         // because KWin forgets about that for hidden windows
882 #if KDEPIM_HAVE_X11
883         if (desktop == NETWinInfo::OnAllDesktops) {
884             toDesktop(desktop);
885         }
886 #endif
887     }
888 
889     if (mDisplayAttribute->keepAbove()) {
890         m_keepAbove->setChecked(true);
891     } else if (mDisplayAttribute->keepBelow()) {
892         m_keepBelow->setChecked(true);
893     } else {
894         m_keepAbove->setChecked(false);
895         m_keepBelow->setChecked(false);
896     }
897 
898     updateKeepAboveBelow();
899     // HACK: update the icon color - again after showing the note, to make kicker
900     // aware of the new colors
901     KIconEffect effect;
902     const QColor col = mDisplayAttribute->backgroundColor();
903     const QPixmap icon = effect.apply(qApp->windowIcon().pixmap(style()->pixelMetric(QStyle::PM_MessageBoxIconSize)), KIconEffect::Colorize, 1, col, false);
904     const QPixmap miniIcon = effect.apply(qApp->windowIcon().pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize)), KIconEffect::Colorize, 1, col, false);
905     KWindowSystem::setIcons(winId(), icon, miniIcon);
906 
907     // set up the look&feel of the note
908     setFrameStyle(Panel | Raised);
909     setMinimumSize(20, 20);
910     setBackgroundRole(QPalette::Base);
911 
912     m_editor->setContentsMargins(0, 0, 0, 0);
913     m_editor->setBackgroundRole(QPalette::Base);
914     m_editor->setFrameStyle(NoFrame);
915     m_editor->document()->setModified(false);
916     mBlockSave = false;
917 }
918 
toDesktop(int desktop)919 void KNote::toDesktop(int desktop)
920 {
921     if (desktop == 0) {
922         return;
923     }
924 
925 #if KDEPIM_HAVE_X11
926     if (desktop == NETWinInfo::OnAllDesktops) {
927         KWindowSystem::setOnAllDesktops(winId(), true);
928     } else {
929         KWindowSystem::setOnDesktop(winId(), desktop);
930     }
931 #endif
932 }
933 
setColor(const QColor & fg,const QColor & bg)934 void KNote::setColor(const QColor &fg, const QColor &bg)
935 {
936     m_editor->setColor(fg, bg);
937     QPalette p = palette();
938 
939     // better: from light(150) to light(100) to light(75)
940     // QLinearGradient g( width()/2, 0, width()/2, height() );
941     // g.setColorAt( 0, bg );
942     // g.setColorAt( 1, bg.darker(150) );
943 
944     p.setColor(QPalette::Window, bg);
945     // p.setBrush( QPalette::Window,     g );
946     p.setColor(QPalette::Base, bg);
947     // p.setBrush( QPalette::Base,       g );
948 
949     p.setColor(QPalette::WindowText, fg);
950     p.setColor(QPalette::Text, fg);
951 
952     p.setColor(QPalette::Button, bg.darker(116));
953     p.setColor(QPalette::ButtonText, fg);
954 
955     // p.setColor( QPalette::Highlight,  bg );
956     // p.setColor( QPalette::HighlightedText, fg );
957 
958     // order: Light, Midlight, Button, Mid, Dark, Shadow
959 
960     // the shadow
961     p.setColor(QPalette::Light, bg.lighter(180));
962     p.setColor(QPalette::Midlight, bg.lighter(150));
963     p.setColor(QPalette::Mid, bg.lighter(150));
964     p.setColor(QPalette::Dark, bg.darker(108));
965     p.setColor(QPalette::Shadow, bg.darker(116));
966 
967     setPalette(p);
968 
969     // darker values for the active label
970     p.setColor(QPalette::Active, QPalette::Base, bg.darker(116));
971 
972     m_label->setPalette(p);
973 
974     // set the text color
975     m_editor->setTextColor(fg);
976 
977     // update the icon color
978     KIconEffect effect;
979     QPixmap icon = effect.apply(qApp->windowIcon().pixmap(style()->pixelMetric(QStyle::PM_MessageBoxIconSize)), KIconEffect::Colorize, 1, bg, false);
980     QPixmap miniIcon = effect.apply(qApp->windowIcon().pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize)), KIconEffect::Colorize, 1, bg, false);
981     KWindowSystem::setIcons(winId(), icon, miniIcon);
982     // update the color of the title
983     updateFocus();
984     Q_EMIT sigColorChanged();
985 }
986 
updateLabelAlignment()987 void KNote::updateLabelAlignment()
988 {
989     // if the name is too long to fit, left-align it, otherwise center it (#59028)
990     const QString labelText = m_label->text();
991     if (m_label->fontMetrics().boundingRect(labelText).width() > m_label->width()) {
992         m_label->setAlignment(Qt::AlignLeft);
993     } else {
994         m_label->setAlignment(Qt::AlignHCenter);
995     }
996 }
997 
updateFocus()998 void KNote::updateFocus()
999 {
1000     if (hasFocus()) {
1001         if (!m_editor->isReadOnly()) {
1002             if (m_tool && m_tool->isHidden() && m_editor->acceptRichText()) {
1003                 m_tool->show();
1004                 updateLayout();
1005             }
1006             m_grip->show();
1007         } else {
1008             if (m_tool && !m_tool->isHidden()) {
1009                 m_tool->hide();
1010                 updateLayout(); // to update the minimum height
1011             }
1012             m_grip->hide();
1013         }
1014     } else {
1015         m_grip->hide();
1016 
1017         if (m_tool && !m_tool->isHidden()) {
1018             m_tool->hide();
1019             updateLayout(); // to update the minimum height
1020         }
1021     }
1022 }
1023 
updateLayout()1024 void KNote::updateLayout()
1025 {
1026     // TODO: remove later if no longer needed.
1027     updateLabelAlignment();
1028 }
1029 
1030 // -------------------- protected methods -------------------- //
1031 
contextMenuEvent(QContextMenuEvent * e)1032 void KNote::contextMenuEvent(QContextMenuEvent *e)
1033 {
1034     if (m_menu) {
1035         m_menu->popup(e->globalPos());
1036     }
1037 }
1038 
showEvent(QShowEvent *)1039 void KNote::showEvent(QShowEvent *)
1040 {
1041     if (mDisplayAttribute->isHidden()) {
1042         // KWin does not preserve these properties for hidden windows
1043         updateKeepAboveBelow(false);
1044 #if KDEPIM_HAVE_X11
1045         slotUpdateShowInTaskbar();
1046 #endif
1047         toDesktop(mDisplayAttribute->desktop());
1048         move(mDisplayAttribute->position());
1049         auto attr = mItem.attribute<NoteShared::NoteDisplayAttribute>(Akonadi::Item::AddIfMissing);
1050         saveNoteContent();
1051         attr->setIsHidden(false);
1052         if (!mBlockSave) {
1053             auto job = new Akonadi::ItemModifyJob(mItem);
1054 #ifdef DEBUG_SAVE_NOTE
1055             qCDebug(KNOTES_LOG) << "showEvent slotNoteSaved(KJob*)";
1056 #endif
1057             connect(job, &Akonadi::ItemModifyJob::result, this, &KNote::slotNoteSaved);
1058         }
1059     }
1060 }
1061 
resizeEvent(QResizeEvent * qre)1062 void KNote::resizeEvent(QResizeEvent *qre)
1063 {
1064     QFrame::resizeEvent(qre);
1065     updateLayout();
1066 }
1067 
closeEvent(QCloseEvent * event)1068 void KNote::closeEvent(QCloseEvent *event)
1069 {
1070     if (qApp->isSavingSession()) {
1071         return;
1072     }
1073     event->ignore(); // We don't want to close (and delete the widget). Just hide it
1074     slotClose();
1075 }
1076 
dragEnterEvent(QDragEnterEvent * e)1077 void KNote::dragEnterEvent(QDragEnterEvent *e)
1078 {
1079     if (!m_editor->isReadOnly()) {
1080         e->setAccepted(e->mimeData()->hasColor());
1081     }
1082 }
1083 
dropEvent(QDropEvent * e)1084 void KNote::dropEvent(QDropEvent *e)
1085 {
1086     if (m_editor->isReadOnly()) {
1087         return;
1088     }
1089 
1090     const QMimeData *md = e->mimeData();
1091     if (md->hasColor()) {
1092         const auto bg = qvariant_cast<QColor>(md->colorData());
1093 
1094         auto attr = mItem.attribute<NoteShared::NoteDisplayAttribute>(Akonadi::Item::AddIfMissing);
1095         saveNoteContent();
1096         attr->setBackgroundColor(bg);
1097         auto job = new Akonadi::ItemModifyJob(mItem);
1098 #ifdef DEBUG_SAVE_NOTE
1099         qCDebug(KNOTES_LOG) << "dropEvent slotNoteSaved(KJob*)";
1100 #endif
1101         connect(job, &Akonadi::ItemModifyJob::result, this, &KNote::slotNoteSaved);
1102     }
1103 }
1104 
event(QEvent * ev)1105 bool KNote::event(QEvent *ev)
1106 {
1107     if (ev->type() == QEvent::LayoutRequest) {
1108         updateLayout();
1109         return true;
1110     } else {
1111         return QFrame::event(ev);
1112     }
1113 }
1114 
eventFilter(QObject * o,QEvent * ev)1115 bool KNote::eventFilter(QObject *o, QEvent *ev)
1116 {
1117     if (ev->type() == QEvent::DragEnter && static_cast<QDragEnterEvent *>(ev)->mimeData()->hasColor()) {
1118         dragEnterEvent(static_cast<QDragEnterEvent *>(ev));
1119         return true;
1120     }
1121 
1122     if (ev->type() == QEvent::Drop && static_cast<QDropEvent *>(ev)->mimeData()->hasColor()) {
1123         dropEvent(static_cast<QDropEvent *>(ev));
1124         return true;
1125     }
1126 
1127     if (o == m_label) {
1128         auto e = (QMouseEvent *)ev;
1129 
1130         if (ev->type() == QEvent::MouseButtonDblClick) {
1131             if (!m_editor->isReadOnly()) {
1132                 slotRename();
1133             }
1134         }
1135 
1136         if (ev->type() == QEvent::MouseButtonPress
1137             && ((e->buttons() & Qt::LeftButton) == Qt::LeftButton || (e->buttons() & Qt::MiddleButton) == Qt::MiddleButton)) {
1138             mOrigPos = e->pos();
1139             return false;
1140         }
1141 
1142         if (ev->type() == QEvent::MouseMove && ((e->buttons() & Qt::LeftButton) == Qt::LeftButton || (e->buttons() & Qt::MiddleButton) == Qt::MiddleButton)) {
1143             QPoint newPos = e->globalPos() - mOrigPos - QPoint(1, 1);
1144             move(newPos);
1145             return true;
1146         }
1147 
1148         if (ev->type() == QEvent::MouseButtonRelease
1149             && ((e->buttons() & Qt::LeftButton) == Qt::LeftButton || (e->buttons() & Qt::MiddleButton) == Qt::MiddleButton)) {
1150             QPoint newPos = e->globalPos() - mOrigPos - QPoint(1, 1);
1151             move(newPos);
1152             return false;
1153         }
1154         return false;
1155     }
1156 
1157     if (o == m_editor) {
1158         if (ev->type() == QEvent::FocusOut) {
1159             auto fe = static_cast<QFocusEvent *>(ev);
1160             if (fe->reason() != Qt::PopupFocusReason && fe->reason() != Qt::MouseFocusReason) {
1161                 updateFocus();
1162                 if (!mBlockSave) {
1163                     saveNote(true);
1164                 }
1165             }
1166         } else if (ev->type() == QEvent::FocusIn) {
1167             updateFocus();
1168         }
1169 
1170         return false;
1171     }
1172 
1173     return false;
1174 }
1175 
item() const1176 Akonadi::Item KNote::item() const
1177 {
1178     return mItem;
1179 }
1180 
slotDebugAkonadiSearch()1181 void KNote::slotDebugAkonadiSearch()
1182 {
1183     QPointer<Akonadi::Search::AkonadiSearchDebugDialog> dlg = new Akonadi::Search::AkonadiSearchDebugDialog;
1184     dlg->setAkonadiId(mItem.id());
1185     dlg->setAttribute(Qt::WA_DeleteOnClose);
1186     dlg->setSearchType(Akonadi::Search::AkonadiSearchDebugSearchPathComboBox::Notes);
1187     dlg->doSearch();
1188     dlg->show();
1189 }
1190