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 | |
29 | class KRichTextWidgetPrivate : public KRichTextEditPrivate |
30 | { |
31 | Q_DECLARE_PUBLIC(KRichTextWidget) |
32 | |
33 | public: |
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 | |
139 | void KRichTextWidgetPrivate::init() |
140 | { |
141 | Q_Q(KRichTextWidget); |
142 | |
143 | q->setRichTextSupport(KRichTextWidget::FullSupport); |
144 | } |
145 | |
146 | KRichTextWidget::KRichTextWidget(QWidget *parent) |
147 | : KRichTextEdit(*new KRichTextWidgetPrivate(this), parent) |
148 | { |
149 | Q_D(KRichTextWidget); |
150 | |
151 | d->init(); |
152 | } |
153 | |
154 | KRichTextWidget::KRichTextWidget(const QString &text, QWidget *parent) |
155 | : KRichTextEdit(*new KRichTextWidgetPrivate(this), text, parent) |
156 | { |
157 | Q_D(KRichTextWidget); |
158 | |
159 | d->init(); |
160 | } |
161 | |
162 | KRichTextWidget::~KRichTextWidget() = default; |
163 | |
164 | KRichTextWidget::RichTextSupport KRichTextWidget::richTextSupport() const |
165 | { |
166 | Q_D(const KRichTextWidget); |
167 | |
168 | return d->richTextSupport; |
169 | } |
170 | |
171 | void KRichTextWidget::setRichTextSupport(const KRichTextWidget::RichTextSupport &support) |
172 | { |
173 | Q_D(KRichTextWidget); |
174 | |
175 | d->richTextSupport = support; |
176 | } |
177 | |
178 | QList<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 | |
509 | void 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 | |
519 | void KRichTextWidgetPrivate::_k_setListStyle(int index) |
520 | { |
521 | Q_Q(KRichTextWidget); |
522 | |
523 | q->setListStyle(index); |
524 | updateMiscActions(); |
525 | } |
526 | |
527 | void KRichTextWidgetPrivate::_k_setHeadingLevel(int level) |
528 | { |
529 | Q_Q(KRichTextWidget); |
530 | |
531 | q->setHeadingLevel(level); |
532 | updateMiscActions(); |
533 | } |
534 | |
535 | void 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 | |
571 | void 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 | |
623 | void 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 | |
639 | void 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 | |
656 | void 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 | |
674 | void 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 | |
692 | void 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 | |
707 | void KRichTextWidget::updateActionStates() |
708 | { |
709 | Q_D(KRichTextWidget); |
710 | |
711 | d->updateMiscActions(); |
712 | d->updateCharFormatActions(format: currentCharFormat()); |
713 | } |
714 | |
715 | #include "moc_krichtextwidget.cpp" |
716 | |