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