]> git.sesse.net Git - kdenlive/blob - src/unicodedialog.cpp
use const+reference
[kdenlive] / src / unicodedialog.cpp
1 /***************************************************************************
2  *   Copyright (C) 2008 by Simon Andreas Eugster (simon.eu@gmail.com)      *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  ***************************************************************************/
9
10 #include "unicodedialog.h"
11
12 #include <QWheelEvent>
13
14 /// CONSTANTS
15
16 const int MAX_LENGTH_HEX = 4;
17 const uint MAX_UNICODE_V1 = 65535;
18
19
20 /// CONSTRUCTORS/DECONSTRUCTORS
21
22 UnicodeDialog::UnicodeDialog(InputMethod inputMeth) :
23         inputMethod(inputMeth),
24         m_lastCursorPos(0)
25 {
26     setupUi(this);
27     readChoices();
28     showLastUnicode();
29     connect(unicodeNumber, SIGNAL(textChanged(QString)), this, SLOT(slotTextChanged(QString)));
30     connect(unicodeNumber, SIGNAL(returnPressed()), this, SLOT(slotReturnPressed()));
31     connect(arrowUp, SIGNAL(clicked()), this, SLOT(slotPrevUnicode()));
32     connect(arrowDown, SIGNAL(clicked()), this, SLOT(slotNextUnicode()));
33
34     switch (inputMethod) {
35     case InputHex:
36         unicodeNumber->setMaxLength(MAX_LENGTH_HEX);
37         break;
38
39     case InputDec:
40         break;
41     }
42
43     arrowUp->setShortcut(Qt::Key_Up);
44     arrowDown->setShortcut(Qt::Key_Down);
45     unicode_link->setText(i18n("Information about unicode characters: <a href=\"http://decodeunicode.org\">http://decodeunicode.org</a>"));
46     arrowUp->setToolTip(i18n("Previous Unicode character (Arrow Up)"));
47     arrowDown->setToolTip(i18n("Next Unicode character (Arrow Down)"));
48     unicodeNumber->setToolTip(i18n("Enter your Unicode number here. Allowed characters: [0-9] and [a-f]."));
49     unicodeNumber->selectAll(); // Selection will be reset by setToolTip and similar, so set it here
50
51 }
52
53 UnicodeDialog::~UnicodeDialog()
54 {
55 }
56
57
58 /// PUBLIC SLOTS
59
60 int UnicodeDialog::exec()
61 {
62     unicodeNumber->setFocus();
63     return QDialog::exec();
64 }
65
66
67 /// METHODS
68
69 void UnicodeDialog::showLastUnicode()
70 {
71     unicodeNumber->setText(m_lastUnicodeNumber);
72     unicodeNumber->selectAll();
73     slotTextChanged(m_lastUnicodeNumber);
74 }
75
76 bool UnicodeDialog::controlCharacter(const QString &text)
77 {
78     bool isControlCharacter = false;
79     QString t = text.toLower();
80
81     switch (inputMethod) {
82     case InputHex:
83         if (t.isEmpty()
84                 || (t.length() == 1 && !(t == "9" || t == "a" || t == "d"))
85                 || (t.length() == 2 && t.at(0) == QChar('1'))) {
86             isControlCharacter = true;
87         }
88         break;
89
90     case InputDec:
91         bool ok;
92         isControlCharacter = controlCharacter(text.toUInt(&ok, 16));
93         break;
94     }
95
96     return isControlCharacter;
97 }
98
99 bool UnicodeDialog::controlCharacter(uint value)
100 {
101     bool isControlCharacter = false;
102
103     if (value < 32 && !(value == 9 || value == 10 || value == 13)) {
104         isControlCharacter = true;
105     }
106     return isControlCharacter;
107
108 }
109
110 QString UnicodeDialog::trimmedUnicodeNumber(QString text)
111 {
112     while (text.length() > 0 && text.at(0) == QChar('0')) {
113         text = text.remove(0, 1);
114     }
115     return text;
116 }
117
118 QString UnicodeDialog::unicodeInfo(const QString &unicode)
119 {
120     QString infoText(i18n("<small>(no character selected)</small>"));
121     if (unicode.length() == 0) return infoText;
122
123     QString u = trimmedUnicodeNumber(unicode).toLower();
124
125     if (controlCharacter(u)) {
126         infoText = i18n("Control character. Cannot be inserted/printed. See <a href=\"http://en.wikipedia.org/wiki/Control_character\">Wikipedia:Control_character</a>");
127     } else if (u == "a") {
128         infoText = i18n("Line Feed (newline character, \\\\n)");
129     } else if (u == "20") {
130         infoText = i18n("Standard space character. (Other space characters: U+00a0, U+2000&#x2013;200b, U+202f)");
131     } else if (u == "a0") {
132         infoText = i18n("No-break space. &amp;nbsp; in HTML. See U+2009 and U+0020.");
133     } else if (u == "ab" || u == "bb" || u == "2039" || u == "203a") {
134         infoText = i18n("<p><strong>&laquo;</strong> (u+00ab, <code>&amp;lfquo;</code> in HTML) and <strong>&raquo;</strong> (u+00bb, <code>&amp;rfquo;</code> in HTML) are called Guillemets or angle quotes. Usage in different countries: France (with non-breaking Space 0x00a0), Switzerland, Germany, Finland and Sweden.</p><p><strong>&lsaquo;</strong> and <strong>&rsaquo;</strong> (U+2039/203a, <code>&amp;lsaquo;/&amp;rsaquo;</code>) are their single quote equivalents.</p><p>See <a href=\"http://en.wikipedia.org/wiki/Guillemets\">Wikipedia:Guillemets</a></p>");
135     } else if (u == "2002") {
136         infoText = i18n("En Space (width of an n)");
137     } else if (u == "2003") {
138         infoText = i18n("Em Space (width of an m)");
139     } else if (u == "2004") {
140         infoText = i18n("Three-Per-Em Space. Width: 1/3 of one <em>em</em>");
141     } else if (u == "2005") {
142         infoText = i18n("Four-Per-Em Space. Width: 1/4 of one <em>em</em>");
143     } else if (u == "2006") {
144         infoText = i18n("Six-Per-Em Space. Width: 1/6 of one <em>em</em>");
145     } else if (u == "2007") {
146         infoText = i18n("Figure space (non-breaking). Width of a digit if digits have fixed width in this font.");
147     } else if (u == "2008") {
148         infoText = i18n("Punctuation Space. Width the same as between a punctuation character and the next character.");
149     } else if (u == "2009") {
150         infoText = i18n("Thin space, in HTML also &amp;thinsp;. See U+202f and <a href=\"http://en.wikipedia.org/wiki/Space_(punctuation)\">Wikipedia:Space_(punctuation)</a>");
151     } else if (u == "200a") {
152         infoText = i18n("Hair Space. Thinner than U+2009.");
153     } else if (u == "2019") {
154         infoText = i18n("Punctuation Apostrophe. Should be used instead of U+0027. See <a href=\"http://en.wikipedia.org/wiki/Apostrophe\">Wikipedia:Apostrophe</a>");
155     } else if (u == "2013") {
156         infoText = i18n("<p>An en Dash (dash of the width of an n).</p><p>Usage examples: In English language for value ranges (1878&#x2013;1903), for relationships/connections (Zurich&#x2013;Dublin). In the German language it is also used (with spaces!) for showing thoughts: &ldquo;Es war &#x2013; wie immer in den Ferien &#x2013; ein regnerischer Tag.</p> <p>See <a href=\"http://en.wikipedia.org/wiki/Dash\">Wikipedia:Dash</a></p>");
157     } else if (u == "2014") {
158         infoText = i18n("<p>An em Dash (dash of the width of an m).</p><p>Usage examples: In English language to mark&#x2014;like here&#x2014;thoughts. Traditionally without spaces. </p><p>See <a href=\"http://en.wikipedia.org/wiki/Dash\">Wikipedia:Dash</a></p>");
159     } else if (u == "202f") {
160         infoText = i18n("<p>Narrow no-break space. Has the same width as U+2009.</p><p>Usage: For units (spaces are marked with U+2423, &#x2423;): 230&#x2423;V, &#x2212;21&#x2423;&deg;C, 50&#x2423;lb, <em>but</em> 90&deg; (no space). In German for abbreviations (like: i.&#x202f;d.&#x202f;R. instead of i.&#xa0;d.&#xa0;R. with U+00a0).</p><p>See <a href=\"http://de.wikipedia.org/wiki/Schmales_Leerzeichen\">Wikipedia:de:Schmales_Leerzeichen</a></p>");
161     } else if (u == "2026") {
162         infoText = i18n("Ellipsis: If text has been left o&#x2026; See <a href=\"http://en.wikipedia.org/wiki/Ellipsis\">Wikipedia:Ellipsis</a>");
163     } else if (u == "2212") {
164         infoText = i18n("Minus sign. For numbers: &#x2212;42");
165     } else if (u == "2423") {
166         infoText = i18n("Open box; stands for a space.");
167     } else if (u == "2669") {
168         infoText = i18n("Quarter note (Am.) or crochet (Brit.). See <a href=\"http://en.wikipedia.org/wiki/Quarter_note\">Wikipedia:Quarter_note</a>");
169     } else if (u == "266a" || u == "266b") {
170         infoText = i18n("Eighth note (Am.) or quaver (Brit.). Half as long as a quarter note (U+2669). See <a href=\"http://en.wikipedia.org/wiki/Eighth_note\">Wikipedia:Eighth_note</a>");
171     } else if (u == "266c") {
172         infoText = i18n("Sixteenth note (Am.) or semiquaver (Brit.). Half as long as an eighth note (U+266a). See <a href=\"http://en.wikipedia.org/wiki/Sixteenth_note\">Wikipedia:Sixteenth_note</a>");
173     } else if (u == "1D162") {
174         infoText = i18n("Thirty-second note (Am.) or demisemiquaver (Brit.). Half as long as a sixteenth note (U+266b). See <a href=\"http://en.wikipedia.org/wiki/Thirty-second_note\">Wikipedia:Thirty-second_note</a>");
175     } else {
176         infoText = i18n("<small>No additional information available for this character.</small>");
177     }
178
179     return infoText;
180 }
181
182 QString UnicodeDialog::validateText(QString text)
183 {
184     QRegExp regex("([0-9]|[a-f])", Qt::CaseInsensitive, QRegExp::RegExp2);
185     QString newText = "";
186     int pos = 0;
187
188     switch (inputMethod) {
189     case InputHex:
190         // Remove all characters we don't want
191         while ((pos = regex.indexIn(text, pos)) != -1) {
192             newText += regex.cap(1);
193             pos++;
194         }
195         break;
196
197     case InputDec:
198         // TODO
199         break;
200     }
201
202     return newText;
203 }
204
205 void UnicodeDialog::updateOverviewChars(uint unicode)
206 {
207     QString left = "";
208     QString right = "";
209     uint i;
210
211     for (i = 1; i <= 4; i++) {
212         if (unicode > i && !controlCharacter(unicode - i)) {
213             left = ' ' + left;
214             left = QChar(unicode - i) + left;
215         }
216     }
217
218     for (i = 1; i <= 8; i++) {
219         if (unicode + i <= MAX_UNICODE_V1 && !controlCharacter(unicode + i)) {
220             right += QChar(unicode + i);
221             right += ' ';
222         }
223     }
224
225     leftChars->setText(left);
226     rightChars->setText(right);
227
228 }
229
230 void UnicodeDialog::clearOverviewChars()
231 {
232     leftChars->setText("");
233     rightChars->setText("");
234 }
235
236 QString UnicodeDialog::nextUnicode(QString text, Direction direction)
237 {
238     uint value = 0;
239     QString newText = "";
240     bool ok;
241
242     switch (inputMethod) {
243     case InputHex:
244         value = text.toUInt(&ok, 16);
245         switch (direction) {
246         case Backward:
247             value--;
248             break;
249         default:
250             value++;
251             break;
252         }
253         // Wrapping
254         if (value == (uint) - 1) value = MAX_UNICODE_V1;
255         if (value > MAX_UNICODE_V1) value = 0;
256
257         newText.setNum(value, 16);
258         break;
259
260     case InputDec:
261         break;
262     }
263
264     return newText;
265 }
266
267 void UnicodeDialog::readChoices()
268 {
269     // Get a pointer to a shared configuration instance, then get the TitleWidget group.
270     KSharedConfigPtr config = KGlobal::config();
271     KConfigGroup titleConfig(config, "TitleWidget");
272
273     // Default is 2013 because there is also (perhaps interesting) information.
274     m_lastUnicodeNumber = titleConfig.readEntry("unicode_number", QString("2013"));
275 }
276
277 void UnicodeDialog::writeChoices()
278 {
279     // Get a pointer to a shared configuration instance, then get the TitleWidget group.
280     KSharedConfigPtr config = KGlobal::config();
281     KConfigGroup titleConfig(config, "TitleWidget");
282
283     titleConfig.writeEntry("unicode_number", m_lastUnicodeNumber);
284 }
285
286
287 /// SLOTS
288
289 /**
290  * \brief Validates the entered Unicode number and displays its Unicode character.
291  */
292 void UnicodeDialog::slotTextChanged(QString text)
293 {
294     unicodeNumber->blockSignals(true);
295
296     QString newText = validateText(text);
297     if (newText.length() == 0) {
298         unicodeChar->setText("");
299         unicodeNumber->setText("");
300         clearOverviewChars();
301         m_lastCursorPos = 0;
302         m_lastUnicodeNumber = "";
303         labelInfoText->setText(unicodeInfo(""));
304
305     } else {
306
307         int cursorPos = unicodeNumber->cursorPosition();
308
309         unicodeNumber->setText(newText);
310         unicodeNumber->setCursorPosition(cursorPos);
311
312         // Get the decimal number as uint to create the QChar from
313         bool ok;
314         uint value = 0;
315         switch (inputMethod) {
316         case InputHex:
317             value = newText.toUInt(&ok, 16);
318             break;
319         case InputDec:
320             value = newText.toUInt(&ok, 10);
321             break;
322         }
323         updateOverviewChars(value);
324
325         if (!ok) {
326             // Impossible! validateText never fails!
327         }
328
329         // If an invalid character has been entered:
330         // Reset the cursor position because the entered char has been deleted.
331         if (text != newText && newText == m_lastUnicodeNumber) {
332             unicodeNumber->setCursorPosition(m_lastCursorPos);
333         }
334
335         m_lastCursorPos = unicodeNumber->cursorPosition();
336         m_lastUnicodeNumber = newText;
337
338         labelInfoText->setText(unicodeInfo(newText));
339         unicodeChar->setText(QChar(value));
340     }
341
342     unicodeNumber->blockSignals(false);
343 }
344
345 /**
346  * When return pressed, we return the selected unicode character
347  * if it was not a control character.
348  */
349 void UnicodeDialog::slotReturnPressed()
350 {
351     QString text = trimmedUnicodeNumber(unicodeNumber->text());
352     if (!controlCharacter(text)) {
353         emit charSelected(unicodeChar->text());
354         writeChoices();
355     }
356     emit accept();
357 }
358
359 void UnicodeDialog::slotNextUnicode()
360 {
361     QString text = unicodeNumber->text();
362     unicodeNumber->setText(nextUnicode(text, Forward));
363 }
364
365 void UnicodeDialog::slotPrevUnicode()
366 {
367     QString text = unicodeNumber->text();
368     unicodeNumber->setText(nextUnicode(text, Backward));
369 }
370
371 void UnicodeDialog::wheelEvent(QWheelEvent * event)
372 {
373     if (frame->underMouse()) {
374         if (event->delta() > 0) slotNextUnicode();
375         else slotPrevUnicode();
376     }
377 }
378
379 #include "unicodedialog.moc"