File indexing completed on 2022-12-06 21:08:29

0001 /**
0002  * SPDX-FileCopyrightText: (C) 2003 Sébastien Laoût <slaout@linux62.org>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "noteedit.h"
0008 
0009 #include <QAction>
0010 #include <QApplication>
0011 #include <QColorDialog>
0012 #include <QDialogButtonBox>
0013 #include <QFontComboBox>
0014 #include <QGraphicsProxyWidget>
0015 #include <QGridLayout>
0016 #include <QHBoxLayout>
0017 #include <QLabel>
0018 #include <QLineEdit>
0019 #include <QLocale>
0020 #include <QPushButton>
0021 #include <QScrollBar>
0022 #include <QVBoxLayout>
0023 #include <QWidgetAction>
0024 #include <QtGui/QKeyEvent>
0025 #include <QtGui/QTextCharFormat>
0026 
0027 #include <KActionCollection>
0028 #include <KColorCombo>
0029 #include <KConfig>
0030 #include <KConfigGroup>
0031 #include <KDesktopFile>
0032 #include <KIconButton>
0033 #include <KLineEdit>
0034 #include <KLocalizedString>
0035 #include <KMessageBox>
0036 #include <KService>
0037 #include <KToggleAction>
0038 #include <KToolBar>
0039 #include <KUrlRequester>
0040 
0041 #include "basketlistview.h"
0042 #include "basketscene.h"
0043 #include "focusedwidgets.h"
0044 #include "icon_names.h"
0045 #include "note.h"
0046 #include "notecontent.h"
0047 #include "notefactory.h"
0048 #include "settings.h"
0049 #include "tools.h"
0050 #include "variouswidgets.h"
0051 
0052 /** class NoteEditor: */
0053 
0054 NoteEditor::NoteEditor(NoteContent *noteContent)
0055 {
0056     m_isEmpty = false;
0057     m_canceled = false;
0058     m_widget = nullptr;
0059     m_textEdit = nullptr;
0060     m_lineEdit = nullptr;
0061     m_noteContent = noteContent;
0062 }
0063 
0064 NoteEditor::~NoteEditor()
0065 {
0066     delete m_widget;
0067 }
0068 
0069 Note *NoteEditor::note()
0070 {
0071     return m_noteContent->note();
0072 }
0073 
0074 void NoteEditor::setCursorTo(const QPointF &pos)
0075 {
0076     // clicked comes from the QMouseEvent, which is in item's coordinate system.
0077     if (m_textEdit) {
0078         QPointF currentPos = note()->mapFromScene(pos);
0079         QPointF deltaPos = m_textEdit->pos() - note()->pos();
0080         m_textEdit->setTextCursor(m_textEdit->cursorForPosition((currentPos - deltaPos).toPoint()));
0081     }
0082 }
0083 
0084 void NoteEditor::startSelection(const QPointF &pos)
0085 {
0086     if (m_textEdit) {
0087         QPointF currentPos = note()->mapFromScene(pos);
0088         QPointF deltaPos = m_textEdit->pos() - note()->pos();
0089         m_textEdit->setTextCursor(m_textEdit->cursorForPosition((currentPos - deltaPos).toPoint()));
0090     }
0091 }
0092 
0093 void NoteEditor::updateSelection(const QPointF &pos)
0094 {
0095     if (m_textEdit) {
0096         QPointF currentPos = note()->mapFromScene(pos);
0097         QPointF deltaPos = m_textEdit->pos() - note()->pos();
0098 
0099         QTextCursor cursor = m_textEdit->cursorForPosition((currentPos - deltaPos).toPoint());
0100         QTextCursor currentCursor = m_textEdit->textCursor();
0101         // select the text
0102         currentCursor.setPosition(cursor.position(), QTextCursor::KeepAnchor);
0103         // update the cursor
0104         m_textEdit->setTextCursor(currentCursor);
0105     }
0106 }
0107 
0108 void NoteEditor::endSelection(const QPointF & /*pos*/)
0109 {
0110     // For TextEdit inside GraphicsScene selectionChanged() is only generated for the first selected char -
0111     // thus we need to call it manually after selection is finished
0112     if (FocusedTextEdit *textEdit = dynamic_cast<FocusedTextEdit *>(m_textEdit))
0113         textEdit->onSelectionChanged();
0114 }
0115 
0116 void NoteEditor::paste(const QPointF &pos, QClipboard::Mode mode)
0117 {
0118     if (FocusedTextEdit *textEdit = dynamic_cast<FocusedTextEdit *>(m_textEdit)) {
0119         setCursorTo(pos);
0120         textEdit->paste(mode);
0121     }
0122 }
0123 
0124 void NoteEditor::connectActions(BasketScene *scene)
0125 {
0126     if (m_textEdit) {
0127         connect(m_textEdit, SIGNAL(textChanged()), scene, SLOT(selectionChangedInEditor()));
0128         connect(m_textEdit, SIGNAL(textChanged()), scene, SLOT(contentChangedInEditor()));
0129         connect(m_textEdit, &KTextEdit::textChanged, scene, &BasketScene::placeEditorAndEnsureVisible);
0130         connect(m_textEdit, SIGNAL(selectionChanged()), scene, SLOT(selectionChangedInEditor()));
0131 
0132     } else if (m_lineEdit) {
0133         connect(m_lineEdit, SIGNAL(textChanged(const QString &)), scene, SLOT(selectionChangedInEditor()));
0134         connect(m_lineEdit, SIGNAL(textChanged(const QString &)), scene, SLOT(contentChangedInEditor()));
0135         connect(m_lineEdit, SIGNAL(selectionChanged()), scene, SLOT(selectionChangedInEditor()));
0136     }
0137 }
0138 
0139 NoteEditor *NoteEditor::editNoteContent(NoteContent *noteContent, QWidget *parent)
0140 {
0141     TextContent *textContent = dynamic_cast<TextContent *>(noteContent);
0142     if (textContent)
0143         return new TextEditor(textContent, parent);
0144 
0145     HtmlContent *htmlContent = dynamic_cast<HtmlContent *>(noteContent);
0146     if (htmlContent)
0147         return new HtmlEditor(htmlContent, parent);
0148 
0149     ImageContent *imageContent = dynamic_cast<ImageContent *>(noteContent);
0150     if (imageContent)
0151         return new ImageEditor(imageContent, parent);
0152 
0153     AnimationContent *animationContent = dynamic_cast<AnimationContent *>(noteContent);
0154     if (animationContent)
0155         return new AnimationEditor(animationContent, parent);
0156 
0157     FileContent *fileContent = dynamic_cast<FileContent *>(noteContent); // Same for SoundContent
0158     if (fileContent)
0159         return new FileEditor(fileContent, parent);
0160 
0161     LinkContent *linkContent = dynamic_cast<LinkContent *>(noteContent);
0162     if (linkContent)
0163         return new LinkEditor(linkContent, parent);
0164 
0165     CrossReferenceContent *crossReferenceContent = dynamic_cast<CrossReferenceContent *>(noteContent);
0166     if (crossReferenceContent)
0167         return new CrossReferenceEditor(crossReferenceContent, parent);
0168 
0169     LauncherContent *launcherContent = dynamic_cast<LauncherContent *>(noteContent);
0170     if (launcherContent)
0171         return new LauncherEditor(launcherContent, parent);
0172 
0173     ColorContent *colorContent = dynamic_cast<ColorContent *>(noteContent);
0174     if (colorContent)
0175         return new ColorEditor(colorContent, parent);
0176 
0177     UnknownContent *unknownContent = dynamic_cast<UnknownContent *>(noteContent);
0178     if (unknownContent)
0179         return new UnknownEditor(unknownContent, parent);
0180 
0181     return nullptr;
0182 }
0183 
0184 void NoteEditor::setInlineEditor(QWidget *inlineEditor)
0185 {
0186     if (!m_widget) {
0187         m_widget = new QGraphicsProxyWidget();
0188     }
0189     m_widget->setWidget(inlineEditor);
0190     m_widget->setZValue(500);
0191     // m_widget->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
0192     m_widget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
0193 
0194     m_textEdit = nullptr;
0195     m_lineEdit = nullptr;
0196     KTextEdit *textEdit = dynamic_cast<KTextEdit *>(inlineEditor);
0197     if (textEdit) {
0198         m_textEdit = textEdit;
0199     } else {
0200         QLineEdit *lineEdit = dynamic_cast<QLineEdit *>(inlineEditor);
0201         if (lineEdit) {
0202             m_lineEdit = lineEdit;
0203         }
0204     }
0205 }
0206 
0207 /** class TextEditor: */
0208 
0209 TextEditor::TextEditor(TextContent *textContent, QWidget *parent)
0210     : NoteEditor(textContent)
0211     , m_textContent(textContent)
0212 {
0213     FocusedTextEdit *textEdit = new FocusedTextEdit(/*disableUpdatesOnKeyPress=*/true, parent);
0214     textEdit->setLineWidth(0);
0215     textEdit->setMidLineWidth(0);
0216     textEdit->setFrameStyle(QFrame::Box);
0217     QPalette palette;
0218     palette.setColor(textEdit->backgroundRole(), note()->backgroundColor());
0219     palette.setColor(textEdit->foregroundRole(), note()->textColor());
0220     textEdit->setPalette(palette);
0221 
0222     textEdit->setFont(note()->font());
0223     textEdit->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0224 
0225     if (Settings::spellCheckTextNotes())
0226         textEdit->setCheckSpellingEnabled(true);
0227     textEdit->setPlainText(m_textContent->text());
0228 
0229     // Not sure if the following comment is still true
0230     // FIXME: Sometimes, the cursor flicker at ends before being positioned where clicked (because qApp->processEvents() I think)
0231     textEdit->moveCursor(QTextCursor::End);
0232     textEdit->verticalScrollBar()->setCursor(Qt::ArrowCursor);
0233     setInlineEditor(textEdit);
0234     connect(textEdit, &FocusedTextEdit::escapePressed, this, &TextEditor::askValidation);
0235     connect(textEdit, &FocusedTextEdit::mouseEntered, this, &TextEditor::mouseEnteredEditorWidget);
0236 
0237     connect(textEdit, &FocusedTextEdit::cursorPositionChanged, textContent->note()->basket(), &BasketScene::editorCursorPositionChanged);
0238     // In case it is a very big note, the top is displayed and Enter is pressed: the cursor is on bottom, we should enure it visible:
0239     QTimer::singleShot(0, textContent->note()->basket(), SLOT(editorCursorPositionChanged()));
0240 }
0241 
0242 TextEditor::~TextEditor()
0243 {
0244     delete graphicsWidget()->widget(); // TODO: delete that in validate(), so we can remove one method
0245 }
0246 
0247 void TextEditor::autoSave(bool toFileToo)
0248 {
0249     bool autoSpellCheck = true;
0250     if (toFileToo) {
0251         if (Settings::spellCheckTextNotes() != textEdit()->checkSpellingEnabled()) {
0252             Settings::setSpellCheckTextNotes(textEdit()->checkSpellingEnabled());
0253             Settings::saveConfig();
0254         }
0255 
0256         autoSpellCheck = textEdit()->checkSpellingEnabled();
0257         textEdit()->setCheckSpellingEnabled(false);
0258     }
0259 
0260     m_textContent->setText(textEdit()->toPlainText());
0261 
0262     if (toFileToo) {
0263         m_textContent->saveToFile();
0264         m_textContent->setEdited();
0265         textEdit()->setCheckSpellingEnabled(autoSpellCheck);
0266     }
0267 }
0268 
0269 void TextEditor::validate()
0270 {
0271     if (Settings::spellCheckTextNotes() != textEdit()->checkSpellingEnabled()) {
0272         Settings::setSpellCheckTextNotes(textEdit()->checkSpellingEnabled());
0273         Settings::saveConfig();
0274     }
0275 
0276     textEdit()->setCheckSpellingEnabled(false);
0277     if (textEdit()->document()->isEmpty())
0278         setEmpty();
0279     m_textContent->setText(textEdit()->toPlainText());
0280     m_textContent->saveToFile();
0281     m_textContent->setEdited();
0282 
0283     note()->setWidth(0);
0284 }
0285 
0286 /** class HtmlEditor: */
0287 
0288 HtmlEditor::HtmlEditor(HtmlContent *htmlContent, QWidget *parent)
0289     : NoteEditor(htmlContent)
0290     , m_htmlContent(htmlContent)
0291 {
0292     FocusedTextEdit *textEdit = new FocusedTextEdit(/*disableUpdatesOnKeyPress=*/true, parent);
0293     textEdit->setLineWidth(0);
0294     textEdit->setMidLineWidth(0);
0295     textEdit->setFrameStyle(QFrame::Box);
0296     textEdit->setAutoFormatting(Settings::autoBullet() ? QTextEdit::AutoAll : QTextEdit::AutoNone);
0297 
0298     QPalette palette;
0299     palette.setColor(textEdit->backgroundRole(), note()->backgroundColor());
0300     palette.setColor(textEdit->foregroundRole(), note()->textColor());
0301     textEdit->setPalette(palette);
0302 
0303     textEdit->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0304     textEdit->setHtml(Tools::detectCrossReferences(m_htmlContent->html(), /*userLink=*/true));
0305     textEdit->moveCursor(QTextCursor::End);
0306     textEdit->verticalScrollBar()->setCursor(Qt::ArrowCursor);
0307     setInlineEditor(textEdit);
0308 
0309     connect(textEdit, &FocusedTextEdit::mouseEntered, this, &HtmlEditor::mouseEnteredEditorWidget);
0310     connect(textEdit, &FocusedTextEdit::escapePressed, this, &HtmlEditor::askValidation);
0311 
0312     connect(InlineEditors::instance()->richTextFont, &QFontComboBox::currentFontChanged, this, &HtmlEditor::onFontSelectionChanged);
0313     connect(InlineEditors::instance()->richTextFontSize, &FontSizeCombo::sizeChanged, textEdit, &FocusedTextEdit::setFontPointSize);
0314     connect(InlineEditors::instance()->richTextColor, &KColorCombo::activated, textEdit, &FocusedTextEdit::setTextColor);
0315 
0316     connect(InlineEditors::instance()->focusWidgetFilter, &FocusWidgetFilter::escapePressed, textEdit, [&textEdit]() { textEdit->setFocus(); });
0317     connect(InlineEditors::instance()->focusWidgetFilter, &FocusWidgetFilter::returnPressed, textEdit, [&textEdit]() { textEdit->setFocus(); });
0318     connect(InlineEditors::instance()->richTextFont, SIGNAL(activated(int)), textEdit, SLOT(setFocus()));
0319 
0320     connect(InlineEditors::instance()->richTextFontSize, SIGNAL(activated(int)), textEdit, SLOT(setFocus()));
0321 
0322     connect(textEdit, SIGNAL(cursorPositionChanged()), this, SLOT(cursorPositionChanged()));
0323     connect(textEdit, SIGNAL(currentCharFormatChanged(const QTextCharFormat &)), this, SLOT(charFormatChanged(const QTextCharFormat &)));
0324     //  connect( textEdit,  SIGNAL(currentVerticalAlignmentChanged(VerticalAlignment)), this, SLOT(slotVerticalAlignmentChanged()) );
0325 
0326     connect(InlineEditors::instance()->richTextBold, SIGNAL(triggered(bool)), this, SLOT(setBold(bool)));
0327     connect(InlineEditors::instance()->richTextItalic, SIGNAL(triggered(bool)), textEdit, SLOT(setFontItalic(bool)));
0328     connect(InlineEditors::instance()->richTextUnderline, SIGNAL(triggered(bool)), textEdit, SLOT(setFontUnderline(bool)));
0329     connect(InlineEditors::instance()->richTextLeft, SIGNAL(triggered()), this, SLOT(setLeft()));
0330     connect(InlineEditors::instance()->richTextCenter, SIGNAL(triggered()), this, SLOT(setCentered()));
0331     connect(InlineEditors::instance()->richTextRight, SIGNAL(triggered()), this, SLOT(setRight()));
0332     connect(InlineEditors::instance()->richTextJustified, SIGNAL(triggered()), this, SLOT(setBlock()));
0333 
0334     //  InlineEditors::instance()->richTextToolBar()->show();
0335     cursorPositionChanged();
0336     charFormatChanged(textEdit->currentCharFormat());
0337     // QTimer::singleShot( 0, this, SLOT(cursorPositionChanged()) );
0338     InlineEditors::instance()->enableRichTextToolBar();
0339 
0340     connect(InlineEditors::instance()->richTextUndo, SIGNAL(triggered()), textEdit, SLOT(undo()));
0341     connect(InlineEditors::instance()->richTextRedo, SIGNAL(triggered()), textEdit, SLOT(redo()));
0342     connect(textEdit, SIGNAL(undoAvailable(bool)), InlineEditors::instance()->richTextUndo, SLOT(setEnabled(bool)));
0343     connect(textEdit, SIGNAL(redoAvailable(bool)), InlineEditors::instance()->richTextRedo, SLOT(setEnabled(bool)));
0344     connect(textEdit, SIGNAL(textChanged()), this, SLOT(editTextChanged()));
0345     InlineEditors::instance()->richTextUndo->setEnabled(false);
0346     InlineEditors::instance()->richTextRedo->setEnabled(false);
0347 
0348     connect(textEdit, SIGNAL(cursorPositionChanged()), htmlContent->note()->basket(), SLOT(editorCursorPositionChanged()));
0349     // In case it is a very big note, the top is displayed and Enter is pressed: the cursor is on bottom, we should enure it visible:
0350     QTimer::singleShot(0, htmlContent->note()->basket(), SLOT(editorCursorPositionChanged()));
0351 }
0352 
0353 void HtmlEditor::cursorPositionChanged()
0354 {
0355     InlineEditors::instance()->richTextFont->setCurrentFont(textEdit()->currentFont().family());
0356     if (InlineEditors::instance()->richTextColor->color() != textEdit()->textColor())
0357         InlineEditors::instance()->richTextColor->setColor(textEdit()->textColor());
0358     InlineEditors::instance()->richTextBold->setChecked((textEdit()->fontWeight() >= QFont::Bold));
0359     InlineEditors::instance()->richTextItalic->setChecked(textEdit()->fontItalic());
0360     InlineEditors::instance()->richTextUnderline->setChecked(textEdit()->fontUnderline());
0361 
0362     switch (textEdit()->alignment()) {
0363     default:
0364     case 1 /*Qt::AlignLeft*/:
0365         InlineEditors::instance()->richTextLeft->setChecked(true);
0366         break;
0367     case 2 /*Qt::AlignRight*/:
0368         InlineEditors::instance()->richTextRight->setChecked(true);
0369         break;
0370     case 4 /*Qt::AlignHCenter*/:
0371         InlineEditors::instance()->richTextCenter->setChecked(true);
0372         break;
0373     case 8 /*Qt::AlignJustify*/:
0374         InlineEditors::instance()->richTextJustified->setChecked(true);
0375         break;
0376     }
0377 }
0378 
0379 void HtmlEditor::editTextChanged()
0380 {
0381     // The following is a workaround for an apparent Qt bug.
0382     // When I start typing in a textEdit, the undo&redo actions are not enabled until I click
0383     // or move the cursor - probably, the signal undoAvailable() is not emitted.
0384     // So, I had to intervene and do that manually.
0385     InlineEditors::instance()->richTextUndo->setEnabled(textEdit()->document()->isUndoAvailable());
0386     InlineEditors::instance()->richTextRedo->setEnabled(textEdit()->document()->isRedoAvailable());
0387 }
0388 
0389 void HtmlEditor::charFormatChanged(const QTextCharFormat &format)
0390 {
0391     InlineEditors::instance()->richTextFontSize->setFontSize(format.font().pointSize());
0392 }
0393 
0394 /*void HtmlEditor::slotVerticalAlignmentChanged(QTextEdit::VerticalAlignment align)
0395 {
0396     QTextEdit::VerticalAlignment align = textEdit()
0397     switch (align) {
0398         case KTextEdit::AlignSuperScript:
0399             InlineEditors::instance()->richTextSuper->setChecked(true);
0400             InlineEditors::instance()->richTextSub->setChecked(false);
0401             break;
0402         case KTextEdit::AlignSubScript:
0403             InlineEditors::instance()->richTextSuper->setChecked(false);
0404             InlineEditors::instance()->richTextSub->setChecked(true);
0405             break;
0406         default:
0407             InlineEditors::instance()->richTextSuper->setChecked(false);
0408             InlineEditors::instance()->richTextSub->setChecked(false);
0409     }
0410 
0411     NoteHtmlEditor::buttonToggled(int id) :
0412         case 106:
0413             if (isChecked && m_toolbar->isButtonOn(107))
0414                 m_toolbar->setButton(107, false);
0415             m_text->setVerticalAlignment(isChecked ? KTextEdit::AlignSuperScript : KTextEdit::AlignNormal);
0416             break;
0417         case 107:
0418             if (isChecked && m_toolbar->isButtonOn(106))
0419                 m_toolbar->setButton(106, false);
0420             m_text->setVerticalAlignment(isChecked ? KTextEdit::AlignSubScript   : KTextEdit::AlignNormal);
0421             break;
0422 }*/
0423 
0424 void HtmlEditor::setLeft()
0425 {
0426     textEdit()->setAlignment(Qt::AlignLeft);
0427 }
0428 void HtmlEditor::setRight()
0429 {
0430     textEdit()->setAlignment(Qt::AlignRight);
0431 }
0432 void HtmlEditor::setCentered()
0433 {
0434     textEdit()->setAlignment(Qt::AlignHCenter);
0435 }
0436 void HtmlEditor::setBlock()
0437 {
0438     textEdit()->setAlignment(Qt::AlignJustify);
0439 }
0440 
0441 void HtmlEditor::onFontSelectionChanged(const QFont &font)
0442 {
0443     // Change font family only
0444     textEdit()->setFontFamily(font.family());
0445     InlineEditors::instance()->richTextFont->clearFocus();
0446     // textEdit()->setFocus();
0447 }
0448 
0449 void HtmlEditor::setBold(bool isChecked)
0450 {
0451     qWarning() << "setBold " << isChecked;
0452     textEdit()->setFontWeight(isChecked ? QFont::Bold : QFont::Normal);
0453 }
0454 
0455 HtmlEditor::~HtmlEditor()
0456 {
0457     // delete graphicsWidget()->widget();
0458 }
0459 
0460 void HtmlEditor::autoSave(bool toFileToo)
0461 {
0462     m_htmlContent->setHtml(Tools::textDocumentToMinimalHTML(textEdit()->document()));
0463     if (toFileToo) {
0464         m_htmlContent->saveToFile();
0465         m_htmlContent->setEdited();
0466     }
0467 }
0468 
0469 void HtmlEditor::validate()
0470 {
0471     if (Tools::htmlToText(textEdit()->toHtml()).isEmpty())
0472         setEmpty();
0473     if (Settings::detectTextTags())
0474         detectTags();
0475     QString convert = Tools::textDocumentToMinimalHTML(textEdit()->document());
0476     QString textEquivalent = Tools::htmlToText(convert);
0477 
0478     if (note()->allowCrossReferences())
0479         convert = Tools::detectCrossReferences(convert, /*userLink=*/true);
0480 
0481     m_htmlContent->setHtml(convert);
0482     m_htmlContent->saveToFile();
0483     m_htmlContent->setEdited();
0484 
0485     disconnect();
0486     graphicsWidget()->disconnect();
0487     if (InlineEditors::instance()) {
0488         InlineEditors::instance()->disableRichTextToolBar();
0489         //      if (InlineEditors::instance()->richTextToolBar())
0490         //          InlineEditors::instance()->richTextToolBar()->hide();
0491     }
0492 
0493     if (graphicsWidget()) {
0494         note()->setZValue(1);
0495         delete graphicsWidget()->widget();
0496         setInlineEditor(nullptr);
0497     }
0498 }
0499 
0500 void HtmlEditor::detectTags()
0501 {
0502     QTextDocument * doc = textEdit()->document();
0503     const QTextBlock& block = doc->firstBlock();
0504     if (!block.isValid() || block.begin() == block.end()) return;
0505 
0506     const QTextFragment& fragment = block.begin().fragment();
0507     if (!fragment.isValid() || fragment.length() == 0) return;
0508     //Process unstyled text only
0509     const QTextCharFormat& charFmt = fragment.charFormat();
0510     if (charFmt.propertyCount() > 0) return;
0511 
0512     QString newText = fragment.text();
0513     int prefixLength;
0514     QTextCursor cursor(block);
0515     const QList<State*>& states = Tools::detectTags(fragment.text(), prefixLength);
0516     cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, prefixLength);
0517     cursor.removeSelectedText();
0518 
0519     for (State* state: states)
0520     {
0521         note()->addState(state, true);
0522     }
0523 }
0524 /** class ImageEditor: */
0525 
0526 ImageEditor::ImageEditor(ImageContent *imageContent, QWidget *parent)
0527     : NoteEditor(imageContent)
0528 {
0529     int choice = KMessageBox::questionYesNoCancel(parent,
0530                                                   i18n("Images can not be edited here at the moment (the next version of BasKet Note Pads will include an image editor).\n"
0531                                                        "Do you want to open it with an application that understand it?"),
0532                                                   i18n("Edit Image Note"),
0533                                                   KStandardGuiItem::open(),
0534                                                   KGuiItem(i18n("Load From &File..."), IconNames::DOCUMENT_IMPORT),
0535                                                   KStandardGuiItem::cancel());
0536 
0537     switch (choice) {
0538     case (KMessageBox::Yes):
0539         note()->basket()->noteOpen(note());
0540         break;
0541     case (KMessageBox::No): // Load from file
0542         cancel();
0543         Global::bnpView->insertWizard(3); // 3 maps to m_actLoadFile
0544         break;
0545     case (KMessageBox::Cancel):
0546         cancel();
0547         break;
0548     }
0549 }
0550 
0551 /** class AnimationEditor: */
0552 
0553 AnimationEditor::AnimationEditor(AnimationContent *animationContent, QWidget *parent)
0554     : NoteEditor(animationContent)
0555 {
0556     int choice = KMessageBox::questionYesNo(parent,
0557                                             i18n("This animated image can not be edited here.\n"
0558                                                  "Do you want to open it with an application that understands it?"),
0559                                             i18n("Edit Animation Note"),
0560                                             KStandardGuiItem::open(),
0561                                             KStandardGuiItem::cancel());
0562 
0563     if (choice == KMessageBox::Yes)
0564         note()->basket()->noteOpen(note());
0565 }
0566 
0567 /** class FileEditor: */
0568 
0569 FileEditor::FileEditor(FileContent *fileContent, QWidget *parent)
0570     : NoteEditor(fileContent)
0571     , m_fileContent(fileContent)
0572 {
0573     QLineEdit *lineEdit = new QLineEdit(parent);
0574     FocusWidgetFilter *filter = new FocusWidgetFilter(lineEdit);
0575 
0576     QPalette palette;
0577     palette.setColor(lineEdit->backgroundRole(), note()->backgroundColor());
0578     palette.setColor(lineEdit->foregroundRole(), note()->textColor());
0579     lineEdit->setPalette(palette);
0580 
0581     lineEdit->setFont(note()->font());
0582     lineEdit->setText(m_fileContent->fileName());
0583     lineEdit->selectAll();
0584     setInlineEditor(lineEdit);
0585     connect(filter, SIGNAL(returnPressed()), this, SIGNAL(askValidation()));
0586     connect(filter, SIGNAL(escapePressed()), this, SIGNAL(askValidation()));
0587     connect(filter, SIGNAL(mouseEntered()), this, SIGNAL(mouseEnteredEditorWidget()));
0588 }
0589 
0590 FileEditor::~FileEditor()
0591 {
0592     delete graphicsWidget()->widget();
0593 }
0594 
0595 void FileEditor::autoSave(bool toFileToo)
0596 {
0597     // FIXME: How to detect cancel?
0598     if (toFileToo && !lineEdit()->text().isEmpty() && m_fileContent->trySetFileName(lineEdit()->text())) {
0599         m_fileContent->setFileName(lineEdit()->text());
0600         m_fileContent->setEdited();
0601     }
0602 }
0603 
0604 void FileEditor::validate()
0605 {
0606     autoSave(/*toFileToo=*/true);
0607 }
0608 
0609 /** class LinkEditor: */
0610 
0611 LinkEditor::LinkEditor(LinkContent *linkContent, QWidget *parent)
0612     : NoteEditor(linkContent)
0613 {
0614     QPointer<LinkEditDialog> dialog = new LinkEditDialog(linkContent, parent);
0615     if (dialog->exec() == QDialog::Rejected)
0616         cancel();
0617     if (linkContent->url().isEmpty() && linkContent->title().isEmpty())
0618         setEmpty();
0619 }
0620 
0621 /** class CrossReferenceEditor: */
0622 
0623 CrossReferenceEditor::CrossReferenceEditor(CrossReferenceContent *crossReferenceContent, QWidget *parent)
0624     : NoteEditor(crossReferenceContent)
0625 {
0626     QPointer<CrossReferenceEditDialog> dialog = new CrossReferenceEditDialog(crossReferenceContent, parent);
0627     if (dialog->exec() == QDialog::Rejected)
0628         cancel();
0629     if (crossReferenceContent->url().isEmpty() && crossReferenceContent->title().isEmpty())
0630         setEmpty();
0631 }
0632 
0633 /** class LauncherEditor: */
0634 
0635 LauncherEditor::LauncherEditor(LauncherContent *launcherContent, QWidget *parent)
0636     : NoteEditor(launcherContent)
0637 {
0638     QPointer<LauncherEditDialog> dialog = new LauncherEditDialog(launcherContent, parent);
0639     if (dialog->exec() == QDialog::Rejected)
0640         cancel();
0641     if (launcherContent->name().isEmpty() && launcherContent->exec().isEmpty())
0642         setEmpty();
0643 }
0644 
0645 /** class ColorEditor: */
0646 
0647 ColorEditor::ColorEditor(ColorContent *colorContent, QWidget *parent)
0648     : NoteEditor(colorContent)
0649 {
0650     QPointer<QColorDialog> dialog = new QColorDialog(parent);
0651     dialog->setCurrentColor(colorContent->color());
0652     dialog->setWindowTitle(i18n("Edit Color Note"));
0653     // dialog->setButtons(QDialog::Ok | QDialog::Cancel);
0654     if (dialog->exec() == QDialog::Accepted) {
0655         if (dialog->currentColor() != colorContent->color()) {
0656             colorContent->setColor(dialog->currentColor());
0657             colorContent->setEdited();
0658         }
0659     } else
0660         cancel();
0661 
0662     /* This code don't allow to set a caption to the dialog:
0663     QColor color = colorContent()->color();
0664     color = QColorDialog::getColor(parent)==QDialog::Accepted&&color!=m_color);
0665     if ( color.isValid() ) {
0666         colorContent()->setColor(color);
0667         setEdited();
0668     }*/
0669 }
0670 
0671 /** class UnknownEditor: */
0672 
0673 UnknownEditor::UnknownEditor(UnknownContent *unknownContent, QWidget *parent)
0674     : NoteEditor(unknownContent)
0675 {
0676     KMessageBox::information(parent,
0677                              i18n("The type of this note is unknown and can not be edited here.\n"
0678                                   "You however can drag or copy the note into an application that understands it."),
0679                              i18n("Edit Unknown Note"));
0680 }
0681 
0682 /*********************************************************************/
0683 
0684 /** class LinkEditDialog: */
0685 
0686 LinkEditDialog::LinkEditDialog(LinkContent *contentNote, QWidget *parent /*, QKeyEvent *ke*/)
0687     : QDialog(parent)
0688     , m_noteContent(contentNote)
0689 {
0690     // QDialog options
0691     setWindowTitle(i18n("Edit Link Note"));
0692 
0693     QWidget *mainWidget = new QWidget(this);
0694     QVBoxLayout *mainLayout = new QVBoxLayout;
0695     setLayout(mainLayout);
0696     mainLayout->addWidget(mainWidget);
0697 
0698     setObjectName("EditLink");
0699     setModal(true);
0700 
0701     QWidget *page = new QWidget(this);
0702     mainLayout->addWidget(page);
0703     // QGridLayout *layout = new QGridLayout(page, /*nRows=*/4, /*nCols=*/2, /*margin=*/0, spacingHint());
0704     QGridLayout *layout = new QGridLayout(page);
0705     mainLayout->addLayout(layout);
0706 
0707     QWidget *wid1 = new QWidget(page);
0708     mainLayout->addWidget(wid1);
0709     QHBoxLayout *titleLay = new QHBoxLayout(wid1);
0710     m_title = new QLineEdit(m_noteContent->title(), wid1);
0711     m_autoTitle = new QPushButton(i18n("Auto"), wid1);
0712     m_autoTitle->setCheckable(true);
0713     m_autoTitle->setChecked(m_noteContent->autoTitle());
0714     titleLay->addWidget(m_title);
0715     titleLay->addWidget(m_autoTitle);
0716 
0717     QWidget *wid = new QWidget(page);
0718     mainLayout->addWidget(wid);
0719     QHBoxLayout *hLay = new QHBoxLayout(wid);
0720     m_icon = new KIconButton(wid);
0721     QLabel *label3 = new QLabel(page);
0722     mainLayout->addWidget(label3);
0723     label3->setText(i18n("&Icon:"));
0724     label3->setBuddy(m_icon);
0725 
0726     if (m_noteContent->url().isEmpty()) {
0727         m_url = new KUrlRequester(QUrl(QString()), wid);
0728         m_url->setMode(KFile::File | KFile::ExistingOnly);
0729     } else {
0730         m_url = new KUrlRequester(m_noteContent->url().toDisplayString(), wid);
0731         m_url->setMode(KFile::File | KFile::ExistingOnly);
0732     }
0733 
0734     if (m_noteContent->title().isEmpty()) {
0735         m_title->setText(QString());
0736     } else {
0737         m_title->setText(m_noteContent->title());
0738     }
0739 
0740     QUrl filteredURL = NoteFactory::filteredURL(QUrl::fromUserInput(m_url->lineEdit()->text())); // KURIFilter::self()->filteredURI(KUrl(m_url->lineEdit()->text()));
0741     m_icon->setIconType(KIconLoader::NoGroup, KIconLoader::MimeType);
0742     m_icon->setIconSize(LinkLook::lookForURL(filteredURL)->iconSize());
0743     m_autoIcon = new QPushButton(i18n("Auto"), wid); // Create before to know size here:
0744     /* Icon button: */
0745     m_icon->setIcon(m_noteContent->icon());
0746     int minSize = m_autoIcon->sizeHint().height();
0747     // Make the icon button at least the same height than the other buttons for a better alignment (nicer to the eyes):
0748     if (m_icon->sizeHint().height() < minSize)
0749         m_icon->setFixedSize(minSize, minSize);
0750     else
0751         m_icon->setFixedSize(m_icon->sizeHint().height(), m_icon->sizeHint().height()); // Make it square
0752     /* Auto button: */
0753     m_autoIcon->setCheckable(true);
0754     m_autoIcon->setChecked(m_noteContent->autoIcon());
0755     hLay->addWidget(m_icon);
0756     hLay->addWidget(m_autoIcon);
0757     hLay->addStretch();
0758 
0759     m_url->lineEdit()->setMinimumWidth(m_url->lineEdit()->fontMetrics().maxWidth() * 20);
0760     m_title->setMinimumWidth(m_title->fontMetrics().maxWidth() * 20);
0761 
0762     // m_url->setShowLocalProtocol(true);
0763     QLabel *label1 = new QLabel(page);
0764     mainLayout->addWidget(label1);
0765     label1->setText(i18n("Ta&rget:"));
0766     label1->setBuddy(m_url);
0767 
0768     QLabel *label2 = new QLabel(page);
0769     mainLayout->addWidget(label2);
0770     label2->setText(i18n("&Title:"));
0771     label2->setBuddy(m_title);
0772 
0773     layout->addWidget(label1, 0, 0, Qt::AlignVCenter);
0774     layout->addWidget(label2, 1, 0, Qt::AlignVCenter);
0775     layout->addWidget(label3, 2, 0, Qt::AlignVCenter);
0776     layout->addWidget(m_url, 0, 1, Qt::AlignVCenter);
0777     layout->addWidget(wid1, 1, 1, Qt::AlignVCenter);
0778     layout->addWidget(wid, 2, 1, Qt::AlignVCenter);
0779 
0780     m_isAutoModified = false;
0781     connect(m_url, &KUrlRequester::textChanged, this, &LinkEditDialog::urlChanged);
0782     connect(m_title, &QLineEdit::textChanged, this, &LinkEditDialog::doNotAutoTitle);
0783     connect(m_icon, &KIconButton::iconChanged, this, &LinkEditDialog::doNotAutoIcon);
0784     connect(m_autoTitle, &QPushButton::clicked, this, [this](bool) { guessTitle(); });
0785     connect(m_autoIcon, &QPushButton::clicked, this, [this](bool) { guessIcon(); });
0786 
0787     QWidget *stretchWidget = new QWidget(page);
0788     mainLayout->addWidget(stretchWidget);
0789     QSizePolicy policy(QSizePolicy::Fixed, QSizePolicy::Expanding);
0790     policy.setHorizontalStretch(1);
0791     policy.setVerticalStretch(255);
0792     stretchWidget->setSizePolicy(policy); // Make it fill ALL vertical space
0793     layout->addWidget(stretchWidget, 3, 1, Qt::AlignVCenter);
0794 
0795     QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
0796     QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
0797     okButton->setDefault(true);
0798     okButton->setShortcut(Qt::CTRL | Qt::Key_Return);
0799     connect(okButton, SIGNAL(clicked()), SLOT(slotOk()));
0800     connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
0801     connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
0802     mainLayout->addWidget(buttonBox);
0803 
0804     // urlChanged(QString());
0805 
0806     //  if (ke)
0807     //      qApp->postEvent(m_url->lineEdit(), ke);
0808 }
0809 
0810 LinkEditDialog::~LinkEditDialog()
0811 {
0812 }
0813 
0814 void LinkEditDialog::ensurePolished()
0815 {
0816     QDialog::ensurePolished();
0817     if (m_url->lineEdit()->text().isEmpty()) {
0818         m_url->setFocus();
0819         m_url->lineEdit()->end(false);
0820     } else {
0821         m_title->setFocus();
0822         m_title->end(false);
0823     }
0824 }
0825 
0826 void LinkEditDialog::urlChanged(const QString &)
0827 {
0828     m_isAutoModified = true;
0829     //  guessTitle();
0830     //  guessIcon();
0831     // Optimization (filter only once):
0832     QUrl filteredURL = NoteFactory::filteredURL(m_url->url()); // KURIFilter::self()->filteredURI(KUrl(m_url->url()));
0833     if (m_autoIcon->isChecked())
0834         m_icon->setIcon(NoteFactory::iconForURL(filteredURL));
0835     if (m_autoTitle->isChecked()) {
0836         m_title->setText(NoteFactory::titleForURL(filteredURL));
0837         m_autoTitle->setChecked(true); // Because the setText() will disable it!
0838     }
0839 }
0840 
0841 void LinkEditDialog::doNotAutoTitle(const QString &)
0842 {
0843     if (m_isAutoModified)
0844         m_isAutoModified = false;
0845     else
0846         m_autoTitle->setChecked(false);
0847 }
0848 
0849 void LinkEditDialog::doNotAutoIcon(QString)
0850 {
0851     m_autoIcon->setChecked(false);
0852 }
0853 
0854 void LinkEditDialog::guessIcon()
0855 {
0856     if (m_autoIcon->isChecked()) {
0857         QUrl filteredURL = NoteFactory::filteredURL(m_url->url()); // KURIFilter::self()->filteredURI(KUrl(m_url->url()));
0858         m_icon->setIcon(NoteFactory::iconForURL(filteredURL));
0859     }
0860 }
0861 
0862 void LinkEditDialog::guessTitle()
0863 {
0864     if (m_autoTitle->isChecked()) {
0865         QUrl filteredURL = NoteFactory::filteredURL(m_url->url()); // KURIFilter::self()->filteredURI(KUrl(m_url->url()));
0866         m_title->setText(NoteFactory::titleForURL(filteredURL));
0867         m_autoTitle->setChecked(true); // Because the setText() will disable it!
0868     }
0869 }
0870 
0871 void LinkEditDialog::slotOk()
0872 {
0873     QUrl filteredURL = NoteFactory::filteredURL(m_url->url()); // KURIFilter::self()->filteredURI(KUrl(m_url->url()));
0874     m_noteContent->setLink(filteredURL, m_title->text(), m_icon->icon(), m_autoTitle->isChecked(), m_autoIcon->isChecked());
0875     m_noteContent->setEdited();
0876 
0877     /* Change icon size if link look have changed */
0878     LinkLook *linkLook = LinkLook::lookForURL(filteredURL);
0879     QString icon = m_icon->icon();                                     // When we change size, icon isn't changed and keep it's old size
0880     m_icon->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); // Reset size policy
0881     m_icon->setIconSize(linkLook->iconSize());                         //  So I store it's name and reload it after size change !
0882     m_icon->setIcon(icon);
0883     int minSize = m_autoIcon->sizeHint().height();
0884     // Make the icon button at least the same height than the other buttons for a better alignment (nicer to the eyes):
0885     if (m_icon->sizeHint().height() < minSize)
0886         m_icon->setFixedSize(minSize, minSize);
0887     else
0888         m_icon->setFixedSize(m_icon->sizeHint().height(), m_icon->sizeHint().height()); // Make it square
0889 }
0890 
0891 /** class CrossReferenceEditDialog: */
0892 
0893 CrossReferenceEditDialog::CrossReferenceEditDialog(CrossReferenceContent *contentNote, QWidget *parent /*, QKeyEvent *ke*/)
0894     : QDialog(parent)
0895     , m_noteContent(contentNote)
0896 {
0897     QVBoxLayout *mainLayout = new QVBoxLayout;
0898     setLayout(mainLayout);
0899 
0900     // QDialog options
0901     setWindowTitle(i18n("Edit Cross Reference"));
0902 
0903     QWidget *page = new QWidget(this);
0904     mainLayout->addWidget(page);
0905     QWidget *wid = new QWidget(page);
0906     mainLayout->addWidget(wid);
0907 
0908     QGridLayout *layout = new QGridLayout(page);
0909     mainLayout->addLayout(layout);
0910 
0911     m_targetBasket = new KComboBox(wid);
0912     this->generateBasketList(m_targetBasket);
0913 
0914     if (m_noteContent->url().isEmpty()) {
0915         BasketListViewItem *item = Global::bnpView->topLevelItem(0);
0916         m_noteContent->setCrossReference(QUrl::fromUserInput(item->data(0, Qt::UserRole).toString()), m_targetBasket->currentText(), "edit-copy");
0917         this->urlChanged(0);
0918     } else {
0919         QString url = m_noteContent->url().url();
0920         // cannot use findData because I'm using a StringList and I don't have the second
0921         // piece of data to make find work.
0922         for (int i = 0; i < m_targetBasket->count(); ++i) {
0923             if (url == m_targetBasket->itemData(i, Qt::UserRole).toStringList().first()) {
0924                 m_targetBasket->setCurrentIndex(i);
0925                 break;
0926             }
0927         }
0928     }
0929 
0930     QLabel *label1 = new QLabel(page);
0931     mainLayout->addWidget(label1);
0932     label1->setText(i18n("Ta&rget:"));
0933     label1->setBuddy(m_targetBasket);
0934 
0935     layout->addWidget(label1, 0, 0, Qt::AlignVCenter);
0936     layout->addWidget(m_targetBasket, 0, 1, Qt::AlignVCenter);
0937 
0938     connect(m_targetBasket, SIGNAL(activated(int)), this, SLOT(urlChanged(int)));
0939 
0940     QWidget *stretchWidget = new QWidget(page);
0941     mainLayout->addWidget(stretchWidget);
0942     QSizePolicy policy(QSizePolicy::Fixed, QSizePolicy::Expanding);
0943     policy.setHorizontalStretch(1);
0944     policy.setVerticalStretch(255);
0945     stretchWidget->setSizePolicy(policy); // Make it fill ALL vertical space
0946     layout->addWidget(stretchWidget, 3, 1, Qt::AlignVCenter);
0947 
0948     QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
0949     QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
0950     okButton->setDefault(true);
0951     okButton->setShortcut(Qt::CTRL | Qt::Key_Return);
0952     connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
0953     connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
0954     mainLayout->addWidget(buttonBox);
0955     setObjectName("EditCrossReference");
0956     setModal(true);
0957     connect(okButton, SIGNAL(clicked()), SLOT(slotOk()));
0958 }
0959 
0960 CrossReferenceEditDialog::~CrossReferenceEditDialog()
0961 {
0962 }
0963 
0964 void CrossReferenceEditDialog::urlChanged(const int index)
0965 {
0966     if (m_targetBasket)
0967         m_noteContent->setCrossReference(
0968             QUrl::fromUserInput(m_targetBasket->itemData(index, Qt::UserRole).toStringList().first()), m_targetBasket->currentText().trimmed(), m_targetBasket->itemData(index, Qt::UserRole).toStringList().last());
0969 }
0970 
0971 void CrossReferenceEditDialog::slotOk()
0972 {
0973     m_noteContent->setEdited();
0974 }
0975 
0976 void CrossReferenceEditDialog::generateBasketList(KComboBox *targetList, BasketListViewItem *item, int indent)
0977 {
0978     if (!item) { // include ALL top level items and their children.
0979         for (int i = 0; i < Global::bnpView->topLevelItemCount(); ++i)
0980             this->generateBasketList(targetList, Global::bnpView->topLevelItem(i));
0981     } else {
0982         BasketScene *bv = item->basket();
0983 
0984         // TODO: add some fancy deco stuff to make it look like a tree list.
0985         QString pad;
0986         QString text = item->text(0); // user text
0987 
0988         text.prepend(pad.fill(' ', indent * 2));
0989 
0990         // create the link text
0991         QString link = "basket://";
0992         link.append(bv->folderName().toLower()); // unique ref.
0993         QStringList data;
0994         data.append(link);
0995         data.append(bv->icon());
0996 
0997         targetList->addItem(item->icon(0), text, QVariant(data));
0998 
0999         int subBasketCount = item->childCount();
1000         if (subBasketCount > 0) {
1001             indent++;
1002             for (int i = 0; i < subBasketCount; ++i) {
1003                 this->generateBasketList(targetList, (BasketListViewItem *)item->child(i), indent);
1004             }
1005         }
1006     }
1007 }
1008 
1009 /** class LauncherEditDialog: */
1010 
1011 LauncherEditDialog::LauncherEditDialog(LauncherContent *contentNote, QWidget *parent)
1012     : QDialog(parent)
1013     , m_noteContent(contentNote)
1014 {
1015     QVBoxLayout *mainLayout = new QVBoxLayout;
1016     setLayout(mainLayout);
1017 
1018     // QDialog options
1019     setWindowTitle(i18n("Edit Launcher Note"));
1020 
1021     setObjectName("EditLauncher");
1022     setModal(true);
1023 
1024     QWidget *page = new QWidget(this);
1025     mainLayout->addWidget(page);
1026 
1027     // QGridLayout *layout = new QGridLayout(page, /*nRows=*/4, /*nCols=*/2, /*margin=*/0, spacingHint());
1028     QGridLayout *layout = new QGridLayout(page);
1029     mainLayout->addLayout(layout);
1030 
1031     KService service(contentNote->fullPath());
1032 
1033     m_command = new RunCommandRequester(service.exec(), i18n("Choose a command to run:"), page);
1034     mainLayout->addWidget(m_command);
1035     m_name = new QLineEdit(service.name(), page);
1036     mainLayout->addWidget(m_name);
1037 
1038     QWidget *wid = new QWidget(page);
1039     mainLayout->addWidget(wid);
1040     QHBoxLayout *hLay = new QHBoxLayout(wid);
1041     m_icon = new KIconButton(wid);
1042 
1043     QLabel *label = new QLabel(page);
1044     mainLayout->addWidget(label);
1045     label->setText(i18n("&Icon:"));
1046     label->setBuddy(m_icon);
1047 
1048     m_icon->setIconType(KIconLoader::NoGroup, KIconLoader::Application);
1049     m_icon->setIconSize(LinkLook::launcherLook->iconSize());
1050     QPushButton *guessButton = new QPushButton(i18n("&Guess"), wid);
1051     /* Icon button: */
1052     m_icon->setIcon(service.icon());
1053     int minSize = guessButton->sizeHint().height();
1054     // Make the icon button at least the same height than the other buttons for a better alignment (nicer to the eyes):
1055     if (m_icon->sizeHint().height() < minSize)
1056         m_icon->setFixedSize(minSize, minSize);
1057     else
1058         m_icon->setFixedSize(m_icon->sizeHint().height(), m_icon->sizeHint().height()); // Make it square
1059     /* Guess button: */
1060     hLay->addWidget(m_icon);
1061     hLay->addWidget(guessButton);
1062     hLay->addStretch();
1063 
1064     m_command->lineEdit()->setMinimumWidth(m_command->lineEdit()->fontMetrics().maxWidth() * 20);
1065 
1066     QLabel *label1 = new QLabel(page);
1067     mainLayout->addWidget(label1);
1068     label1->setText(i18n("Comman&d:"));
1069     label1->setBuddy(m_command->lineEdit());
1070 
1071     QLabel *label2 = new QLabel(page);
1072     mainLayout->addWidget(label2);
1073     label2->setText(i18n("&Name:"));
1074     label2->setBuddy(m_name);
1075 
1076     layout->addWidget(label1, 0, 0, Qt::AlignVCenter);
1077     layout->addWidget(label2, 1, 0, Qt::AlignVCenter);
1078     layout->addWidget(label, 2, 0, Qt::AlignVCenter);
1079     layout->addWidget(m_command, 0, 1, Qt::AlignVCenter);
1080     layout->addWidget(m_name, 1, 1, Qt::AlignVCenter);
1081     layout->addWidget(wid, 2, 1, Qt::AlignVCenter);
1082 
1083     QWidget *stretchWidget = new QWidget(page);
1084     mainLayout->addWidget(stretchWidget);
1085 
1086     QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
1087     QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
1088     okButton->setDefault(true);
1089     okButton->setShortcut(Qt::CTRL | Qt::Key_Return);
1090     connect(okButton, SIGNAL(clicked()), SLOT(slotOk()));
1091     connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
1092     connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
1093     mainLayout->addWidget(buttonBox);
1094 
1095     QSizePolicy policy(QSizePolicy::Fixed, QSizePolicy::Expanding);
1096     policy.setHorizontalStretch(1);
1097     policy.setVerticalStretch(255);
1098     stretchWidget->setSizePolicy(policy); // Make it fill ALL vertical space
1099 
1100     layout->addWidget(stretchWidget, 3, 1, Qt::AlignVCenter);
1101 
1102     connect(guessButton, SIGNAL(clicked()), this, SLOT(guessIcon()));
1103 }
1104 
1105 LauncherEditDialog::~LauncherEditDialog()
1106 {
1107 }
1108 
1109 void LauncherEditDialog::ensurePolished()
1110 {
1111     QDialog::ensurePolished();
1112     if (m_command->runCommand().isEmpty()) {
1113         m_command->lineEdit()->setFocus();
1114         m_command->lineEdit()->end(false);
1115     } else {
1116         m_name->setFocus();
1117         m_name->end(false);
1118     }
1119 }
1120 
1121 void LauncherEditDialog::slotOk()
1122 {
1123     // TODO: Remember if a string has been modified AND IS DIFFERENT FROM THE
1124     // ORIGINAL!
1125 
1126     KDesktopFile dtFile(m_noteContent->fullPath());
1127     KConfigGroup grp = dtFile.desktopGroup();
1128     grp.writeEntry("Exec", m_command->runCommand());
1129     grp.writeEntry("Name", m_name->text());
1130     grp.writeEntry("Icon", m_icon->icon());
1131 
1132     // Just for faster feedback: conf object will save to disk (and then
1133     // m_note->loadContent() called)
1134     m_noteContent->setLauncher(m_name->text(), m_icon->icon(), m_command->runCommand());
1135     m_noteContent->setEdited();
1136 }
1137 
1138 void LauncherEditDialog::guessIcon()
1139 {
1140     m_icon->setIcon(NoteFactory::iconForCommand(m_command->runCommand()));
1141 }
1142 
1143 /** class InlineEditors: */
1144 
1145 InlineEditors::InlineEditors()
1146 {
1147 }
1148 
1149 InlineEditors::~InlineEditors()
1150 {
1151 }
1152 
1153 InlineEditors *InlineEditors::instance()
1154 {
1155     static InlineEditors *instance = nullptr;
1156     if (!instance)
1157         instance = new InlineEditors();
1158     return instance;
1159 }
1160 
1161 void InlineEditors::initToolBars(KActionCollection *ac)
1162 {
1163     QFont defaultFont;
1164     QColor textColor = (Global::bnpView && Global::bnpView->currentBasket() ? Global::bnpView->currentBasket()->textColor() : palette().color(QPalette::Text));
1165 
1166     // NOTE: currently it is NULL since initToolBars is called early. Could use different way to get MainWindow pointer from main
1167     KMainWindow *parent = Global::activeMainWindow();
1168 
1169     // Init the RichTextEditor Toolbar:
1170     richTextFont = new QFontComboBox(Global::activeMainWindow());
1171     focusWidgetFilter = new FocusWidgetFilter(richTextFont);
1172     richTextFont->setFixedWidth(richTextFont->sizeHint().width() * 2 / 3);
1173     richTextFont->setCurrentFont(defaultFont.family());
1174 
1175     QWidgetAction *action = new QWidgetAction(parent);
1176     ac->addAction("richtext_font", action);
1177     action->setDefaultWidget(richTextFont);
1178     action->setText(i18n("Font"));
1179     ac->setDefaultShortcut(action, Qt::Key_F6);
1180 
1181     richTextFontSize = new FontSizeCombo(/*rw=*/true, Global::activeMainWindow());
1182     richTextFontSize->setFontSize(defaultFont.pointSize());
1183     action = new QWidgetAction(parent);
1184     ac->addAction("richtext_font_size", action);
1185     action->setDefaultWidget(richTextFontSize);
1186     action->setText(i18n("Font Size"));
1187     ac->setDefaultShortcut(action, Qt::Key_F7);
1188 
1189     richTextColor = new KColorCombo(Global::activeMainWindow());
1190     richTextColor->installEventFilter(focusWidgetFilter);
1191     richTextColor->setFixedWidth(richTextColor->sizeHint().height() * 2);
1192     richTextColor->setColor(textColor);
1193     action = new QWidgetAction(parent);
1194     ac->addAction("richtext_color", action);
1195     action->setDefaultWidget(richTextColor);
1196     action->setText(i18n("Color"));
1197 
1198     KToggleAction *ta = nullptr;
1199     ta = new KToggleAction(ac);
1200     ac->addAction("richtext_bold", ta);
1201     ta->setText(i18n("Bold"));
1202     ta->setIcon(QIcon::fromTheme("format-text-bold"));
1203     ac->setDefaultShortcut(ta, QKeySequence("Ctrl+B"));
1204     richTextBold = ta;
1205 
1206     ta = new KToggleAction(ac);
1207     ac->addAction("richtext_italic", ta);
1208     ta->setText(i18n("Italic"));
1209     ta->setIcon(QIcon::fromTheme("format-text-italic"));
1210     ac->setDefaultShortcut(ta, QKeySequence("Ctrl+I"));
1211     richTextItalic = ta;
1212 
1213     ta = new KToggleAction(ac);
1214     ac->addAction("richtext_underline", ta);
1215     ta->setText(i18n("Underline"));
1216     ta->setIcon(QIcon::fromTheme("format-text-underline"));
1217     ac->setDefaultShortcut(ta, QKeySequence("Ctrl+U"));
1218     richTextUnderline = ta;
1219 
1220 #if 0
1221     ta = new KToggleAction(ac);
1222     ac->addAction("richtext_super", ta);
1223     ta->setText(i18n("Superscript"));
1224     ta->setIcon(QIcon::fromTheme("text_super"));
1225     richTextSuper = ta;
1226 
1227     ta = new KToggleAction(ac);
1228     ac->addAction("richtext_sub", ta);
1229     ta->setText(i18n("Subscript"));
1230     ta->setIcon(QIcon::fromTheme("text_sub"));
1231     richTextSub = ta;
1232 #endif
1233 
1234     ta = new KToggleAction(ac);
1235     ac->addAction("richtext_left", ta);
1236     ta->setText(i18n("Align Left"));
1237     ta->setIcon(QIcon::fromTheme("format-justify-left"));
1238     richTextLeft = ta;
1239 
1240     ta = new KToggleAction(ac);
1241     ac->addAction("richtext_center", ta);
1242     ta->setText(i18n("Centered"));
1243     ta->setIcon(QIcon::fromTheme("format-justify-center"));
1244     richTextCenter = ta;
1245 
1246     ta = new KToggleAction(ac);
1247     ac->addAction("richtext_right", ta);
1248     ta->setText(i18n("Align Right"));
1249     ta->setIcon(QIcon::fromTheme("format-justify-right"));
1250     richTextRight = ta;
1251 
1252     ta = new KToggleAction(ac);
1253     ac->addAction("richtext_block", ta);
1254     ta->setText(i18n("Justified"));
1255     ta->setIcon(QIcon::fromTheme("format-justify-fill"));
1256     richTextJustified = ta;
1257 
1258     QActionGroup *alignmentGroup = new QActionGroup(this);
1259     alignmentGroup->addAction(richTextLeft);
1260     alignmentGroup->addAction(richTextCenter);
1261     alignmentGroup->addAction(richTextRight);
1262     alignmentGroup->addAction(richTextJustified);
1263 
1264     ta = new KToggleAction(ac);
1265     ac->addAction("richtext_undo", ta);
1266     ta->setText(i18n("Undo"));
1267     ta->setIcon(QIcon::fromTheme("edit-undo"));
1268     richTextUndo = ta;
1269 
1270     ta = new KToggleAction(ac);
1271     ac->addAction("richtext_redo", ta);
1272     ta->setText(i18n("Redo"));
1273     ta->setIcon(QIcon::fromTheme("edit-redo"));
1274     richTextRedo = ta;
1275 
1276     disableRichTextToolBar();
1277 }
1278 
1279 KToolBar *InlineEditors::richTextToolBar()
1280 {
1281     if (Global::activeMainWindow()) {
1282         Global::activeMainWindow()->toolBar(); // Make sure we create the main toolbar FIRST, so it will be on top of the edit toolbar!
1283         return Global::activeMainWindow()->toolBar("richTextEditToolBar");
1284     } else
1285         return nullptr;
1286 }
1287 
1288 void InlineEditors::enableRichTextToolBar()
1289 {
1290     richTextFont->setEnabled(true);
1291     richTextFontSize->setEnabled(true);
1292     richTextColor->setEnabled(true);
1293     richTextBold->setEnabled(true);
1294     richTextItalic->setEnabled(true);
1295     richTextUnderline->setEnabled(true);
1296     richTextLeft->setEnabled(true);
1297     richTextCenter->setEnabled(true);
1298     richTextRight->setEnabled(true);
1299     richTextJustified->setEnabled(true);
1300     richTextUndo->setEnabled(true);
1301     richTextRedo->setEnabled(true);
1302 }
1303 
1304 void InlineEditors::disableRichTextToolBar()
1305 {
1306     disconnect(richTextFont);
1307     disconnect(richTextFontSize);
1308     disconnect(richTextColor);
1309     disconnect(richTextBold);
1310     disconnect(richTextItalic);
1311     disconnect(richTextUnderline);
1312     disconnect(richTextLeft);
1313     disconnect(richTextCenter);
1314     disconnect(richTextRight);
1315     disconnect(richTextJustified);
1316     disconnect(richTextUndo);
1317     disconnect(richTextRedo);
1318 
1319     richTextFont->setEnabled(false);
1320     richTextFontSize->setEnabled(false);
1321     richTextColor->setEnabled(false);
1322     richTextBold->setEnabled(false);
1323     richTextItalic->setEnabled(false);
1324     richTextUnderline->setEnabled(false);
1325     richTextLeft->setEnabled(false);
1326     richTextCenter->setEnabled(false);
1327     richTextRight->setEnabled(false);
1328     richTextJustified->setEnabled(false);
1329     richTextUndo->setEnabled(false);
1330     richTextRedo->setEnabled(false);
1331 
1332     // Return to a "proper" state:
1333     QFont defaultFont;
1334     QColor textColor = (Global::bnpView && Global::bnpView->currentBasket() ? Global::bnpView->currentBasket()->textColor() : palette().color(QPalette::Text));
1335     richTextFont->setCurrentFont(defaultFont.family());
1336     richTextFontSize->setFontSize(defaultFont.pointSize());
1337     richTextColor->setColor(textColor);
1338     richTextBold->setChecked(false);
1339     richTextItalic->setChecked(false);
1340     richTextUnderline->setChecked(false);
1341     richTextLeft->setChecked(false);
1342     richTextCenter->setChecked(false);
1343     richTextRight->setChecked(false);
1344     richTextJustified->setChecked(false);
1345 }
1346 
1347 QPalette InlineEditors::palette() const
1348 {
1349     return qApp->palette();
1350 }