diff --git a/src/notes/knote.cpp b/src/notes/knote.cpp index 7e891c8..15f565b 100644 --- a/src/notes/knote.cpp +++ b/src/notes/knote.cpp @@ -1,1229 +1,1241 @@ /******************************************************************* KNotes -- Notes for the KDE project Copyright (c) 1997-2013, The KNotes Developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *******************************************************************/ #include #include "knote.h" #include "knotes_debug.h" #include "knotedisplaysettings.h" #include "knoteedit.h" #include "knotesglobalconfig.h" #include "noteutils.h" #include "alarms/notealarmdialog.h" #include "attributes/notealarmattribute.h" #include "attributes/notedisplayattribute.h" #include "attributes/notelockattribute.h" #include "configdialog/knotesimpleconfigdialog.h" #include "notes/knotebutton.h" #include "print/knoteprinter.h" #include "print/knoteprintobject.h" #include "print/knoteprintselectthemedialog.h" #include "utils/knoteutils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if KDEPIM_HAVE_X11 #include #include #endif //#define DEBUG_SAVE_NOTE 1 KNote::KNote(const QDomDocument &buildDoc, const Akonadi::Item &item, bool allowAkonadiSearchDebug, QWidget *parent) : QFrame(parent, Qt::FramelessWindowHint) , mItem(item) , m_label(nullptr) , m_grip(nullptr) , m_button(nullptr) , m_tool(nullptr) , m_editor(nullptr) , m_kwinConf(KSharedConfig::openConfig(QStringLiteral("kwinrc"))) , mDisplayAttribute(new KNoteDisplaySettings) , mAllowDebugAkonadiSearch(allowAkonadiSearchDebug) { if (mItem.hasAttribute()) { mDisplayAttribute->setDisplayAttribute(mItem.attribute()); } else { setDisplayDefaultValue(); //save default display value } setAcceptDrops(true); setAttribute(Qt::WA_DeleteOnClose); setDOMDocument(buildDoc); setXMLFile(componentName() + QLatin1String("ui.rc"), false, false); // create the main layout m_noteLayout = new QVBoxLayout(this); m_noteLayout->setMargin(0); createActions(); buildGui(); prepare(); } KNote::~KNote() { delete mDisplayAttribute; } void KNote::setDisplayDefaultValue() { KNoteUtils::setDefaultValue(mItem); Akonadi::ItemModifyJob *job = new Akonadi::ItemModifyJob(mItem); #ifdef DEBUG_SAVE_NOTE qCDebug(KNOTES_LOG) << "setDisplayDefaultValue slotNoteSaved(KJob*)"; #endif connect(job, &Akonadi::ItemModifyJob::result, this, &KNote::slotNoteSaved); } void KNote::setChangeItem(const Akonadi::Item &item, const QSet &set) { mItem = item; if (item.hasAttribute()) { mDisplayAttribute->setDisplayAttribute(item.attribute()); } if (set.contains("KJotsLockAttribute")) { m_editor->setReadOnly(item.hasAttribute()); } if (set.contains("PLD:RFC822")) { loadNoteContent(item); } if (set.contains("NoteDisplayAttribute")) { qCDebug(KNOTES_LOG) << " ATR:NoteDisplayAttribute"; slotApplyConfig(); } //TODO update display/content etc. updateLabelAlignment(); } void KNote::slotKill(bool force) { if (!force && KMessageBox::warningContinueCancel(this, i18n("Do you really want to delete note %1?", m_label->text()), i18n("Confirm Delete"), KGuiItem(i18n("&Delete"), QStringLiteral("edit-delete")), KStandardGuiItem::cancel(), QStringLiteral("ConfirmDeleteNote")) != KMessageBox::Continue) { return; } Q_EMIT sigKillNote(mItem.id()); } // -------------------- public member functions -------------------- // void KNote::saveNote(bool force, bool sync) { if (!force && !m_editor->document()->isModified()) { return; } bool needToSave = false; NoteShared::NoteDisplayAttribute *attribute = mItem.attribute(Akonadi::Item::AddIfMissing); const QPoint notePosition = pos(); if (attribute->position() != notePosition) { needToSave = true; attribute->setPosition(notePosition); } const QSize currentSize(QSize(width(), height())); if (attribute->size() != currentSize) { needToSave = true; attribute->setSize(currentSize); } #if KDEPIM_HAVE_X11 KWindowInfo info(winId(), NET::WMDesktop); const int count = KWindowSystem::numberOfDesktops(); for (int n = 1; n <= count; ++n) { if (info.isOnDesktop(n)) { if (attribute->desktop() != n) { needToSave = true; attribute->setDesktop(n); break; } } } #endif if (m_editor->document()->isModified()) { needToSave = true; saveNoteContent(); } if (needToSave) { #ifdef DEBUG_SAVE_NOTE qCDebug(KNOTES_LOG) << "save Note slotClose() slotNoteSaved(KJob*) : sync" << sync; #endif Akonadi::ItemModifyJob *job = new Akonadi::ItemModifyJob(mItem); if (sync) { job->exec(); } else { #ifdef DEBUG_SAVE_NOTE qCDebug(KNOTES_LOG) << "save Note slotClose() slotNoteSaved(KJob*)"; #endif connect(job, &Akonadi::ItemModifyJob::result, this, &KNote::slotNoteSaved); } } } void KNote::slotNoteSaved(KJob *job) { qCDebug(KNOTES_LOG) << " void KNote::slotNoteSaved(KJob *job)"; if (job->error()) { qCDebug(KNOTES_LOG) << " problem during save note:" << job->errorString(); } else { m_editor->document()->setModified(false); } } Akonadi::Item::Id KNote::noteId() const { return mItem.id(); } QString KNote::name() const { return m_label->text(); } QString KNote::text() const { return m_editor->text(); } void KNote::setName(const QString &name) { m_label->setText(name); updateLabelAlignment(); if (m_editor) { // not called from CTOR? saveNote(); } setWindowTitle(name); Q_EMIT sigNameChanged(name); } void KNote::setText(const QString &text) { m_editor->setText(text); saveNote(); } bool KNote::isDesktopAssigned() const { return mDisplayAttribute->rememberDesktop(); } bool KNote::isModified() const { return m_editor->document()->isModified(); } // ------------------ private slots (menu actions) ------------------ // void KNote::slotRename() { // pop up dialog to get the new name bool ok; const QString oldName = m_label->text(); const QString newName = QInputDialog::getText(this, QString(), i18n("Please enter the new name:"), QLineEdit::Normal, m_label->text(), &ok); if (!ok || (oldName == newName)) { // handle cancel return; } setName(newName); } void KNote::slotUpdateReadOnly() { const bool readOnly = m_readOnly->isChecked(); m_editor->setReadOnly(readOnly); if (mItem.hasAttribute()) { if (!readOnly) { mItem.removeAttribute(); } } else { if (readOnly) { mItem.attribute(Akonadi::Item::AddIfMissing); } } if (!mBlockSave) { updateAllAttributes(); Akonadi::ItemModifyJob *job = new Akonadi::ItemModifyJob(mItem); #ifdef DEBUG_SAVE_NOTE qCDebug(KNOTES_LOG) << " void KNote::slotUpdateReadOnly() slotNoteSaved(KJob*)"; #endif connect(job, &Akonadi::ItemModifyJob::result, this, &KNote::slotNoteSaved); } // enable/disable actions accordingly actionCollection()->action(QStringLiteral("configure_note"))->setEnabled(!readOnly); actionCollection()->action(QStringLiteral("delete_note"))->setEnabled(!readOnly); actionCollection()->action(QStringLiteral("format_bold"))->setEnabled(!readOnly); actionCollection()->action(QStringLiteral("format_italic"))->setEnabled(!readOnly); actionCollection()->action(QStringLiteral("format_underline"))->setEnabled(!readOnly); actionCollection()->action(QStringLiteral("format_strikeout"))->setEnabled(!readOnly); actionCollection()->action(QStringLiteral("format_alignleft"))->setEnabled(!readOnly); actionCollection()->action(QStringLiteral("format_aligncenter"))->setEnabled(!readOnly); actionCollection()->action(QStringLiteral("format_alignright"))->setEnabled(!readOnly); actionCollection()->action(QStringLiteral("format_alignblock"))->setEnabled(!readOnly); actionCollection()->action(QStringLiteral("format_list"))->setEnabled(!readOnly); actionCollection()->action(QStringLiteral("format_super"))->setEnabled(!readOnly); actionCollection()->action(QStringLiteral("format_sub"))->setEnabled(!readOnly); actionCollection()->action(QStringLiteral("format_increaseindent"))->setEnabled(!readOnly); actionCollection()->action(QStringLiteral("format_decreaseindent"))->setEnabled(!readOnly); actionCollection()->action(QStringLiteral("text_background_color"))->setEnabled(!readOnly); actionCollection()->action(QStringLiteral("format_size"))->setEnabled(!readOnly); actionCollection()->action(QStringLiteral("format_color"))->setEnabled(!readOnly); actionCollection()->action(QStringLiteral("rename_note"))->setEnabled(!readOnly); actionCollection()->action(QStringLiteral("set_alarm"))->setEnabled(!readOnly); m_keepAbove->setEnabled(!readOnly); m_keepBelow->setEnabled(!readOnly); #if KDEPIM_HAVE_X11 m_toDesktop->setEnabled(!readOnly); #endif updateFocus(); } void KNote::updateAllAttributes() { #if KDEPIM_HAVE_X11 NoteShared::NoteDisplayAttribute *attribute = mItem.attribute(Akonadi::Item::AddIfMissing); KWindowInfo info(winId(), NET::WMDesktop); const int count = KWindowSystem::numberOfDesktops(); for (int n = 1; n <= count; ++n) { if (info.isOnDesktop(n)) { attribute->setDesktop(n); } } #endif saveNoteContent(); attribute->setIsHidden(true); attribute->setPosition(pos()); const QSize currentSize(QSize(width(), height())); if (attribute->size() != currentSize) { attribute->setSize(currentSize); } } void KNote::slotClose() { updateAllAttributes(); m_editor->clearFocus(); Akonadi::ItemModifyJob *job = new Akonadi::ItemModifyJob(mItem); #ifdef DEBUG_SAVE_NOTE qCDebug(KNOTES_LOG) << "slotClose() slotNoteSaved(KJob*)"; #endif connect(job, &Akonadi::ItemModifyJob::result, this, &KNote::slotNoteSaved); hide(); } void KNote::slotSetAlarm() { QPointer dlg = new NoteShared::NoteAlarmDialog(name(), this); if (mItem.hasAttribute()) { dlg->setAlarm(mItem.attribute()->dateTime()); } if (dlg->exec()) { bool needToModify = true; QDateTime dateTime = dlg->alarm(); if (dateTime.isValid()) { NoteShared::NoteAlarmAttribute *attribute = mItem.attribute(Akonadi::Item::AddIfMissing); attribute->setDateTime(dateTime); } else { if (mItem.hasAttribute()) { mItem.removeAttribute(); } else { needToModify = false; } } if (needToModify) { //Verify it! saveNoteContent(); Akonadi::ItemModifyJob *job = new Akonadi::ItemModifyJob(mItem); #ifdef DEBUG_SAVE_NOTE qCDebug(KNOTES_LOG) << "setAlarm() slotNoteSaved(KJob*)"; #endif connect(job, &Akonadi::ItemModifyJob::result, this, &KNote::slotNoteSaved); } } delete dlg; } void KNote::saveNoteContent() { KMime::Message::Ptr message = mItem.payload(); const QByteArray encoding("utf-8"); message->subject(true)->fromUnicodeString(name(), encoding); message->contentType(true)->setMimeType(m_editor->acceptRichText() ? "text/html" : "text/plain"); message->contentType()->setCharset(encoding); message->contentTransferEncoding(true)->setEncoding(KMime::Headers::CEquPr); message->date(true)->setDateTime(QDateTime::currentDateTime()); message->mainBodyPart()->fromUnicodeString(text().isEmpty() ? QStringLiteral(" ") : text()); KMime::Headers::Generic *header = new KMime::Headers::Generic("X-Cursor-Position"); header->fromUnicodeString(QString::number(m_editor->cursorPositionFromStart()), "utf-8"); message->setHeader(header); message->assemble(); mItem.setPayload(message); } void KNote::slotPreferences() { // create a new preferences dialog... QPointer dialog = new KNoteSimpleConfigDialog(name(), this); NoteShared::NoteDisplayAttribute *attribute = mItem.attribute(Akonadi::Item::AddIfMissing); attribute->setSize(QSize(width(), height())); dialog->load(mItem, m_editor->acceptRichText()); connect(this, &KNote::sigNameChanged, dialog.data(), &KNoteSimpleConfigDialog::slotUpdateCaption); if (dialog->exec()) { bool isRichText; dialog->save(mItem, isRichText); m_editor->setAcceptRichText(isRichText); saveNoteContent(); Akonadi::ItemModifyJob *job = new Akonadi::ItemModifyJob(mItem); #ifdef DEBUG_SAVE_NOTE qCDebug(KNOTES_LOG) << "slotPreference slotNoteSaved(KJob*)"; #endif connect(job, &Akonadi::ItemModifyJob::result, this, &KNote::slotNoteSaved); } delete dialog; } void KNote::slotSend() { NoteShared::NoteUtils noteUtils; noteUtils.sendToNetwork(this, name(), text()); } void KNote::slotMail() { NoteShared::NoteUtils noteUtils; noteUtils.sendToMail(this, m_label->text(), m_editor->toPlainText()); } void KNote::slotPrint() { print(false); } void KNote::slotPrintPreview() { print(true); } void KNote::print(bool preview) { QString content; if (!Qt::mightBeRichText(m_editor->text())) { content = Qt::convertFromPlainText(m_editor->text()); } else { content = m_editor->text(); } if (isModified()) { saveNote(); } KNotesGlobalConfig *globalConfig = KNotesGlobalConfig::self(); QString printingTheme = globalConfig->theme(); if (printingTheme.isEmpty()) { QPointer dlg = new KNotePrintSelectThemeDialog(this); if (dlg->exec()) { printingTheme = dlg->selectedTheme(); } delete dlg; } if (!printingTheme.isEmpty()) { KNotePrinter printer(this); QList lst; lst.append(new KNotePrintObject(mItem)); printer.setDefaultFont(mDisplayAttribute->font()); printer.printNotes(lst, printingTheme, preview); qDeleteAll(lst); } } void KNote::slotSaveAs() { // TODO: where to put pdf file support? In the printer??!??! QCheckBox *convert = nullptr; if (m_editor->acceptRichText()) { convert = new QCheckBox(nullptr); convert->setText(i18n("Save note as plain text")); } QUrl url; QPointer dlg = new KFileDialog(url, QString(), this, convert); dlg->setOperationMode(KFileDialog::Saving); dlg->setWindowTitle(i18n("Save As")); if (!dlg->exec()) { delete dlg; return; } const QString fileName = dlg->selectedFile(); const bool htmlFormatAndSaveAsHtml = (convert && !convert->isChecked()); delete dlg; if (fileName.isEmpty()) { return; } QFile file(fileName); if (file.exists() && KMessageBox::warningContinueCancel(this, i18n("A file named %1 already exists.
" "Are you sure you want to overwrite it?
", QFileInfo(file).fileName())) != KMessageBox::Continue) { return; } if (file.open(QIODevice::WriteOnly)) { QTextStream stream(&file); if (htmlFormatAndSaveAsHtml) { QString htmlStr = m_editor->toHtml(); htmlStr.replace(QStringLiteral("meta name=\"qrichtext\" content=\"1\""), QStringLiteral("meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"")); stream << htmlStr; } else { stream << m_editor->toPlainText(); } } } void KNote::slotPopupActionToDesktop(QAction *act) { const int id = act->data().toInt(); toDesktop(id); // compensate for the menu separator, -1 == all desktops } // ------------------ private slots (configuration) ------------------ // void KNote::slotApplyConfig() { m_label->setFont(mDisplayAttribute->titleFont()); m_editor->setTextFont(mDisplayAttribute->font()); m_editor->setTabStop(mDisplayAttribute->tabSize()); m_editor->setAutoIndentMode(mDisplayAttribute->autoIndent()); setColor(mDisplayAttribute->foregroundColor(), mDisplayAttribute->backgroundColor()); updateLayout(); slotUpdateShowInTaskbar(); resize(mDisplayAttribute->size()); } void KNote::slotKeepAbove() { if (m_keepBelow->isChecked()) { m_keepBelow->setChecked(false); } updateKeepAboveBelow(); } void KNote::slotKeepBelow() { if (m_keepAbove->isChecked()) { m_keepAbove->setChecked(false); } updateKeepAboveBelow(); } void KNote::updateKeepAboveBelow(bool save) { #if KDEPIM_HAVE_X11 NET::States state = KWindowInfo(winId(), NET::WMState).state(); #else NET::States state = 0; // neutral state, TODO #endif NoteShared::NoteDisplayAttribute *attribute = mItem.attribute(Akonadi::Item::AddIfMissing); if (m_keepAbove->isChecked()) { attribute->setKeepAbove(true); attribute->setKeepBelow(false); KWindowSystem::setState(winId(), state | NET::KeepAbove); } else if (m_keepBelow->isChecked()) { attribute->setKeepAbove(false); attribute->setKeepBelow(true); KWindowSystem::setState(winId(), state | NET::KeepBelow); } else { attribute->setKeepAbove(false); attribute->setKeepBelow(false); KWindowSystem::clearState(winId(), NET::KeepAbove); KWindowSystem::clearState(winId(), NET::KeepBelow); } if (!mBlockSave && save) { saveNoteContent(); Akonadi::ItemModifyJob *job = new Akonadi::ItemModifyJob(mItem); #ifdef DEBUG_SAVE_NOTE qCDebug(KNOTES_LOG) << "slotUpdateKeepAboveBelow slotNoteSaved(KJob*)"; #endif connect(job, &Akonadi::ItemModifyJob::result, this, &KNote::slotNoteSaved); } } void KNote::slotUpdateShowInTaskbar() { #if KDEPIM_HAVE_X11 if (!mDisplayAttribute->showInTaskbar()) { KWindowSystem::setState(winId(), KWindowInfo(winId(), NET::WMState).state() | NET::SkipTaskbar); } else { KWindowSystem::clearState(winId(), NET::SkipTaskbar); } #endif } void KNote::slotUpdateDesktopActions() { m_toDesktop->clear(); QAction *act = m_toDesktop->addAction(i18n("&All Desktops")); KWindowInfo info(winId(), NET::WMDesktop); if (info.onAllDesktops()) { act->setChecked(true); act->setData(NETWinInfo::OnAllDesktops); } QAction *separator = new QAction(m_toDesktop); separator->setSeparator(true); m_toDesktop->addAction(separator); const int count = KWindowSystem::numberOfDesktops(); for (int n = 1; n <= count; ++n) { QAction *desktopAct = m_toDesktop->addAction(QStringLiteral("&%1 %2").arg(n).arg(KWindowSystem::desktopName(n))); desktopAct->setData(n); if (info.isOnDesktop(n)) { desktopAct->setChecked(true); } } } // -------------------- private methods -------------------- // void KNote::buildGui() { createNoteHeader(); createNoteEditor(QString()); KXMLGUIBuilder builder(this); KXMLGUIFactory factory(&builder, this); factory.addClient(this); m_menu = qobject_cast(factory.container(QStringLiteral("note_context"), this)); m_tool = qobject_cast(factory.container(QStringLiteral("note_tool"), this)); createNoteFooter(); } void KNote::createActions() { // create the menu items for the note - not the editor... // rename, mail, print, save as, insert date, alarm, close, delete, new note QAction *action = new QAction(QIcon::fromTheme(QStringLiteral("document-new")), i18n("New"), this); actionCollection()->addAction(QStringLiteral("new_note"), action); connect(action, &QAction::triggered, this, &KNote::slotRequestNewNote); action = new QAction(QIcon::fromTheme(QStringLiteral("edit-rename")), i18n("Rename..."), this); actionCollection()->addAction(QStringLiteral("rename_note"), action); connect(action, &QAction::triggered, this, &KNote::slotRename); m_readOnly = new KToggleAction(QIcon::fromTheme(QStringLiteral("object-locked")), i18n("Lock"), this); actionCollection()->addAction(QStringLiteral("lock_note"), m_readOnly); connect(m_readOnly, &KToggleAction::triggered, this, &KNote::slotUpdateReadOnly); m_readOnly->setCheckedState(KGuiItem(i18n("Unlock"), QStringLiteral("object-unlocked"))); action = new QAction(QIcon::fromTheme(QStringLiteral("window-close")), i18n("Hide"), this); actionCollection()->addAction(QStringLiteral("hide_note"), action); connect(action, &QAction::triggered, this, &KNote::slotClose); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::Key_Escape)); action = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18n("Delete"), this); actionCollection()->addAction(QStringLiteral("delete_note"), action); connect(action, &QAction::triggered, this, &KNote::slotKill); action = new QAction(QIcon::fromTheme(QStringLiteral("knotes_alarm")), i18n("Set Alarm..."), this); actionCollection()->addAction(QStringLiteral("set_alarm"), action); connect(action, &QAction::triggered, this, &KNote::slotSetAlarm); action = new QAction(QIcon::fromTheme(QStringLiteral("network-wired")), i18n("Send..."), this); actionCollection()->addAction(QStringLiteral("send_note"), action); connect(action, &QAction::triggered, this, &KNote::slotSend); action = new QAction(QIcon::fromTheme(QStringLiteral("mail-send")), i18n("Mail..."), this); actionCollection()->addAction(QStringLiteral("mail_note"), action); connect(action, &QAction::triggered, this, &KNote::slotMail); action = new QAction(QIcon::fromTheme(QStringLiteral("document-save-as")), i18n("Save As..."), this); actionCollection()->addAction(QStringLiteral("save_note"), action); connect(action, &QAction::triggered, this, &KNote::slotSaveAs); action = actionCollection()->addAction(KStandardAction::Print, QStringLiteral("print_note")); connect(action, &QAction::triggered, this, &KNote::slotPrint); action = actionCollection()->addAction(KStandardAction::PrintPreview, QStringLiteral("print_preview_note")); connect(action, &QAction::triggered, this, &KNote::slotPrintPreview); action = new QAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Preferences..."), this); actionCollection()->addAction(QStringLiteral("configure_note"), action); connect(action, &QAction::triggered, this, &KNote::slotPreferences); m_keepAbove = new KToggleAction(QIcon::fromTheme(QStringLiteral("go-up")), i18n("Keep Above Others"), this); actionCollection()->addAction(QStringLiteral("keep_above"), m_keepAbove); connect(m_keepAbove, &KToggleAction::triggered, this, &KNote::slotKeepAbove); m_keepBelow = new KToggleAction(QIcon::fromTheme(QStringLiteral("go-down")), i18n("Keep Below Others"), this); actionCollection()->addAction(QStringLiteral("keep_below"), m_keepBelow); connect(m_keepBelow, &KToggleAction::triggered, this, &KNote::slotKeepBelow); #if KDEPIM_HAVE_X11 m_toDesktop = new KSelectAction(i18n("To Desktop"), this); actionCollection()->addAction(QStringLiteral("to_desktop"), m_toDesktop); connect(m_toDesktop, QOverload::of(&KSelectAction::triggered), this, &KNote::slotPopupActionToDesktop); connect(m_toDesktop->menu(), &QMenu::aboutToShow, this, &KNote::slotUpdateDesktopActions); // initially populate it, otherwise stays disabled slotUpdateDesktopActions(); #endif // invisible action to walk through the notes to make this configurable action = new QAction(i18n("Walk Through Notes"), this); actionCollection()->addAction(QStringLiteral("walk_notes"), action); connect(action, &QAction::triggered, this, &KNote::sigShowNextNote); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::SHIFT + Qt::Key_Backtab)); actionCollection()->addAssociatedWidget(this); foreach (QAction *action, actionCollection()->actions()) { action->setShortcutContext(Qt::WidgetWithChildrenShortcut); } if (mAllowDebugAkonadiSearch) { //Don't translate it it's just for debugging action = new QAction(QStringLiteral("Debug Akonadi Search..."), this); actionCollection()->addAction(QStringLiteral("debug_akonadi_search"), action); connect(action, &QAction::triggered, this, &KNote::slotDebugAkonadiSearch); } } void KNote::createNoteHeader() { // load style configuration KConfigGroup styleGroup(m_kwinConf, "Style"); QBoxLayout::Direction headerLayoutDirection = QBoxLayout::LeftToRight; if (styleGroup.readEntry("CustomButtonPositions", false)) { if (styleGroup.readEntry("ButtonsOnLeft").contains(QLatin1Char('X'))) { headerLayoutDirection = QBoxLayout::RightToLeft; } } QBoxLayout *headerLayout = new QBoxLayout(headerLayoutDirection); // create header label m_label = new QLabel(this); headerLayout->addWidget(m_label); m_label->setFrameStyle(NoFrame); m_label->setBackgroundRole(QPalette::Base); m_label->setLineWidth(0); m_label->setAutoFillBackground(true); m_label->installEventFilter(this); // receive events ( for dragging & // action menu ) m_button = new KNoteButton(QStringLiteral("knotes_close"), this); headerLayout->addWidget(m_button); connect(m_button, &KNoteButton::clicked, this, &KNote::slotClose); m_noteLayout->addItem(headerLayout); } void KNote::createNoteEditor(const QString &configFile) { Q_UNUSED(configFile); m_editor = new KNoteEdit(actionCollection(), this); m_noteLayout->addWidget(m_editor); m_editor->setNote(this); m_editor->installEventFilter(this); // receive focus events for modified setFocusProxy(m_editor); } void KNote::slotRequestNewNote() { //Be sure to save before to request a new note saveNote(); Q_EMIT sigRequestNewNote(); } void KNote::createNoteFooter() { if (m_tool) { m_tool->setIconSize(QSize(10, 10)); m_tool->setFixedHeight(24); m_tool->setToolButtonStyle(Qt::ToolButtonIconOnly); } // create size grip QHBoxLayout *gripLayout = new QHBoxLayout; m_grip = new QSizeGrip(this); m_grip->setFixedSize(m_grip->sizeHint()); if (m_tool) { gripLayout->addWidget(m_tool); gripLayout->setAlignment(m_tool, Qt::AlignBottom | Qt::AlignLeft); m_tool->hide(); } gripLayout->addWidget(m_grip); gripLayout->setAlignment(m_grip, Qt::AlignBottom | Qt::AlignRight); m_noteLayout->addItem(gripLayout); // if there was just a way of making KComboBox adhere the toolbar height... if (m_tool) { foreach (KComboBox *combo, m_tool->findChildren()) { QFont font = combo->font(); font.setPointSize(7); combo->setFont(font); combo->setFixedHeight(14); } } } void KNote::loadNoteContent(const Akonadi::Item &item) { KMime::Message::Ptr noteMessage = item.payload(); const KMime::Headers::Subject *const subject = noteMessage ? noteMessage->subject(false) : nullptr; setName(subject ? subject->asUnicodeString() : QString()); if (noteMessage->contentType()->isHTMLText()) { m_editor->setAcceptRichText(true); m_editor->setHtml(noteMessage->mainBodyPart()->decodedText()); } else { m_editor->setAcceptRichText(false); m_editor->setPlainText(noteMessage->mainBodyPart()->decodedText()); } if (auto hrd = noteMessage->headerByType("X-Cursor-Position")) { m_editor->setCursorPositionFromStart(hrd->asUnicodeString().toInt()); } } void KNote::prepare() { mBlockSave = true; loadNoteContent(mItem); resize(mDisplayAttribute->size()); const QPoint &position = mDisplayAttribute->position(); QRect desk = qApp->desktop()->rect(); desk.adjust(10, 10, -10, -10); if (desk.intersects(QRect(position, mDisplayAttribute->size()))) { move(position); // do before calling show() to avoid flicker } if (mDisplayAttribute->isHidden()) { hide(); } else { show(); } // read configuration settings... slotApplyConfig(); if (mItem.hasAttribute()) { m_editor->setReadOnly(true); m_readOnly->setChecked(true); } else { m_readOnly->setChecked(false); } slotUpdateReadOnly(); // if this is a new note put on current desktop - we can't use defaults // in KConfig XT since only _changes_ will be stored in the config file int desktop = mDisplayAttribute->desktop(); #if KDEPIM_HAVE_X11 if ((desktop < 0 && desktop != NETWinInfo::OnAllDesktops) || !mDisplayAttribute->rememberDesktop()) { desktop = KWindowSystem::currentDesktop(); } #endif // show the note if desired if (desktop != 0 && !mDisplayAttribute->isHidden()) { // to avoid flicker, call this before show() toDesktop(desktop); show(); // because KWin forgets about that for hidden windows #if KDEPIM_HAVE_X11 if (desktop == NETWinInfo::OnAllDesktops) { toDesktop(desktop); } #endif } if (mDisplayAttribute->keepAbove()) { m_keepAbove->setChecked(true); } else if (mDisplayAttribute->keepBelow()) { m_keepBelow->setChecked(true); } else { m_keepAbove->setChecked(false); m_keepBelow->setChecked(false); } updateKeepAboveBelow(); // HACK: update the icon color - again after showing the note, to make kicker // aware of the new colors KIconEffect effect; const QColor col = mDisplayAttribute->backgroundColor(); const QPixmap icon = effect.apply(qApp->windowIcon().pixmap( IconSize(KIconLoader::Desktop), IconSize(KIconLoader::Desktop)), KIconEffect::Colorize, 1, col, false); const QPixmap miniIcon = effect.apply(qApp->windowIcon().pixmap( IconSize(KIconLoader::Small), IconSize(KIconLoader::Small)), KIconEffect::Colorize, 1, col, false); KWindowSystem::setIcons(winId(), icon, miniIcon); // set up the look&feel of the note setFrameStyle(Panel | Raised); setMinimumSize(20, 20); setBackgroundRole(QPalette::Base); m_editor->setContentsMargins(0, 0, 0, 0); m_editor->setBackgroundRole(QPalette::Base); m_editor->setFrameStyle(NoFrame); m_editor->document()->setModified(false); mBlockSave = false; } void KNote::toDesktop(int desktop) { if (desktop == 0) { return; } #if KDEPIM_HAVE_X11 if (desktop == NETWinInfo::OnAllDesktops) { KWindowSystem::setOnAllDesktops(winId(), true); } else { KWindowSystem::setOnDesktop(winId(), desktop); } #endif } void KNote::setColor(const QColor &fg, const QColor &bg) { m_editor->setColor(fg, bg); QPalette p = palette(); // better: from light(150) to light(100) to light(75) // QLinearGradient g( width()/2, 0, width()/2, height() ); // g.setColorAt( 0, bg ); // g.setColorAt( 1, bg.darker(150) ); p.setColor(QPalette::Window, bg); // p.setBrush( QPalette::Window, g ); p.setColor(QPalette::Base, bg); // p.setBrush( QPalette::Base, g ); p.setColor(QPalette::WindowText, fg); p.setColor(QPalette::Text, fg); p.setColor(QPalette::Button, bg.darker(116)); p.setColor(QPalette::ButtonText, fg); //p.setColor( QPalette::Highlight, bg ); //p.setColor( QPalette::HighlightedText, fg ); // order: Light, Midlight, Button, Mid, Dark, Shadow // the shadow p.setColor(QPalette::Light, bg.lighter(180)); p.setColor(QPalette::Midlight, bg.lighter(150)); p.setColor(QPalette::Mid, bg.lighter(150)); p.setColor(QPalette::Dark, bg.darker(108)); p.setColor(QPalette::Shadow, bg.darker(116)); setPalette(p); // darker values for the active label p.setColor(QPalette::Active, QPalette::Base, bg.darker(116)); m_label->setPalette(p); // set the text color m_editor->setTextColor(fg); // update the icon color KIconEffect effect; QPixmap icon = effect.apply(qApp->windowIcon().pixmap( IconSize(KIconLoader::Desktop), IconSize(KIconLoader::Desktop)), KIconEffect::Colorize, 1, bg, false); QPixmap miniIcon = effect.apply(qApp->windowIcon().pixmap( IconSize(KIconLoader::Small), IconSize(KIconLoader::Small)), KIconEffect::Colorize, 1, bg, false); KWindowSystem::setIcons(winId(), icon, miniIcon); // update the color of the title updateFocus(); Q_EMIT sigColorChanged(); } void KNote::updateLabelAlignment() { // if the name is too long to fit, left-align it, otherwise center it (#59028) const QString labelText = m_label->text(); if (m_label->fontMetrics().boundingRect(labelText).width() > m_label->width()) { m_label->setAlignment(Qt::AlignLeft); } else { m_label->setAlignment(Qt::AlignHCenter); } } void KNote::updateFocus() { if (hasFocus()) { if (!m_editor->isReadOnly()) { if (m_tool && m_tool->isHidden() && m_editor->acceptRichText()) { m_tool->show(); updateLayout(); } m_grip->show(); } else { if (m_tool && !m_tool->isHidden()) { m_tool->hide(); updateLayout(); // to update the minimum height } m_grip->hide(); } } else { m_grip->hide(); if (m_tool && !m_tool->isHidden()) { m_tool->hide(); updateLayout(); // to update the minimum height } } } void KNote::updateLayout() { // TODO: remove later if no longer needed. updateLabelAlignment(); } // -------------------- protected methods -------------------- // void KNote::contextMenuEvent(QContextMenuEvent *e) { if (m_menu) { m_menu->popup(e->globalPos()); } } void KNote::showEvent(QShowEvent *) { if (mDisplayAttribute->isHidden()) { // KWin does not preserve these properties for hidden windows updateKeepAboveBelow(false); slotUpdateShowInTaskbar(); toDesktop(mDisplayAttribute->desktop()); move(mDisplayAttribute->position()); NoteShared::NoteDisplayAttribute *attr = mItem.attribute(Akonadi::Item::AddIfMissing); saveNoteContent(); attr->setIsHidden(false); if (!mBlockSave) { Akonadi::ItemModifyJob *job = new Akonadi::ItemModifyJob(mItem); #ifdef DEBUG_SAVE_NOTE qCDebug(KNOTES_LOG) << "showEvent slotNoteSaved(KJob*)"; #endif connect(job, &Akonadi::ItemModifyJob::result, this, &KNote::slotNoteSaved); } } } void KNote::resizeEvent(QResizeEvent *qre) { QFrame::resizeEvent(qre); updateLayout(); } void KNote::closeEvent(QCloseEvent *event) { if (qApp->isSavingSession()) { return; } event->ignore(); //We don't want to close (and delete the widget). Just hide it slotClose(); } void KNote::dragEnterEvent(QDragEnterEvent *e) { if (!m_editor->isReadOnly()) { e->setAccepted(e->mimeData()->hasColor()); } } void KNote::dropEvent(QDropEvent *e) { if (m_editor->isReadOnly()) { return; } const QMimeData *md = e->mimeData(); if (md->hasColor()) { const QColor bg = qvariant_cast(md->colorData()); NoteShared::NoteDisplayAttribute *attr = mItem.attribute(Akonadi::Item::AddIfMissing); saveNoteContent(); attr->setBackgroundColor(bg); Akonadi::ItemModifyJob *job = new Akonadi::ItemModifyJob(mItem); #ifdef DEBUG_SAVE_NOTE qCDebug(KNOTES_LOG) << "dropEvent slotNoteSaved(KJob*)"; #endif connect(job, &Akonadi::ItemModifyJob::result, this, &KNote::slotNoteSaved); } } bool KNote::event(QEvent *ev) { if (ev->type() == QEvent::LayoutRequest) { updateLayout(); return true; } else { return QFrame::event(ev); } } bool KNote::eventFilter(QObject *o, QEvent *ev) { if (ev->type() == QEvent::DragEnter && static_cast(ev)->mimeData()->hasColor()) { dragEnterEvent(static_cast(ev)); return true; } if (ev->type() == QEvent::Drop && static_cast(ev)->mimeData()->hasColor()) { dropEvent(static_cast(ev)); return true; } if (o == m_label) { QMouseEvent *e = (QMouseEvent *)ev; if (ev->type() == QEvent::MouseButtonDblClick) { if (!m_editor->isReadOnly()) { slotRename(); } } + + if (ev->type() == QEvent::MouseButtonPress + && ( (e->buttons() & Qt::LeftButton) == Qt::LeftButton + || (e->buttons() & Qt::MidButton) == Qt::MidButton)) { + mOrigPos = e->pos(); + return false; + } + if (ev->type() == QEvent::MouseMove && ( (e->buttons() & Qt::LeftButton) == Qt::LeftButton || (e->buttons() & Qt::MidButton) == Qt::MidButton)) { - move(e->globalPos()); + QPoint newPos = e->globalPos() - mOrigPos - QPoint(1, 1); + move(newPos); return true; } - if (ev->type() == QEvent::MouseButtonRelease) { - move(e->globalPos()); + if (ev->type() == QEvent::MouseButtonRelease + && ( (e->buttons() & Qt::LeftButton) == Qt::LeftButton + || (e->buttons() & Qt::MidButton) == Qt::MidButton)) { + QPoint newPos = e->globalPos() - mOrigPos - QPoint(1, 1); + move(newPos); return false; } return false; } if (o == m_editor) { if (ev->type() == QEvent::FocusOut) { QFocusEvent *fe = static_cast(ev); if (fe->reason() != Qt::PopupFocusReason && fe->reason() != Qt::MouseFocusReason) { updateFocus(); if (!mBlockSave) { saveNote(true); } } } else if (ev->type() == QEvent::FocusIn) { updateFocus(); } return false; } return false; } Akonadi::Item KNote::item() const { return mItem; } void KNote::slotDebugAkonadiSearch() { QPointer dlg = new Akonadi::Search::AkonadiSearchDebugDialog; dlg->setAkonadiId(mItem.id()); dlg->setAttribute(Qt::WA_DeleteOnClose); dlg->setSearchType(Akonadi::Search::AkonadiSearchDebugSearchPathComboBox::Notes); dlg->doSearch(); dlg->show(); } diff --git a/src/notes/knote.h b/src/notes/knote.h index 75c4df6..563d784 100644 --- a/src/notes/knote.h +++ b/src/notes/knote.h @@ -1,166 +1,167 @@ /******************************************************************* KNotes -- Notes for the KDE project Copyright (c) 1997-2013, The KNotes Developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *******************************************************************/ #ifndef KNOTE_H #define KNOTE_H #include #include "knoteinterface.h" #include #include #include #include #include #include class KNoteButton; class KNoteDisplaySettings; class KNoteEdit; class KSelectAction; class KToggleAction; class KToolBar; class QLabel; class QMenu; class QSizeGrip; class KNote : public QFrame, virtual public KXMLGUIClient, public KNoteInterface { Q_OBJECT public: explicit KNote(const QDomDocument &buildDoc, const Akonadi::Item &item, bool allowAkonadiSearchDebug = false, QWidget *parent = nullptr); ~KNote(); void setChangeItem(const Akonadi::Item &item, const QSet &set = QSet()); void saveNote(bool force = false, bool sync = false); QString name() const; QString text() const; Akonadi::Item::Id noteId() const; Akonadi::Item item() const; void setName(const QString &name); void setText(const QString &text); bool isModified() const; bool isDesktopAssigned() const; void toDesktop(int desktop); public Q_SLOTS: void slotRename(); void slotKill(bool force = false); void slotClose(); Q_SIGNALS: void sigRequestNewNote(); void sigShowNextNote(); void sigNameChanged(const QString &); void sigColorChanged(); void sigKillNote(Akonadi::Item::Id); protected: void contextMenuEvent(QContextMenuEvent *) override; void showEvent(QShowEvent *) override; void resizeEvent(QResizeEvent *) override; void closeEvent(QCloseEvent *) override; void dropEvent(QDropEvent *) override; void dragEnterEvent(QDragEnterEvent *) override; bool event(QEvent *) override; bool eventFilter(QObject *, QEvent *) override; private Q_SLOTS: void slotUpdateReadOnly(); void slotSend(); void slotMail(); void slotPrint(); void slotPrintPreview(); void slotSaveAs(); void slotSetAlarm(); void slotPreferences(); void slotPopupActionToDesktop(QAction *act); void slotApplyConfig(); void slotUpdateShowInTaskbar(); void slotUpdateDesktopActions(); void slotKeepAbove(); void slotKeepBelow(); void slotRequestNewNote(); void slotNoteSaved(KJob *job); void slotDebugAkonadiSearch(); private: void updateKeepAboveBelow(bool save = true); void buildGui(); void createActions(); void createNoteEditor(const QString &configFile); void createNoteFooter(); void createNoteHeader(); void prepare(); void updateFocus(); void updateLayout(); void updateLabelAlignment(); void setColor(const QColor &, const QColor &); void print(bool preview); void setDisplayDefaultValue(); private: void loadNoteContent(const Akonadi::Item &item); void updateAllAttributes(); void saveNoteContent(); Akonadi::Item mItem; QLayout *m_noteLayout = nullptr; QLabel *m_label = nullptr; QSizeGrip *m_grip = nullptr; KNoteButton *m_button = nullptr; KToolBar *m_tool = nullptr; KNoteEdit *m_editor = nullptr; QMenu *m_menu = nullptr; KToggleAction *m_readOnly = nullptr; #if KDEPIM_HAVE_X11 KSelectAction *m_toDesktop = nullptr; #endif KToggleAction *m_keepAbove = nullptr; KToggleAction *m_keepBelow = nullptr; KSharedConfig::Ptr m_kwinConf; KNoteDisplaySettings *mDisplayAttribute = nullptr; bool mAllowDebugAkonadiSearch; + QPoint mOrigPos; }; #endif