1 /*******************************************************************
2 KNotes -- Notes for the KDE project
4 Copyright (c) 1997-2013, The KNotes Developers
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 *******************************************************************/
21 #include "knoteedit.h"
22 #include "notes/knote.h"
23 #include "NoteShared/NoteEditorUtils"
24 #include "KPIMTextEdit/EditorUtil"
25 #include "PimCommon/KActionMenuChangeCase"
26 #include "knotesglobalconfig.h"
30 #include <kactioncollection.h>
31 #include <QColorDialog>
32 #include "knotes_debug.h"
33 #include <kfontaction.h>
34 #include <kfontsizeaction.h>
35 #include <KLocalizedString>
37 #include <kstandardaction.h>
38 #include <ktoggleaction.h>
44 static const short ICON_SIZE
= 10;
46 KNoteEdit::KNoteEdit(KActionCollection
*actions
, QWidget
*parent
)
52 setWordWrapMode(QTextOption::WordWrap
);
53 setLineWrapMode(WidgetWidth
);
54 if (acceptRichText()) {
55 setAutoFormatting(AutoAll
);
57 setAutoFormatting(AutoNone
);
60 // create the actions modifying the text format
61 m_textBold
= new KToggleAction(QIcon::fromTheme(QStringLiteral("format-text-bold")), i18n("Bold"),
63 actions
->addAction(QStringLiteral("format_bold"), m_textBold
);
64 actions
->setDefaultShortcut(m_textBold
, QKeySequence(Qt::CTRL
+ Qt::Key_B
));
65 m_textItalic
= new KToggleAction(QIcon::fromTheme(QStringLiteral("format-text-italic")),
66 i18n("Italic"), this);
67 actions
->addAction(QStringLiteral("format_italic"), m_textItalic
);
68 actions
->setDefaultShortcut(m_textItalic
, QKeySequence(Qt::CTRL
+ Qt::Key_I
));
69 m_textUnderline
= new KToggleAction(QIcon::fromTheme(QStringLiteral("format-text-underline")),
70 i18n("Underline"), this);
71 actions
->addAction(QStringLiteral("format_underline"), m_textUnderline
);
72 actions
->setDefaultShortcut(m_textUnderline
, QKeySequence(Qt::CTRL
+ Qt::Key_U
));
73 m_textStrikeOut
= new KToggleAction(QIcon::fromTheme(QStringLiteral("format-text-strikethrough")),
74 i18n("Strike Out"), this);
75 actions
->addAction(QStringLiteral("format_strikeout"), m_textStrikeOut
);
76 actions
->setDefaultShortcut(m_textStrikeOut
, QKeySequence(Qt::CTRL
+ Qt::Key_S
));
78 connect(m_textBold
, &KToggleAction::toggled
, this, &KNoteEdit::textBold
);
79 connect(m_textItalic
, &KToggleAction::toggled
, this, &KNoteEdit::setFontItalic
);
80 connect(m_textUnderline
, &KToggleAction::toggled
, this, &KNoteEdit::setFontUnderline
);
81 connect(m_textStrikeOut
, &KToggleAction::toggled
, this, &KNoteEdit::textStrikeOut
);
83 m_textAlignLeft
= new KToggleAction(QIcon::fromTheme(QStringLiteral("format-justify-left")),
84 i18n("Align Left"), this);
85 actions
->addAction(QStringLiteral("format_alignleft"), m_textAlignLeft
);
86 connect(m_textAlignLeft
, &KToggleAction::triggered
, this, &KNoteEdit::textAlignLeft
);
87 actions
->setDefaultShortcut(m_textAlignLeft
, QKeySequence(Qt::ALT
+ Qt::Key_L
));
88 m_textAlignLeft
->setChecked(true); // just a dummy, will be updated later
89 m_textAlignCenter
= new KToggleAction(QIcon::fromTheme(QStringLiteral("format-justify-center")),
90 i18n("Align Center"), this);
91 actions
->addAction(QStringLiteral("format_aligncenter"), m_textAlignCenter
);
92 connect(m_textAlignCenter
, &KToggleAction::triggered
, this, &KNoteEdit::textAlignCenter
);
93 actions
->setDefaultShortcut(m_textAlignCenter
, QKeySequence(Qt::ALT
+ Qt::Key_C
));
94 m_textAlignRight
= new KToggleAction(QIcon::fromTheme(QStringLiteral("format-justify-right")),
95 i18n("Align Right"), this);
96 actions
->addAction(QStringLiteral("format_alignright"), m_textAlignRight
);
97 connect(m_textAlignRight
, &KToggleAction::triggered
, this, &KNoteEdit::textAlignRight
);
98 actions
->setDefaultShortcut(m_textAlignRight
, QKeySequence(Qt::ALT
+ Qt::Key_R
));
99 m_textAlignBlock
= new KToggleAction(QIcon::fromTheme(QStringLiteral("format-justify-fill")),
100 i18n("Align Block"), this);
101 actions
->addAction(QStringLiteral("format_alignblock"), m_textAlignBlock
);
102 connect(m_textAlignBlock
, &KToggleAction::triggered
, this, &KNoteEdit::textAlignBlock
);
103 actions
->setDefaultShortcut(m_textAlignBlock
, QKeySequence(Qt::ALT
+ Qt::Key_B
));
105 QActionGroup
*group
= new QActionGroup(this);
106 group
->addAction(m_textAlignLeft
);
107 group
->addAction(m_textAlignCenter
);
108 group
->addAction(m_textAlignRight
);
109 group
->addAction(m_textAlignBlock
);
111 m_textList
= new KToggleAction(QIcon::fromTheme(QStringLiteral("format-list-ordered")), i18n("List"), this);
112 actions
->addAction(QStringLiteral("format_list"), m_textList
);
113 connect(m_textList
, &KToggleAction::triggered
, this, &KNoteEdit::textList
);
115 m_textSuper
= new KToggleAction(QIcon::fromTheme(QStringLiteral("format-text-superscript")),
116 i18n("Superscript"), this);
117 actions
->addAction(QStringLiteral("format_super"), m_textSuper
);
118 connect(m_textSuper
, &KToggleAction::triggered
, this, &KNoteEdit::textSuperScript
);
119 m_textSub
= new KToggleAction(QIcon::fromTheme(QStringLiteral("format-text-subscript")), i18n("Subscript"),
121 actions
->addAction(QStringLiteral("format_sub"), m_textSub
);
122 connect(m_textSub
, &KToggleAction::triggered
, this, &KNoteEdit::textSubScript
);
124 m_textIncreaseIndent
= new QAction(QIcon::fromTheme(QStringLiteral("format-indent-more")),
125 i18n("Increase Indent"), this);
126 actions
->addAction(QStringLiteral("format_increaseindent"), m_textIncreaseIndent
);
127 actions
->setDefaultShortcut(m_textIncreaseIndent
, QKeySequence(Qt::CTRL
+ Qt::ALT
+
129 connect(m_textIncreaseIndent
, &QAction::triggered
, this, &KNoteEdit::textIncreaseIndent
);
131 m_textDecreaseIndent
= new QAction(QIcon::fromTheme(QStringLiteral("format-indent-less")),
132 i18n("Decrease Indent"), this);
133 actions
->addAction(QStringLiteral("format_decreaseindent"), m_textDecreaseIndent
);
134 actions
->setDefaultShortcut(m_textDecreaseIndent
, QKeySequence(Qt::CTRL
+ Qt::ALT
+
136 connect(m_textDecreaseIndent
, &QAction::triggered
, this, &KNoteEdit::textDecreaseIndent
);
138 group
= new QActionGroup(this);
139 group
->addAction(m_textIncreaseIndent
);
140 group
->addAction(m_textDecreaseIndent
);
142 QPixmap
pix(ICON_SIZE
, ICON_SIZE
);
143 pix
.fill(Qt::black
); // just a dummy, gets updated before widget is shown
144 m_textColor
= new QAction(i18n("Text Color..."), this);
145 actions
->addAction(QStringLiteral("format_color"), m_textColor
);
146 m_textColor
->setIcon(pix
);
147 connect(m_textColor
, &QAction::triggered
, this, &KNoteEdit::slotTextColor
);
149 QAction
*act
= new QAction(QIcon::fromTheme(QStringLiteral("format-fill-color")), i18n("Text Background Color..."), this);
150 actions
->addAction(QStringLiteral("text_background_color"), act
);
151 connect(act
, &QAction::triggered
, this, &KNoteEdit::slotTextBackgroundColor
);
153 m_textFont
= new KFontAction(i18n("Text Font"), this);
154 actions
->addAction(QStringLiteral("format_font"), m_textFont
);
155 connect(m_textFont
, static_cast<void (KFontAction::*)(const QString
&)>(&KFontAction::triggered
), this, &KNoteEdit::setFontFamily
);
157 m_textSize
= new KFontSizeAction(i18n("Text Size"), this);
158 actions
->addAction(QStringLiteral("format_size"), m_textSize
);
159 connect(m_textSize
, &KFontSizeAction::fontSizeChanged
, this, &KNoteEdit::setTextFontSize
);
161 mChangeCaseActionMenu
= new PimCommon::KActionMenuChangeCase(this);
162 mChangeCaseActionMenu
->appendInActionCollection(actions
);
163 connect(mChangeCaseActionMenu
, &PimCommon::KActionMenuChangeCase::upperCase
, this, &KNoteEdit::slotUpperCase
);
164 connect(mChangeCaseActionMenu
, &PimCommon::KActionMenuChangeCase::lowerCase
, this, &KNoteEdit::slotLowerCase
);
165 connect(mChangeCaseActionMenu
, &PimCommon::KActionMenuChangeCase::sentenceCase
, this, &KNoteEdit::slotSentenceCase
);
166 connect(mChangeCaseActionMenu
, &PimCommon::KActionMenuChangeCase::reverseCase
, this, &KNoteEdit::slotReverseCase
);
168 QAction
*action
= new QAction(QIcon::fromTheme(QStringLiteral("knotes_date")), i18n("Insert Date"), this);
169 actions
->addAction(QStringLiteral("insert_date"), action
);
170 connect(action
, &QAction::triggered
, this, &KNoteEdit::slotInsertDate
);
172 action
= new QAction(QIcon::fromTheme(QStringLiteral("checkmark")), i18n("Insert Checkmark"), this);
173 actions
->addAction(QStringLiteral("insert_checkmark"), action
);
174 connect(action
, &QAction::triggered
, this, &KNoteEdit::slotInsertCheckMark
);
176 // QTextEdit connections
177 connect(this, &KNoteEdit::currentCharFormatChanged
, this, &KNoteEdit::slotCurrentCharFormatChanged
);
178 connect(this, &KNoteEdit::cursorPositionChanged
, this, &KNoteEdit::slotCursorPositionChanged
);
179 slotCurrentCharFormatChanged(currentCharFormat());
180 slotCursorPositionChanged();
183 KNoteEdit::~KNoteEdit()
187 void KNoteEdit::setColor(const QColor
&fg
, const QColor
&bg
)
189 mDefaultBackgroundColor
= bg
;
190 mDefaultForegroundColor
= fg
;
192 QPalette p
= palette();
194 // better: from light(150) to light(100) to light(75)
195 // QLinearGradient g( width()/2, 0, width()/2, height() );
196 // g.setColorAt( 0, bg );
197 // g.setColorAt( 1, bg.dark(150) );
199 p
.setColor(QPalette::Window
, bg
);
200 // p.setBrush( QPalette::Window, g );
201 p
.setColor(QPalette::Base
, bg
);
202 // p.setBrush( QPalette::Base, g );
204 p
.setColor(QPalette::WindowText
, fg
);
205 p
.setColor(QPalette::Text
, fg
);
207 p
.setColor(QPalette::Button
, bg
.dark(116));
208 p
.setColor(QPalette::ButtonText
, fg
);
210 //p.setColor( QPalette::Highlight, bg );
211 //p.setColor( QPalette::HighlightedText, fg );
213 // order: Light, Midlight, Button, Mid, Dark, Shadow
216 p
.setColor(QPalette::Light
, bg
.light(180));
217 p
.setColor(QPalette::Midlight
, bg
.light(150));
218 p
.setColor(QPalette::Mid
, bg
.light(150));
219 p
.setColor(QPalette::Dark
, bg
.dark(108));
220 p
.setColor(QPalette::Shadow
, bg
.dark(116));
227 void KNoteEdit::setNote(KNote
*_note
)
232 void KNoteEdit::slotReverseCase()
234 QTextCursor cursor
= textCursor();
235 KPIMTextEdit::EditorUtil editorUtil
;
236 editorUtil
.reverseCase(cursor
);
239 void KNoteEdit::slotSentenceCase()
241 QTextCursor cursor
= textCursor();
242 KPIMTextEdit::EditorUtil editorUtil
;
243 editorUtil
.sentenceCase(cursor
);
246 void KNoteEdit::slotUpperCase()
248 QTextCursor cursor
= textCursor();
249 KPIMTextEdit::EditorUtil editorUtil
;
250 editorUtil
.upperCase(cursor
);
253 void KNoteEdit::slotLowerCase()
255 QTextCursor cursor
= textCursor();
256 KPIMTextEdit::EditorUtil editorUtil
;
257 editorUtil
.lowerCase(cursor
);
260 QMenu
*KNoteEdit::mousePopupMenu()
262 QMenu
*popup
= KTextEdit::mousePopupMenu();
264 QTextCursor cursor
= textCursor();
266 if (cursor
.hasSelection()) {
267 popup
->addSeparator();
268 popup
->addAction(mChangeCaseActionMenu
);
270 popup
->addSeparator();
271 QAction
*act
= m_actions
->action(QStringLiteral("insert_date"));
272 popup
->addAction(act
);
273 popup
->addSeparator();
274 act
= m_actions
->action(QStringLiteral("insert_checkmark"));
275 popup
->addAction(act
);
281 void KNoteEdit::setText(const QString
&text
)
283 if (acceptRichText() && Qt::mightBeRichText(text
)) {
290 QString
KNoteEdit::text() const
292 if (acceptRichText()) {
295 return toPlainText();
299 void KNoteEdit::setTextFont(const QFont
&font
)
301 setCurrentFont(font
);
303 // make this font default so that if user deletes note content
304 // font is remembered
305 document()->setDefaultFont(font
);
308 void KNoteEdit::setTextFontSize(int size
)
310 setFontPointSize(size
);
313 void KNoteEdit::setTabStop(int tabs
)
315 QFontMetrics
fm(font());
316 setTabStopWidth(fm
.width(QLatin1Char('x')) * tabs
);
319 void KNoteEdit::setAutoIndentMode(bool newmode
)
321 m_autoIndentMode
= newmode
;
326 void KNoteEdit::setRichText(bool f
)
328 if (f
== acceptRichText()) {
332 setAcceptRichText(f
);
335 setAutoFormatting(AutoAll
);
337 setAutoFormatting(AutoNone
);
340 const QString t
= toPlainText();
342 // if the note contains html source try to render it
343 if (Qt::mightBeRichText(t
)) {
349 enableRichTextActions(true);
352 enableRichTextActions(false);
356 void KNoteEdit::textBold(bool b
)
358 if (!acceptRichText()) {
363 f
.setFontWeight(b
? QFont::Bold
: QFont::Normal
);
364 mergeCurrentCharFormat(f
);
367 void KNoteEdit::textStrikeOut(bool s
)
369 if (!acceptRichText()) {
374 f
.setFontStrikeOut(s
);
375 mergeCurrentCharFormat(f
);
378 void KNoteEdit::slotTextColor()
380 if (!acceptRichText()) {
385 m_note
->setBlockSave(true);
387 QColor c
= QColorDialog::getColor(textColor(), this);
392 m_note
->setBlockSave(false);
396 void KNoteEdit::slotTextBackgroundColor()
398 if (!acceptRichText()) {
403 m_note
->setBlockSave(true);
405 QColor c
= QColorDialog::getColor(textBackgroundColor(), this);
407 setTextBackgroundColor(c
);
410 m_note
->setBlockSave(false);
414 void KNoteEdit::textAlignLeft()
416 if (!acceptRichText()) {
419 setAlignment(Qt::AlignLeft
);
420 m_textAlignLeft
->setChecked(true);
423 void KNoteEdit::textAlignCenter()
425 if (!acceptRichText()) {
428 setAlignment(Qt::AlignCenter
);
429 m_textAlignCenter
->setChecked(true);
432 void KNoteEdit::textAlignRight()
434 if (!acceptRichText()) {
437 setAlignment(Qt::AlignRight
);
438 m_textAlignRight
->setChecked(true);
441 void KNoteEdit::textAlignBlock()
443 if (!acceptRichText()) {
446 setAlignment(Qt::AlignJustify
);
447 m_textAlignBlock
->setChecked(true);
450 void KNoteEdit::textList()
452 if (!acceptRichText()) {
455 QTextCursor c
= textCursor();
458 if (m_textList
->isChecked()) {
460 QTextBlockFormat bf
= c
.blockFormat();
462 lf
.setIndent(bf
.indent() + 1);
465 lf
.setStyle(QTextListFormat::ListDisc
);
467 c
.setBlockFormat(bf
);
471 bf
.setObjectIndex(-1);
472 c
.setBlockFormat(bf
);
479 void KNoteEdit::textSuperScript()
481 if (!acceptRichText()) {
485 if (m_textSuper
->isChecked()) {
486 if (m_textSub
->isChecked()) {
487 m_textSub
->setChecked(false);
489 f
.setVerticalAlignment(QTextCharFormat::AlignSuperScript
);
491 f
.setVerticalAlignment(QTextCharFormat::AlignNormal
);
493 mergeCurrentCharFormat(f
);
496 void KNoteEdit::textSubScript()
498 if (!acceptRichText()) {
502 if (m_textSub
->isChecked()) {
503 if (m_textSuper
->isChecked()) {
504 m_textSuper
->setChecked(false);
506 f
.setVerticalAlignment(QTextCharFormat::AlignSubScript
);
508 f
.setVerticalAlignment(QTextCharFormat::AlignNormal
);
510 mergeCurrentCharFormat(f
);
513 void KNoteEdit::textIncreaseIndent()
515 if (!acceptRichText()) {
518 QTextBlockFormat f
= textCursor().blockFormat();
519 f
.setIndent(f
.indent() + 1);
520 textCursor().setBlockFormat(f
);
523 void KNoteEdit::textDecreaseIndent()
525 if (!acceptRichText()) {
528 QTextBlockFormat f
= textCursor().blockFormat();
529 short int curIndent
= f
.indent();
532 f
.setIndent(curIndent
- 1);
534 textCursor().setBlockFormat(f
);
537 /** protected methods **/
539 void KNoteEdit::keyPressEvent(QKeyEvent
*e
)
541 KTextEdit::keyPressEvent(e
);
543 if (m_autoIndentMode
&&
544 ((e
->key() == Qt::Key_Return
) || (e
->key() == Qt::Key_Enter
))) {
549 void KNoteEdit::focusInEvent(QFocusEvent
*e
)
551 KTextEdit::focusInEvent(e
);
553 setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded
);
554 setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded
);
557 void KNoteEdit::focusOutEvent(QFocusEvent
*e
)
559 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
560 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
562 KTextEdit::focusOutEvent(e
);
565 /** private slots **/
567 void KNoteEdit::slotCurrentCharFormatChanged(const QTextCharFormat
&f
)
569 if (!acceptRichText()) {
574 m_textFont
->setFont(f
.fontFamily());
575 m_textSize
->setFontSize((f
.fontPointSize() > 0) ? (int) f
.fontPointSize() : 10);
577 m_textBold
->setChecked(f
.font().bold());
578 m_textItalic
->setChecked(f
.fontItalic());
579 m_textUnderline
->setChecked(f
.fontUnderline());
580 m_textStrikeOut
->setChecked(f
.fontStrikeOut());
583 QPixmap
pix(ICON_SIZE
, ICON_SIZE
);
584 pix
.fill(f
.foreground().color());
585 m_textColor
->QAction::setIcon(pix
);
587 // vertical alignment changes
588 QTextCharFormat::VerticalAlignment va
= f
.verticalAlignment();
589 if (va
== QTextCharFormat::AlignNormal
) {
590 m_textSuper
->setChecked(false);
591 m_textSub
->setChecked(false);
592 } else if (va
== QTextCharFormat::AlignSuperScript
) {
593 m_textSuper
->setChecked(true);
594 } else if (va
== QTextCharFormat::AlignSubScript
) {
595 m_textSub
->setChecked(true);
599 void KNoteEdit::slotCursorPositionChanged()
601 if (!acceptRichText()) {
605 const Qt::Alignment a
= alignment();
606 if (a
& Qt::AlignLeft
) {
607 m_textAlignLeft
->setChecked(true);
608 } else if (a
& Qt::AlignHCenter
) {
609 m_textAlignCenter
->setChecked(true);
610 } else if (a
& Qt::AlignRight
) {
611 m_textAlignRight
->setChecked(true);
612 } else if (a
& Qt::AlignJustify
) {
613 m_textAlignBlock
->setChecked(true);
617 /** private methods **/
619 void KNoteEdit::autoIndent()
621 QTextCursor c
= textCursor();
622 QTextBlock b
= c
.block();
625 while ((b
.previous().length() > 0) && string
.trimmed().isEmpty()) {
630 if (string
.trimmed().isEmpty()) {
634 // This routine returns the whitespace before the first non white space
635 // character in string.
636 // It is assumed that string contains at least one non whitespace character
637 // ie \n \r \t \v \f and space
638 QString indentString
;
640 const int len
= string
.length();
642 while (i
< len
&& string
.at(i
).isSpace()) {
643 indentString
+= string
.at(i
++);
646 if (!indentString
.isEmpty()) {
647 c
.insertText(indentString
);
651 void KNoteEdit::enableRichTextActions(bool enabled
)
653 m_textColor
->setEnabled(enabled
);
654 m_textFont
->setEnabled(enabled
);
655 m_textSize
->setEnabled(enabled
);
657 m_textBold
->setEnabled(enabled
);
658 m_textItalic
->setEnabled(enabled
);
659 m_textUnderline
->setEnabled(enabled
);
660 m_textStrikeOut
->setEnabled(enabled
);
662 m_textAlignLeft
->setEnabled(enabled
);
663 m_textAlignCenter
->setEnabled(enabled
);
664 m_textAlignRight
->setEnabled(enabled
);
665 m_textAlignBlock
->setEnabled(enabled
);
667 m_textList
->setEnabled(enabled
);
668 m_textSuper
->setEnabled(enabled
);
669 m_textSub
->setEnabled(enabled
);
671 m_textIncreaseIndent
->setEnabled(enabled
);
672 m_textDecreaseIndent
->setEnabled(enabled
);
675 void KNoteEdit::slotInsertDate()
677 NoteShared::NoteEditorUtils noteEditorUtils
;
678 noteEditorUtils
.insertDate(this);
681 void KNoteEdit::slotInsertCheckMark()
683 QTextCursor cursor
= textCursor();
684 NoteShared::NoteEditorUtils noteEditorUtils
;
685 noteEditorUtils
.addCheckmark(cursor
);
688 void KNoteEdit::setCursorPositionFromStart(int pos
)
691 QTextCursor cursor
= textCursor();
692 //Fix html pos cursor
693 cursor
.setPosition(qMin(pos
, cursor
.document()->characterCount() - 1));
694 setTextCursor(cursor
);
695 ensureCursorVisible();
699 int KNoteEdit::cursorPositionFromStart() const
701 return textCursor().position();