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

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