1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2008 Stephen Kelly <steveire@gmail.com>
4 SPDX-FileCopyrightText: 2008 Thomas McGuire <thomas.mcguire@gmx.net>
5
6 SPDX-License-Identifier: LGPL-2.0-only
7*/
8
9#include "krichtextwidget.h"
10
11#include "krichtextedit_p.h"
12
13// KDE includes
14#include <KFontAction>
15#include <KFontSizeAction>
16#include <KLocalizedString>
17#include <ktoggleaction.h>
18
19// Qt includes
20#include <QAction>
21#include <QActionGroup>
22#include <QColorDialog>
23#include <QTextList>
24
25#include "klinkdialog_p.h"
26
27// TODO: Add i18n context
28
29class KRichTextWidgetPrivate : public KRichTextEditPrivate
30{
31 Q_DECLARE_PUBLIC(KRichTextWidget)
32
33public:
34 KRichTextWidgetPrivate(KRichTextWidget *qq)
35 : KRichTextEditPrivate(qq)
36 {
37 }
38
39 QList<QAction *> richTextActionList;
40 QTextCharFormat painterFormat;
41
42 KRichTextWidget::RichTextSupport richTextSupport;
43
44 bool painterActive = false;
45
46 bool richTextEnabled = false;
47 KToggleAction *enableRichText = nullptr;
48
49 QAction *action_text_foreground_color = nullptr;
50 QAction *action_text_background_color = nullptr;
51
52 KToggleAction *action_text_bold = nullptr;
53 KToggleAction *action_text_italic = nullptr;
54 KToggleAction *action_text_underline = nullptr;
55 KToggleAction *action_text_strikeout = nullptr;
56
57 KFontAction *action_font_family = nullptr;
58 KFontSizeAction *action_font_size = nullptr;
59
60 KSelectAction *action_list_style = nullptr;
61 QAction *action_list_indent = nullptr;
62 QAction *action_list_dedent = nullptr;
63
64 QAction *action_manage_link = nullptr;
65 QAction *action_insert_horizontal_rule = nullptr;
66 QAction *action_format_painter = nullptr;
67 QAction *action_to_plain_text = nullptr;
68
69 KToggleAction *action_align_left = nullptr;
70 KToggleAction *action_align_right = nullptr;
71 KToggleAction *action_align_center = nullptr;
72 KToggleAction *action_align_justify = nullptr;
73
74 KToggleAction *action_direction_ltr = nullptr;
75 KToggleAction *action_direction_rtl = nullptr;
76
77 KToggleAction *action_text_superscript = nullptr;
78 KToggleAction *action_text_subscript = nullptr;
79
80 KSelectAction *action_heading_level = nullptr;
81
82 //
83 // Normal functions
84 //
85 void init();
86
87 //
88 // Slots
89 //
90
91 /*
92 * Opens a dialog to allow the user to select a foreground color.
93 */
94 void _k_setTextForegroundColor();
95
96 /*
97 * Opens a dialog to allow the user to select a background color.
98 */
99 void _k_setTextBackgroundColor();
100
101 /*
102 * Opens a dialog which lets the user turn the currently selected text into
103 * a link.
104 * If no text is selected, the word under the cursor will be taken.
105 * If the cursor is already over a link, the user can edit that link.
106 *
107 */
108 void _k_manageLink();
109
110 /*
111 * Activates a format painter to allow the user to copy font/text formatting
112 * to different parts of the document.
113 *
114 */
115 void _k_formatPainter(bool active);
116
117 /*
118 * Update actions relating to text format (bold, size etc.).
119 */
120 void updateCharFormatActions(const QTextCharFormat &format);
121
122 /*
123 * Update actions not covered by text formatting, such as alignment,
124 * list style and level.
125 */
126 void updateMiscActions();
127
128 /*
129 * Change the style of the current list or create a new list with the style given by @a index.
130 */
131 void _k_setListStyle(int index);
132
133 /*
134 * Change the heading level of a current line to a level given by level
135 */
136 void _k_setHeadingLevel(int level);
137};
138
139void KRichTextWidgetPrivate::init()
140{
141 Q_Q(KRichTextWidget);
142
143 q->setRichTextSupport(KRichTextWidget::FullSupport);
144}
145
146KRichTextWidget::KRichTextWidget(QWidget *parent)
147 : KRichTextEdit(*new KRichTextWidgetPrivate(this), parent)
148{
149 Q_D(KRichTextWidget);
150
151 d->init();
152}
153
154KRichTextWidget::KRichTextWidget(const QString &text, QWidget *parent)
155 : KRichTextEdit(*new KRichTextWidgetPrivate(this), text, parent)
156{
157 Q_D(KRichTextWidget);
158
159 d->init();
160}
161
162KRichTextWidget::~KRichTextWidget() = default;
163
164KRichTextWidget::RichTextSupport KRichTextWidget::richTextSupport() const
165{
166 Q_D(const KRichTextWidget);
167
168 return d->richTextSupport;
169}
170
171void KRichTextWidget::setRichTextSupport(const KRichTextWidget::RichTextSupport &support)
172{
173 Q_D(KRichTextWidget);
174
175 d->richTextSupport = support;
176}
177
178QList<QAction *> KRichTextWidget::createActions()
179{
180 Q_D(KRichTextWidget);
181
182 // Note to maintainers: If adding new functionality here, make sure to disconnect
183 // and delete actions which should not be supported.
184 //
185 // New Actions need to be added to the following places:
186 // - possibly the RichTextSupportValues enum
187 // - the API documentation for createActions()
188 // - this function
189 // - the action needs to be added to the private class as a member
190 // - the constructor of the private class
191 // - depending on the action, some slot that changes the toggle state when
192 // appropriate, such as updateCharFormatActions or updateMiscActions.
193
194 // The list of actions currently supported is also stored internally.
195 // This is used to disable all actions at once in setActionsEnabled.
196 d->richTextActionList.clear();
197
198 if (d->richTextSupport & SupportTextForegroundColor) {
199 // Foreground Color
200 d->action_text_foreground_color = new QAction(QIcon::fromTheme(QStringLiteral("format-stroke-color")), i18nc("@action", "Text &Color…"), this);
201 d->action_text_foreground_color->setIconText(i18nc("@label stroke color", "Color"));
202 d->richTextActionList.append(t: (d->action_text_foreground_color));
203 d->action_text_foreground_color->setObjectName(QStringLiteral("format_text_foreground_color"));
204 connect(sender: d->action_text_foreground_color, signal: &QAction::triggered, context: this, slot: [this]() {
205 Q_D(KRichTextWidget);
206 d->_k_setTextForegroundColor();
207 });
208 } else {
209 d->action_text_foreground_color = nullptr;
210 }
211
212 if (d->richTextSupport & SupportTextBackgroundColor) {
213 // Background Color
214 d->action_text_background_color = new QAction(QIcon::fromTheme(QStringLiteral("format-fill-color")), i18nc("@action", "Text &Highlight…"), this);
215 d->richTextActionList.append(t: (d->action_text_background_color));
216 d->action_text_background_color->setObjectName(QStringLiteral("format_text_background_color"));
217 connect(sender: d->action_text_background_color, signal: &QAction::triggered, context: this, slot: [this]() {
218 Q_D(KRichTextWidget);
219 d->_k_setTextBackgroundColor();
220 });
221 } else {
222 d->action_text_background_color = nullptr;
223 }
224
225 if (d->richTextSupport & SupportFontFamily) {
226 // Font Family
227 d->action_font_family = new KFontAction(i18nc("@action", "&Font"), this);
228 d->richTextActionList.append(t: (d->action_font_family));
229 d->action_font_family->setObjectName(QStringLiteral("format_font_family"));
230 connect(sender: d->action_font_family, signal: &KSelectAction::textTriggered, context: this, slot: &KRichTextWidget::setFontFamily);
231 } else {
232 d->action_font_family = nullptr;
233 }
234
235 if (d->richTextSupport & SupportFontSize) {
236 // Font Size
237 d->action_font_size = new KFontSizeAction(i18nc("@action", "Font &Size"), this);
238 d->richTextActionList.append(t: (d->action_font_size));
239 d->action_font_size->setObjectName(QStringLiteral("format_font_size"));
240 connect(sender: d->action_font_size, signal: &KFontSizeAction::fontSizeChanged, context: this, slot: &KRichTextEdit::setFontSize);
241 } else {
242 d->action_font_size = nullptr;
243 }
244
245 if (d->richTextSupport & SupportBold) {
246 d->action_text_bold = new KToggleAction(QIcon::fromTheme(QStringLiteral("format-text-bold")), i18nc("@action boldify selected text", "&Bold"), this);
247 QFont bold;
248 bold.setBold(true);
249 d->action_text_bold->setFont(bold);
250 d->richTextActionList.append(t: (d->action_text_bold));
251 d->action_text_bold->setObjectName(QStringLiteral("format_text_bold"));
252 d->action_text_bold->setShortcut(Qt::CTRL | Qt::Key_B);
253 connect(sender: d->action_text_bold, signal: &QAction::triggered, context: this, slot: &KRichTextEdit::setTextBold);
254 } else {
255 d->action_text_bold = nullptr;
256 }
257
258 if (d->richTextSupport & SupportItalic) {
259 d->action_text_italic =
260 new KToggleAction(QIcon::fromTheme(QStringLiteral("format-text-italic")), i18nc("@action italicize selected text", "&Italic"), this);
261 QFont italic;
262 italic.setItalic(true);
263 d->action_text_italic->setFont(italic);
264 d->richTextActionList.append(t: (d->action_text_italic));
265 d->action_text_italic->setObjectName(QStringLiteral("format_text_italic"));
266 d->action_text_italic->setShortcut(Qt::CTRL | Qt::Key_I);
267 connect(sender: d->action_text_italic, signal: &QAction::triggered, context: this, slot: &KRichTextEdit::setTextItalic);
268 } else {
269 d->action_text_italic = nullptr;
270 }
271
272 if (d->richTextSupport & SupportUnderline) {
273 d->action_text_underline =
274 new KToggleAction(QIcon::fromTheme(QStringLiteral("format-text-underline")), i18nc("@action underline selected text", "&Underline"), this);
275 QFont underline;
276 underline.setUnderline(true);
277 d->action_text_underline->setFont(underline);
278 d->richTextActionList.append(t: (d->action_text_underline));
279 d->action_text_underline->setObjectName(QStringLiteral("format_text_underline"));
280 d->action_text_underline->setShortcut(Qt::CTRL | Qt::Key_U);
281 connect(sender: d->action_text_underline, signal: &QAction::triggered, context: this, slot: &KRichTextEdit::setTextUnderline);
282 } else {
283 d->action_text_underline = nullptr;
284 }
285
286 if (d->richTextSupport & SupportStrikeOut) {
287 d->action_text_strikeout = new KToggleAction(QIcon::fromTheme(QStringLiteral("format-text-strikethrough")), i18nc("@action", "&Strike Out"), this);
288 QFont strikeout;
289 strikeout.setStrikeOut(true);
290 d->action_text_strikeout->setFont(strikeout);
291 d->richTextActionList.append(t: (d->action_text_strikeout));
292 d->action_text_strikeout->setObjectName(QStringLiteral("format_text_strikeout"));
293 d->action_text_strikeout->setShortcut(Qt::CTRL | Qt::Key_L);
294 connect(sender: d->action_text_strikeout, signal: &QAction::triggered, context: this, slot: &KRichTextEdit::setTextStrikeOut);
295 } else {
296 d->action_text_strikeout = nullptr;
297 }
298
299 if (d->richTextSupport & SupportAlignment) {
300 // Alignment
301 d->action_align_left = new KToggleAction(QIcon::fromTheme(QStringLiteral("format-justify-left")), i18nc("@action", "Align &Left"), this);
302 d->action_align_left->setIconText(i18nc("@label left justify", "Left"));
303 d->richTextActionList.append(t: (d->action_align_left));
304 d->action_align_left->setObjectName(QStringLiteral("format_align_left"));
305 connect(sender: d->action_align_left, signal: &QAction::triggered, context: this, slot: &KRichTextEdit::alignLeft);
306
307 d->action_align_center = new KToggleAction(QIcon::fromTheme(QStringLiteral("format-justify-center")), i18nc("@action", "Align &Center"), this);
308 d->action_align_center->setIconText(i18nc("@label center justify", "Center"));
309 d->richTextActionList.append(t: (d->action_align_center));
310 d->action_align_center->setObjectName(QStringLiteral("format_align_center"));
311 connect(sender: d->action_align_center, signal: &QAction::triggered, context: this, slot: &KRichTextEdit::alignCenter);
312
313 d->action_align_right = new KToggleAction(QIcon::fromTheme(QStringLiteral("format-justify-right")), i18nc("@action", "Align &Right"), this);
314 d->action_align_right->setIconText(i18nc("@label right justify", "Right"));
315 d->richTextActionList.append(t: (d->action_align_right));
316 d->action_align_right->setObjectName(QStringLiteral("format_align_right"));
317 connect(sender: d->action_align_right, signal: &QAction::triggered, context: this, slot: &KRichTextEdit::alignRight);
318
319 d->action_align_justify = new KToggleAction(QIcon::fromTheme(QStringLiteral("format-justify-fill")), i18nc("@action", "&Justify"), this);
320 d->action_align_justify->setIconText(i18nc("@label justify fill", "Justify"));
321 d->richTextActionList.append(t: (d->action_align_justify));
322 d->action_align_justify->setObjectName(QStringLiteral("format_align_justify"));
323 connect(sender: d->action_align_justify, signal: &QAction::triggered, context: this, slot: &KRichTextEdit::alignJustify);
324
325 QActionGroup *alignmentGroup = new QActionGroup(this);
326 alignmentGroup->addAction(a: d->action_align_left);
327 alignmentGroup->addAction(a: d->action_align_center);
328 alignmentGroup->addAction(a: d->action_align_right);
329 alignmentGroup->addAction(a: d->action_align_justify);
330 } else {
331 d->action_align_left = nullptr;
332 d->action_align_center = nullptr;
333 d->action_align_right = nullptr;
334 d->action_align_justify = nullptr;
335 }
336
337 if (d->richTextSupport & SupportDirection) {
338 d->action_direction_ltr = new KToggleAction(QIcon::fromTheme(QStringLiteral("format-text-direction-ltr")), i18nc("@action", "Left-to-Right"), this);
339 d->action_direction_ltr->setIconText(i18nc("@label left-to-right", "Left-to-Right"));
340 d->richTextActionList.append(t: d->action_direction_ltr);
341 d->action_direction_ltr->setObjectName(QStringLiteral("direction_ltr"));
342 connect(sender: d->action_direction_ltr, signal: &QAction::triggered, context: this, slot: &KRichTextEdit::makeLeftToRight);
343
344 d->action_direction_rtl = new KToggleAction(QIcon::fromTheme(QStringLiteral("format-text-direction-rtl")), i18nc("@action", "Right-to-Left"), this);
345 d->action_direction_rtl->setIconText(i18nc("@label right-to-left", "Right-to-Left"));
346 d->richTextActionList.append(t: d->action_direction_rtl);
347 d->action_direction_rtl->setObjectName(QStringLiteral("direction_rtl"));
348 connect(sender: d->action_direction_rtl, signal: &QAction::triggered, context: this, slot: &KRichTextEdit::makeRightToLeft);
349
350 QActionGroup *directionGroup = new QActionGroup(this);
351 directionGroup->addAction(a: d->action_direction_ltr);
352 directionGroup->addAction(a: d->action_direction_rtl);
353 } else {
354 d->action_direction_ltr = nullptr;
355 d->action_direction_rtl = nullptr;
356 }
357
358 if (d->richTextSupport & SupportChangeListStyle) {
359 d->action_list_style = new KSelectAction(QIcon::fromTheme(QStringLiteral("format-list-unordered")), i18nc("@title:menu", "List Style"), this);
360 QStringList listStyles;
361 /* clang-format off */
362 listStyles << i18nc("@item:inmenu no list style", "None")
363 << i18nc("@item:inmenu disc list style", "Disc")
364 << i18nc("@item:inmenu circle list style", "Circle")
365 << i18nc("@item:inmenu square list style", "Square")
366 << i18nc("@item:inmenu numbered lists", "123")
367 << i18nc("@item:inmenu lowercase abc lists", "abc")
368 << i18nc("@item:inmenu uppercase abc lists", "ABC")
369 << i18nc("@item:inmenu lower case roman numerals", "i ii iii")
370 << i18nc("@item:inmenu upper case roman numerals", "I II III");
371 /* clang-format on */
372
373 d->action_list_style->setItems(listStyles);
374 d->action_list_style->setCurrentItem(0);
375 d->richTextActionList.append(t: (d->action_list_style));
376 d->action_list_style->setObjectName(QStringLiteral("format_list_style"));
377
378 connect(sender: d->action_list_style, signal: &KSelectAction::indexTriggered, context: this, slot: [this](int style) {
379 Q_D(KRichTextWidget);
380 d->_k_setListStyle(index: style);
381 });
382 connect(sender: d->action_list_style, signal: &QAction::triggered, context: this, slot: [d]() {
383 d->updateMiscActions();
384 });
385 } else {
386 d->action_list_style = nullptr;
387 }
388
389 if (d->richTextSupport & SupportIndentLists) {
390 d->action_list_indent = new QAction(QIcon::fromTheme(QStringLiteral("format-indent-more")), i18nc("@action", "Increase Indent"), this);
391 d->richTextActionList.append(t: (d->action_list_indent));
392 d->action_list_indent->setObjectName(QStringLiteral("format_list_indent_more"));
393 connect(sender: d->action_list_indent, signal: &QAction::triggered, context: this, slot: &KRichTextEdit::indentListMore);
394 connect(sender: d->action_list_indent, signal: &QAction::triggered, context: this, slot: [d]() {
395 d->updateMiscActions();
396 });
397 } else {
398 d->action_list_indent = nullptr;
399 }
400
401 if (d->richTextSupport & SupportDedentLists) {
402 d->action_list_dedent = new QAction(QIcon::fromTheme(QStringLiteral("format-indent-less")), i18nc("@action", "Decrease Indent"), this);
403 d->richTextActionList.append(t: (d->action_list_dedent));
404 d->action_list_dedent->setObjectName(QStringLiteral("format_list_indent_less"));
405 connect(sender: d->action_list_dedent, signal: &QAction::triggered, context: this, slot: &KRichTextEdit::indentListLess);
406 connect(sender: d->action_list_dedent, signal: &QAction::triggered, context: this, slot: [d]() {
407 d->updateMiscActions();
408 });
409 } else {
410 d->action_list_dedent = nullptr;
411 }
412
413 if (d->richTextSupport & SupportRuleLine) {
414 d->action_insert_horizontal_rule = new QAction(QIcon::fromTheme(QStringLiteral("insert-horizontal-rule")), i18nc("@action", "Insert Rule Line"), this);
415 d->richTextActionList.append(t: (d->action_insert_horizontal_rule));
416 d->action_insert_horizontal_rule->setObjectName(QStringLiteral("insert_horizontal_rule"));
417 connect(sender: d->action_insert_horizontal_rule, signal: &QAction::triggered, context: this, slot: &KRichTextEdit::insertHorizontalRule);
418 } else {
419 d->action_insert_horizontal_rule = nullptr;
420 }
421
422 if (d->richTextSupport & SupportHyperlinks) {
423 d->action_manage_link = new QAction(QIcon::fromTheme(QStringLiteral("insert-link")), i18nc("@action", "Link"), this);
424 d->richTextActionList.append(t: (d->action_manage_link));
425 d->action_manage_link->setObjectName(QStringLiteral("manage_link"));
426 connect(sender: d->action_manage_link, signal: &QAction::triggered, context: this, slot: [this]() {
427 Q_D(KRichTextWidget);
428 d->_k_manageLink();
429 });
430 } else {
431 d->action_manage_link = nullptr;
432 }
433
434 if (d->richTextSupport & SupportFormatPainting) {
435 d->action_format_painter = new KToggleAction(QIcon::fromTheme(QStringLiteral("draw-brush")), i18nc("@action", "Format Painter"), this);
436 d->richTextActionList.append(t: (d->action_format_painter));
437 d->action_format_painter->setObjectName(QStringLiteral("format_painter"));
438 connect(sender: d->action_format_painter, signal: &QAction::toggled, context: this, slot: [this](bool state) {
439 Q_D(KRichTextWidget);
440 d->_k_formatPainter(active: state);
441 });
442 } else {
443 d->action_format_painter = nullptr;
444 }
445
446 if (d->richTextSupport & SupportToPlainText) {
447 d->action_to_plain_text = new KToggleAction(i18nc("@action", "To Plain Text"), this);
448 d->richTextActionList.append(t: (d->action_to_plain_text));
449 d->action_to_plain_text->setObjectName(QStringLiteral("action_to_plain_text"));
450 connect(sender: d->action_to_plain_text, signal: &QAction::triggered, context: this, slot: &KRichTextEdit::switchToPlainText);
451 } else {
452 d->action_to_plain_text = nullptr;
453 }
454
455 if (d->richTextSupport & SupportSuperScriptAndSubScript) {
456 d->action_text_subscript = new KToggleAction(QIcon::fromTheme(QStringLiteral("format-text-subscript")), i18nc("@action", "Subscript"), this);
457 d->richTextActionList.append(t: (d->action_text_subscript));
458 d->action_text_subscript->setObjectName(QStringLiteral("format_text_subscript"));
459 connect(sender: d->action_text_subscript, signal: &QAction::triggered, context: this, slot: &KRichTextEdit::setTextSubScript);
460
461 d->action_text_superscript = new KToggleAction(QIcon::fromTheme(QStringLiteral("format-text-superscript")), i18nc("@action", "Superscript"), this);
462 d->richTextActionList.append(t: (d->action_text_superscript));
463 d->action_text_superscript->setObjectName(QStringLiteral("format_text_superscript"));
464 connect(sender: d->action_text_superscript, signal: &QAction::triggered, context: this, slot: &KRichTextEdit::setTextSuperScript);
465 } else {
466 d->action_text_subscript = nullptr;
467
468 d->action_text_superscript = nullptr;
469 }
470
471 if (d->richTextSupport & SupportHeading) {
472 // TODO: an icon maybe?
473 d->action_heading_level = new KSelectAction(i18nc("@title:menu", "Heading Level"), this);
474 const QStringList headingLevels = {i18nc("@item:inmenu no heading", "Basic text"),
475 i18nc("@item:inmenu heading level 1 (largest)", "Title"),
476 i18nc("@item:inmenu heading level 2", "Subtitle"),
477 i18nc("@item:inmenu heading level 3", "Section"),
478 i18nc("@item:inmenu heading level 4", "Subsection"),
479 i18nc("@item:inmenu heading level 5", "Paragraph"),
480 i18nc("@item:inmenu heading level 6 (smallest)", "Subparagraph")};
481
482 d->action_heading_level->setItems(headingLevels);
483 d->action_heading_level->setCurrentItem(0);
484 d->richTextActionList.append(t: d->action_heading_level);
485 d->action_heading_level->setObjectName(QStringLiteral("format_heading_level"));
486 connect(sender: d->action_heading_level, signal: &KSelectAction::indexTriggered, context: this, slot: [this](int level) {
487 Q_D(KRichTextWidget);
488 d->_k_setHeadingLevel(level);
489 });
490 } else {
491 d->action_heading_level = nullptr;
492 }
493
494 disconnect(sender: this, signal: &QTextEdit::currentCharFormatChanged, receiver: this, zero: nullptr);
495 disconnect(sender: this, signal: &QTextEdit::cursorPositionChanged, receiver: this, zero: nullptr);
496 connect(sender: this, signal: &QTextEdit::currentCharFormatChanged, context: this, slot: [d](const QTextCharFormat &format) {
497 d->updateCharFormatActions(format);
498 });
499 connect(sender: this, signal: &QTextEdit::cursorPositionChanged, context: this, slot: [d]() {
500 d->updateMiscActions();
501 });
502
503 d->updateMiscActions();
504 d->updateCharFormatActions(format: currentCharFormat());
505
506 return d->richTextActionList;
507}
508
509void KRichTextWidget::setActionsEnabled(bool enabled)
510{
511 Q_D(KRichTextWidget);
512
513 for (QAction *action : std::as_const(t&: d->richTextActionList)) {
514 action->setEnabled(enabled);
515 }
516 d->richTextEnabled = enabled;
517}
518
519void KRichTextWidgetPrivate::_k_setListStyle(int index)
520{
521 Q_Q(KRichTextWidget);
522
523 q->setListStyle(index);
524 updateMiscActions();
525}
526
527void KRichTextWidgetPrivate::_k_setHeadingLevel(int level)
528{
529 Q_Q(KRichTextWidget);
530
531 q->setHeadingLevel(level);
532 updateMiscActions();
533}
534
535void KRichTextWidgetPrivate::updateCharFormatActions(const QTextCharFormat &format)
536{
537 QFont f = format.font();
538
539 if (richTextSupport & KRichTextWidget::SupportFontFamily) {
540 action_font_family->setFont(f.family());
541 }
542 if (richTextSupport & KRichTextWidget::SupportFontSize) {
543 if (f.pointSize() > 0) {
544 action_font_size->setFontSize(f.pointSize());
545 }
546 }
547
548 if (richTextSupport & KRichTextWidget::SupportBold) {
549 action_text_bold->setChecked(f.bold());
550 }
551
552 if (richTextSupport & KRichTextWidget::SupportItalic) {
553 action_text_italic->setChecked(f.italic());
554 }
555
556 if (richTextSupport & KRichTextWidget::SupportUnderline) {
557 action_text_underline->setChecked(f.underline());
558 }
559
560 if (richTextSupport & KRichTextWidget::SupportStrikeOut) {
561 action_text_strikeout->setChecked(f.strikeOut());
562 }
563
564 if (richTextSupport & KRichTextWidget::SupportSuperScriptAndSubScript) {
565 QTextCharFormat::VerticalAlignment vAlign = format.verticalAlignment();
566 action_text_superscript->setChecked(vAlign == QTextCharFormat::AlignSuperScript);
567 action_text_subscript->setChecked(vAlign == QTextCharFormat::AlignSubScript);
568 }
569}
570
571void KRichTextWidgetPrivate::updateMiscActions()
572{
573 Q_Q(KRichTextWidget);
574
575 if (richTextSupport & KRichTextWidget::SupportAlignment) {
576 Qt::Alignment a = q->alignment();
577 if (a & Qt::AlignLeft) {
578 action_align_left->setChecked(true);
579 } else if (a & Qt::AlignHCenter) {
580 action_align_center->setChecked(true);
581 } else if (a & Qt::AlignRight) {
582 action_align_right->setChecked(true);
583 } else if (a & Qt::AlignJustify) {
584 action_align_justify->setChecked(true);
585 }
586 }
587
588 if (richTextSupport & KRichTextWidget::SupportChangeListStyle) {
589 if (q->textCursor().currentList()) {
590 action_list_style->setCurrentItem(-q->textCursor().currentList()->format().style());
591 } else {
592 action_list_style->setCurrentItem(0);
593 }
594 }
595
596 if (richTextSupport & KRichTextWidget::SupportIndentLists) {
597 if (richTextEnabled) {
598 action_list_indent->setEnabled(q->canIndentList());
599 } else {
600 action_list_indent->setEnabled(false);
601 }
602 }
603
604 if (richTextSupport & KRichTextWidget::SupportDedentLists) {
605 if (richTextEnabled) {
606 action_list_dedent->setEnabled(q->canDedentList());
607 } else {
608 action_list_dedent->setEnabled(false);
609 }
610 }
611
612 if (richTextSupport & KRichTextWidget::SupportDirection) {
613 const Qt::LayoutDirection direction = q->textCursor().blockFormat().layoutDirection();
614 action_direction_ltr->setChecked(direction == Qt::LeftToRight);
615 action_direction_rtl->setChecked(direction == Qt::RightToLeft);
616 }
617
618 if (richTextSupport & KRichTextWidget::SupportHeading) {
619 action_heading_level->setCurrentItem(q->textCursor().blockFormat().headingLevel());
620 }
621}
622
623void KRichTextWidgetPrivate::_k_setTextForegroundColor()
624{
625 Q_Q(KRichTextWidget);
626
627 const QColor currentColor = q->textColor();
628 const QColor defaultColor = q->palette().color(cg: QPalette::Active, cr: QPalette::Text);
629
630 const QColor selectedColor = QColorDialog::getColor(initial: currentColor.isValid() ? currentColor : defaultColor, parent: q);
631
632 if (!selectedColor.isValid() && !currentColor.isValid()) {
633 q->setTextForegroundColor(defaultColor);
634 } else if (selectedColor.isValid()) {
635 q->setTextForegroundColor(selectedColor);
636 }
637}
638
639void KRichTextWidgetPrivate::_k_setTextBackgroundColor()
640{
641 Q_Q(KRichTextWidget);
642
643 QTextCharFormat fmt = q->textCursor().charFormat();
644 const QColor currentColor = fmt.background().color();
645 const QColor defaultColor = q->palette().color(cg: QPalette::Active, cr: QPalette::Text);
646
647 const QColor selectedColor = QColorDialog::getColor(initial: currentColor.isValid() ? currentColor : defaultColor, parent: q);
648
649 if (!selectedColor.isValid() && !currentColor.isValid()) {
650 q->setTextBackgroundColor(defaultColor);
651 } else if (selectedColor.isValid()) {
652 q->setTextBackgroundColor(selectedColor);
653 }
654}
655
656void KRichTextWidgetPrivate::_k_manageLink()
657{
658 Q_Q(KRichTextWidget);
659
660 q->selectLinkText();
661 KLinkDialog *linkDialog = new KLinkDialog(q);
662 linkDialog->setLinkText(q->currentLinkText());
663 linkDialog->setLinkUrl(q->currentLinkUrl());
664 linkDialog->setAttribute(Qt::WA_DeleteOnClose);
665
666 QObject::connect(sender: linkDialog, signal: &QDialog::accepted, context: linkDialog, slot: [linkDialog, this]() {
667 Q_Q(KRichTextWidget);
668 q->updateLink(linkUrl: linkDialog->linkUrl(), linkText: linkDialog->linkText());
669 });
670
671 linkDialog->show();
672}
673
674void KRichTextWidget::mouseReleaseEvent(QMouseEvent *event)
675{
676 Q_D(KRichTextWidget);
677
678 if (d->painterActive) {
679 // If the painter is active, paint the selection with the
680 // correct format.
681 if (textCursor().hasSelection()) {
682 QTextCursor c = textCursor();
683 c.setCharFormat(d->painterFormat);
684 setTextCursor(c);
685 }
686 d->painterActive = false;
687 d->action_format_painter->setChecked(false);
688 }
689 KRichTextEdit::mouseReleaseEvent(e: event);
690}
691
692void KRichTextWidgetPrivate::_k_formatPainter(bool active)
693{
694 Q_Q(KRichTextWidget);
695
696 if (active) {
697 painterFormat = q->currentCharFormat();
698 painterActive = true;
699 q->viewport()->setCursor(QCursor(QIcon::fromTheme(QStringLiteral("draw-brush")).pixmap(w: 32, h: 32), 0, 32));
700 } else {
701 painterFormat = QTextCharFormat();
702 painterActive = false;
703 q->viewport()->setCursor(Qt::IBeamCursor);
704 }
705}
706
707void KRichTextWidget::updateActionStates()
708{
709 Q_D(KRichTextWidget);
710
711 d->updateMiscActions();
712 d->updateCharFormatActions(format: currentCharFormat());
713}
714
715#include "moc_krichtextwidget.cpp"
716

source code of ktextwidgets/src/widgets/krichtextwidget.cpp