1 /* This file is part of the KDE project
2 * Copyright (C) 2007, 2008, 2010 Thomas Zander <zander@kde.org>
3 * Copyright (C) 2009-2010 C. Boemann <cbo@boemann.dk>
4 * Copyright (C) 2011-2012 Pierre Stirnweiss <pstirnweiss@googlemail.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library 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 GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22 #include "SimpleCharacterWidget.h"
23 #include "TextTool.h"
24 #include "../commands/ChangeListCommand.h"
25 #include "StylesModel.h"
26 #include "DockerStylesComboModel.h"
27 #include "StylesDelegate.h"
28 #include <KoStyleThumbnailer.h>
29
30 #include <QAction>
31 #include <kselectaction.h>
32 #include <KoTextBlockData.h>
33 #include <KoCharacterStyle.h>
34 #include <KoParagraphStyle.h>
35 #include <KoInlineTextObjectManager.h>
36 #include <KoTextDocumentLayout.h>
37 #include <KoZoomHandler.h>
38 #include <KoStyleManager.h>
39
40 #include <QDebug>
41
42 #include <QTextLayout>
43 #include <QComboBox>
44
SimpleCharacterWidget(TextTool * tool,QWidget * parent)45 SimpleCharacterWidget::SimpleCharacterWidget(TextTool *tool, QWidget *parent)
46 : QWidget(parent),
47 m_styleManager(0),
48 m_blockSignals(false),
49 m_comboboxHasBidiItems(false),
50 m_tool(tool),
51 m_thumbnailer(new KoStyleThumbnailer()),
52 m_stylesModel(new StylesModel(0, StylesModel::CharacterStyle)),
53 m_sortedStylesModel(new DockerStylesComboModel()),
54 m_stylesDelegate(0)
55 {
56 widget.setupUi(this);
57 widget.bold->setDefaultAction(tool->action("format_bold"));
58 widget.italic->setDefaultAction(tool->action("format_italic"));
59 widget.strikeOut->setDefaultAction(tool->action("format_strike"));
60 widget.underline->setDefaultAction(tool->action("format_underline"));
61 widget.textColor->setDefaultAction(tool->action("format_textcolor"));
62 widget.backgroundColor->setDefaultAction(tool->action("format_backgroundcolor"));
63 widget.superscript->setDefaultAction(tool->action("format_super"));
64 widget.subscript->setDefaultAction(tool->action("format_sub"));
65 widget.moreOptions->setText("...");
66 widget.moreOptions->setToolTip(i18n("Change font format"));
67 connect(widget.moreOptions, SIGNAL(clicked(bool)), tool->action("format_font"), SLOT(trigger()));
68
69 connect(widget.bold, SIGNAL(clicked(bool)), this, SIGNAL(doneWithFocus()));
70 connect(widget.italic, SIGNAL(clicked(bool)), this, SIGNAL(doneWithFocus()));
71 connect(widget.strikeOut, SIGNAL(clicked(bool)), this, SIGNAL(doneWithFocus()));
72 connect(widget.underline, SIGNAL(clicked(bool)), this, SIGNAL(doneWithFocus()));
73 connect(widget.textColor, SIGNAL(clicked(bool)), this, SIGNAL(doneWithFocus()));
74 connect(widget.backgroundColor, SIGNAL(clicked(bool)), this, SIGNAL(doneWithFocus()));
75 connect(widget.superscript, SIGNAL(clicked(bool)), this, SIGNAL(doneWithFocus()));
76 connect(widget.subscript, SIGNAL(clicked(bool)), this, SIGNAL(doneWithFocus()));
77
78 QWidgetAction *fontFamilyAction = qobject_cast<QWidgetAction *>(tool->action("format_fontfamily"));
79 QComboBox *family = fontFamilyAction ? qobject_cast<QComboBox*> (fontFamilyAction->requestWidget(this)) : 0;
80 if (family) { // kdelibs 4.1 didn't return anything here.
81 widget.fontsFrame->addWidget(family,0,0);
82 connect(family, SIGNAL(activated(int)), this, SIGNAL(doneWithFocus()));
83 connect(family, SIGNAL(activated(int)), this, SLOT(fontFamilyActivated(int)));
84 }
85 QWidgetAction *fontSizeAction = qobject_cast<QWidgetAction *>(tool->action("format_fontsize"));
86 QComboBox *size = fontSizeAction ? qobject_cast<QComboBox*> (fontSizeAction->requestWidget(this)) : 0;
87 if (size) { // kdelibs 4.1 didn't return anything here.
88 widget.fontsFrame->addWidget(size,0,1);
89 connect(size, SIGNAL(activated(int)), this, SIGNAL(doneWithFocus()));
90 connect(size, SIGNAL(activated(int)), this, SLOT(fontSizeActivated(int)));
91 QDoubleValidator* validator = new QDoubleValidator(2, 999, 1, size);
92 size->setValidator(validator);
93 }
94
95 widget.fontsFrame->setColumnStretch(0,1);
96
97 m_stylesModel->setStyleThumbnailer(m_thumbnailer);
98 widget.characterStyleCombo->setStylesModel(m_sortedStylesModel);
99 connect(widget.characterStyleCombo, SIGNAL(selected(QModelIndex)), this, SLOT(styleSelected(QModelIndex)));
100 connect(widget.characterStyleCombo, SIGNAL(newStyleRequested(QString)), this, SIGNAL(newStyleRequested(QString)));
101 connect(widget.characterStyleCombo, SIGNAL(newStyleRequested(QString)), this, SIGNAL(doneWithFocus()));
102 connect(widget.characterStyleCombo, SIGNAL(showStyleManager(int)), this, SLOT(slotShowStyleManager(int)));
103
104 m_sortedStylesModel->setStylesModel(m_stylesModel);
105 }
106
~SimpleCharacterWidget()107 SimpleCharacterWidget::~SimpleCharacterWidget()
108 {
109 //the model is set on the comboBox which takes ownership
110 delete m_thumbnailer;
111 }
112
setStyleManager(KoStyleManager * sm)113 void SimpleCharacterWidget::setStyleManager(KoStyleManager *sm)
114 {
115 Q_ASSERT(sm);
116 if (!sm || m_styleManager == sm) {
117 return;
118 }
119 if (m_styleManager) {
120 disconnect(m_styleManager, SIGNAL(styleApplied(const KoCharacterStyle*)), this, SLOT(slotParagraphStyleApplied(const KoCharacterStyle*)));
121 }
122 m_styleManager = sm;
123 //we want to disconnect this before setting the stylemanager. Populating the model apparently selects the first inserted item. We don't want this to actually set a new style.
124 disconnect(widget.characterStyleCombo, SIGNAL(selected(QModelIndex)), this, SLOT(styleSelected(QModelIndex)));
125 m_stylesModel->setStyleManager(sm);
126 m_sortedStylesModel->setStyleManager(sm);
127 connect(widget.characterStyleCombo, SIGNAL(selected(QModelIndex)), this, SLOT(styleSelected(QModelIndex)));
128 connect(m_styleManager, SIGNAL(styleApplied(const KoCharacterStyle*)), this, SLOT(slotCharacterStyleApplied(const KoCharacterStyle*)));
129 }
130
setInitialUsedStyles(QVector<int> list)131 void SimpleCharacterWidget::setInitialUsedStyles(QVector<int> list)
132 {
133 m_sortedStylesModel->setInitialUsedStyles(list);
134 }
135
setCurrentFormat(const QTextCharFormat & format,const QTextCharFormat & refBlockCharFormat)136 void SimpleCharacterWidget::setCurrentFormat(const QTextCharFormat& format, const QTextCharFormat& refBlockCharFormat)
137 {
138 if (!m_styleManager || format == m_currentCharFormat) {
139 return;
140 }
141 m_currentCharFormat = format;
142
143 KoCharacterStyle *style(m_styleManager->characterStyle(m_currentCharFormat.intProperty(KoCharacterStyle::StyleId)));
144 bool useParagraphStyle = false;
145 if (!style) {
146 style = static_cast<KoCharacterStyle*>(m_styleManager->paragraphStyle(m_currentCharFormat.intProperty(KoParagraphStyle::StyleId)));
147 useParagraphStyle = true;
148 }
149 if (style) {
150 bool unchanged = true;
151 QTextCharFormat comparisonFormat = refBlockCharFormat;
152 style->applyStyle(comparisonFormat);
153 //Here we are making quite a few assumptions:
154 //i. we can set the "ensured" properties on a blank charFormat. These corresponds to Qt default. We are not creating false positive (ie. different styles showing as identical).
155 //ii. a property whose toBool returns as false is identical to an unset property (this is done through the clearUnsetProperties method)
156 style->ensureMinimalProperties(comparisonFormat);
157 style->ensureMinimalProperties(m_currentCharFormat);
158 clearUnsetProperties(comparisonFormat);
159 clearUnsetProperties(m_currentCharFormat);
160 if (m_currentCharFormat.properties().count() != comparisonFormat.properties().count()) {
161 unchanged = false;
162 }
163 else {
164 foreach(int property, m_currentCharFormat.properties().keys()) {
165 if (m_currentCharFormat.property(property) != comparisonFormat.property(property)) {
166 unchanged = false;
167 }
168 }
169 }
170 disconnect(widget.characterStyleCombo, SIGNAL(selected(QModelIndex)), this, SLOT(styleSelected(QModelIndex)));
171 //TODO, this is very brittle index 1 is because index 0 is the title. The proper solution to that would be for the "None" style to have a styleId which does not get applied on the text, but can be used in the ui
172 widget.characterStyleCombo->setCurrentIndex((useParagraphStyle) ? 1 : m_sortedStylesModel->indexOf(style).row());
173 widget.characterStyleCombo->setStyleIsOriginal(unchanged);
174 widget.characterStyleCombo->slotUpdatePreview();
175 connect(widget.characterStyleCombo, SIGNAL(selected(QModelIndex)), this, SLOT(styleSelected(QModelIndex)));
176 }
177 }
178
clearUnsetProperties(QTextFormat & format)179 void SimpleCharacterWidget::clearUnsetProperties(QTextFormat &format)
180 {
181 foreach(int property, format.properties().keys()) {
182 if (!format.property(property).toBool()) {
183 format.clearProperty(property);
184 }
185 }
186 }
187
fontFamilyActivated(int index)188 void SimpleCharacterWidget::fontFamilyActivated(int index) {
189 /**
190 * Hack:
191 *
192 * Selecting a font that is already selected in the combobox
193 * will not trigger the action, so we help it on the way by
194 * manually triggering it here if that happens.
195 */
196 if (index == m_lastFontFamilyIndex) {
197 KSelectAction *action = qobject_cast<KSelectAction*>(m_tool->action("format_fontfamily"));
198 if(action->currentAction())
199 action->currentAction()->trigger();
200 }
201 m_lastFontFamilyIndex = index;
202 }
203
fontSizeActivated(int index)204 void SimpleCharacterWidget::fontSizeActivated(int index) {
205 /**
206 * Hack:
207 *
208 * Selecting a font size that is already selected in the
209 * combobox will not trigger the action, so we help it on
210 * the way by manually triggering it here if that happens.
211 */
212 if (index == m_lastFontSizeIndex) {
213 KSelectAction *action = qobject_cast<KSelectAction*>(m_tool->action("format_fontsize"));
214 action->currentAction()->trigger();
215 }
216 m_lastFontSizeIndex = index;
217 }
218
setCurrentBlockFormat(const QTextBlockFormat & format)219 void SimpleCharacterWidget::setCurrentBlockFormat(const QTextBlockFormat &format)
220 {
221 if (format == m_currentBlockFormat)
222 return;
223 m_currentBlockFormat = format;
224
225 m_stylesModel->setCurrentParagraphStyle(format.intProperty(KoParagraphStyle::StyleId));
226 disconnect(widget.characterStyleCombo, SIGNAL(selected(QModelIndex)), this, SLOT(styleSelected(QModelIndex)));
227 widget.characterStyleCombo->slotUpdatePreview();
228 connect(widget.characterStyleCombo, SIGNAL(selected(QModelIndex)), this, SLOT(styleSelected(QModelIndex)));
229 }
230
styleSelected(int index)231 void SimpleCharacterWidget::styleSelected(int index)
232 {
233 KoCharacterStyle *charStyle = m_styleManager->characterStyle(m_sortedStylesModel->index(index, 0, QModelIndex()).internalId());
234
235 //if the selected item correspond to a null characterStyle, send the null pointer. the tool should set the characterStyle as per paragraph
236 emit characterStyleSelected(charStyle);
237 emit doneWithFocus();
238 }
239
styleSelected(const QModelIndex & index)240 void SimpleCharacterWidget::styleSelected(const QModelIndex &index)
241 {
242 if (!index.isValid()) {
243 emit doneWithFocus();
244 return;
245 }
246 KoCharacterStyle *charStyle = m_styleManager->characterStyle(index.internalId());
247
248 //if the selected item correspond to a null characterStyle, send the null pointer. the tool should set the characterStyle as per paragraph
249 emit characterStyleSelected(charStyle);
250 emit doneWithFocus();
251 }
252
slotShowStyleManager(int index)253 void SimpleCharacterWidget::slotShowStyleManager(int index)
254 {
255 int styleId = m_sortedStylesModel->index(index, 0, QModelIndex()).internalId();
256 emit showStyleManager(styleId);
257 emit doneWithFocus();
258 }
259
slotCharacterStyleApplied(const KoCharacterStyle * style)260 void SimpleCharacterWidget::slotCharacterStyleApplied(const KoCharacterStyle *style)
261 {
262 m_sortedStylesModel->styleApplied(style);
263 }
264