moved kdeaccessibility kdeaddons kdeadmin kdeartwork kdebindings kdeedu kdegames...
[kdeedu.git] / ktouch / src / ktouchkeyboard.cpp
blob8e9b7e91a4ce45df212ba59c7c2be73401fc1881
1 /***************************************************************************
2 * ktouchkeyboard.cpp *
3 * ------------------ *
4 * Copyright (C) 2000 by Håvard Frøiland, 2004 by Andreas Nicolai *
5 * ghorwin@users.sourceforge.net *
6 * *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
11 ***************************************************************************/
13 #include "ktouchkeyboard.h"
14 #include "ktouchkeyboard.moc"
16 #include <algorithm>
18 #include <qfile.h>
20 #include <kdebug.h>
21 #include <ktempfile.h>
22 #include <klocale.h>
23 #include <kio/netaccess.h>
24 #include <kstandarddirs.h>
25 #include <kmessagebox.h>
27 #include "prefs.h"
29 // the margin between keyboard and widget frame
30 const int MARGIN = 10;
32 // --------------------------------------------------------------------------
35 KTouchKeyboard::KTouchKeyboard(QWidget *parent)
36 : QWidget(parent), m_keyboardWidth(100), m_keyboardHeight(60), m_currentLayout("")
38 setMinimumHeight(100); // when it's smaller you won't see anything
39 m_keyList.setAutoDelete(true); // the list is responsable for cleaning up
43 bool KTouchKeyboard::loadKeyboard(QWidget * window, const KURL& url, QString* errorMsg) {
44 QString target;
45 if (KIO::NetAccess::download(url, target, window)) {
46 QString msg;
47 bool result = readKeyboard(target, msg);
48 KIO::NetAccess::removeTempFile(target);
49 if (!result && errorMsg!=NULL)
50 *errorMsg = i18n("Could not read the keyboard layout file '%1'. ").arg(url.url()) + msg;
51 return result;
53 else {
54 if (errorMsg!=NULL)
55 *errorMsg = i18n("Could not download/open keyboard layout file from '%1'.").arg(url.url());
56 return false;
61 void KTouchKeyboard::saveKeyboard(QWidget * window, const KURL& url) {
62 QString tmpFile;
63 KTempFile *temp=0;
64 if (url.isLocalFile())
65 tmpFile=url.path(); // for local files the path is sufficient
66 else {
67 temp=new KTempFile; // for remote files create a temporary file first
68 tmpFile=temp->name();
71 QFile outfile(tmpFile);
72 if ( !outfile.open( IO_WriteOnly ) ) {
73 if (temp) delete temp;
74 return;
77 QTextStream out( &outfile );
78 out << "########################################## \n";
79 out << "# # \n";
80 out << "# Keyboard layout file for KTouch # \n";
81 out << "# # \n";
82 out << "########################################## \n";
83 out << "#\n";
84 out << endl;
86 for (KTouchKey * key = m_keyList.first(); key; key = m_keyList.next()) {
87 switch (key->type()) {
88 case KTouchKey::FINGER_KEY : out << "FingerKey "; break;
89 case KTouchKey::NORMAL_KEY : out << "NormalKey "; break;
90 case KTouchKey::CONTROL_KEY : out << "ControlKey "; break;
91 default : out << "NormalKey "; break;
93 QRect rect=key->frame();
94 out << key->m_keyChar.unicode() << '\t' << key->m_keyText << '\t'
95 << rect.left() << '\t' << rect.top() << '\t' << rect.width() << '\t' << rect.height() << endl;
98 if (temp) {
99 KIO::NetAccess::upload(tmpFile, url, window);
100 temp->unlink();
101 delete temp;
105 void KTouchKeyboard::applyPreferences(QWidget * window, bool silent) {
106 // let's check whether the keyboard layout has changed
107 if (Prefs::currentKeyboardFile() != m_currentLayout) {
108 // if the layout is the number layout just create it and we're done
109 kdDebug() << "[KTouchKeyboard::applyPreferences] keyboard = " << Prefs::currentKeyboardFile() << endl;
110 if (Prefs::currentKeyboardFile()=="number.keyboard") {
111 createDefaultKeyboard();
112 resizeEvent(NULL);
113 return;
115 // ok, let's load this layout
116 if (silent) {
117 // during initialisation we don't want to have a message box, that's why this is silent
118 if (!loadKeyboard(window, KURL::fromPathOrURL( Prefs::currentKeyboardFile() )))
119 createDefaultKeyboard();
120 else
121 m_currentLayout=Prefs::currentKeyboardFile();
123 else {
124 QString errorMsg;
125 if (!loadKeyboard(window, KURL::fromPathOrURL( Prefs::currentKeyboardFile() ), &errorMsg)) {
126 KMessageBox::error( 0, i18n("Error reading the keyboard layout; the default number keypad will "
127 "be created instead. You can choose another keyboard layout in the preferences dialog."),
128 errorMsg);
129 createDefaultKeyboard();
131 else
132 m_currentLayout=Prefs::currentKeyboardFile();
135 updateColours(); // we recreate the colour connections
136 // assign keyboard font to keys
137 for (KTouchKey * key = m_keyList.first(); key; key = m_keyList.next()) {
138 if (Prefs::overrideKeyboardFont())
139 key->m_font = Prefs::keyboardFont();
140 else
141 key->m_font = Prefs::font();
143 // kdDebug() << "[KTouchKeyboard::applyPreferences] Assigned key font" << endl;
144 resizeEvent(NULL); // paint the keyboard
145 newKey(m_nextKey); // and finally display the "next to be pressed" key again
149 void KTouchKeyboard::newKey(const QChar& nextChar) {
150 QPainter painter(this);
151 painter.translate(m_shift, MARGIN);
152 // first clean the markings on all keys
153 for (KTouchKey * key = m_keyList.first(); key; key = m_keyList.next()) {
154 if (key->m_isActive || key->m_isNextKey) {
155 key->m_isActive=key->m_isNextKey=false;
156 key->paint(painter);
160 if (Prefs::showAnimation()){ // only do this if we want to show animation.
161 // find the key in the key connector list
162 QValueList<KTouchKeyConnector>::iterator keyIt = m_connectorList.begin();
163 while (keyIt!=m_connectorList.end() && (*keyIt).m_keyChar!=nextChar) ++keyIt;
164 // if found mark the appropriate keys
165 if (keyIt!=m_connectorList.end()) {
166 QChar targetChar = (*keyIt).m_targetKeyChar;
167 QChar fingerChar = (*keyIt).m_fingerKeyChar;
168 QChar controlChar = (*keyIt).m_controlKeyChar;
169 // find the keys in the keylist
170 for (KTouchKey * key = m_keyList.first(); key; key = m_keyList.next()) {
171 if (key->m_keyChar==QChar(0)) continue; // skip decorative keys
172 if (key->m_keyChar==targetChar) key->m_isNextKey=true;
173 else if (key->m_keyChar==fingerChar) key->m_isActive=true;
174 else if (key->m_keyChar==controlChar) key->m_isActive=true;
175 if (key->m_isActive || key->m_isNextKey)
176 key->paint(painter);
179 m_nextKey = nextChar;
184 void KTouchKeyboard::paintEvent(QPaintEvent *) {
185 QPainter painter(this);
186 painter.translate(m_shift, MARGIN);
187 // just print all visible keys
188 for (KTouchKey * key = m_keyList.first(); key; key = m_keyList.next())
189 key->paint(painter);
193 void KTouchKeyboard::resizeEvent(QResizeEvent *) {
194 // kdDebug() << "[KTouchKeyboard::resizeEvent] Window = " << width() << "x" << height() << endl;
195 // kdDebug() << "[KTouchKeyboard::resizeEvent] Keyboard = " << m_keyboardWidth << "x" << m_keyboardHeight << endl;
196 double hScale = static_cast<double>(width()-2*MARGIN)/m_keyboardWidth;
197 double vScale = static_cast<double>(height()-2*MARGIN)/m_keyboardHeight;
198 double scale = std::max(1.0, std::min(hScale, vScale));
199 // kdDebug() << "[KTouchKeyboard::resizeEvent] using scale = " << scale << endl;
200 m_shift = (width() - static_cast<int>(m_keyboardWidth*scale))/2;
201 for (KTouchKey * key = m_keyList.first(); key; key = m_keyList.next())
202 key->resize(scale); // resize all keys
203 update(); // and finally redraw the keyboard
207 void KTouchKeyboard::createDefaultKeyboard() {
208 // let's create a default keyboard
209 const int keySpacing = 4;
210 const int keyHeight = 20;
211 const int keyWidth = 20;
212 const int col = keyWidth+keySpacing;
213 const int row = keyHeight+keySpacing;
214 // first let's create the "visible" keys, that means all keys that will be displayed
215 // Note: purely decorative keys get a key char code of 0!
216 m_keyList.clear();
217 m_keyList.append( new KTouchControlKey( 0, "Num", 0, 0, keyWidth, keyHeight) );
218 m_keyList.append( new KTouchNormalKey( '/', "/", col, 0, keyWidth, keyHeight) );
219 m_keyList.append( new KTouchNormalKey( '*', "*", 2*col, 0, keyWidth, keyHeight) );
220 m_keyList.append( new KTouchNormalKey( '-', "-", 3*col, 0, keyWidth, keyHeight) );
221 m_keyList.append( new KTouchNormalKey( '7', "7", 0, row, keyWidth, keyHeight) );
222 m_keyList.append( new KTouchNormalKey( '8', "8", col, row, keyWidth, keyHeight) );
223 m_keyList.append( new KTouchNormalKey( '9', "9", 2*col, row, keyWidth, keyHeight) );
224 m_keyList.append( new KTouchFingerKey( '4', "4", 0, 2*row, keyWidth, keyHeight) );
225 m_keyList.append( new KTouchFingerKey( '5', "5", col, 2*row, keyWidth, keyHeight) );
226 m_keyList.append( new KTouchFingerKey( '6', "6", 2*col, 2*row, keyWidth, keyHeight) );
227 m_keyList.append( new KTouchNormalKey( '1', "1", 0, 3*row, keyWidth, keyHeight) );
228 m_keyList.append( new KTouchNormalKey( '2', "2", col, 3*row, keyWidth, keyHeight) );
229 m_keyList.append( new KTouchNormalKey( '3', "3", 2*col, 3*row, keyWidth, keyHeight) );
230 m_keyList.append( new KTouchNormalKey( '0', "0", 0, 4*row, 2*keyWidth+keySpacing, keyHeight) );
231 m_keyList.append( new KTouchNormalKey( '.', ".", 2*col, 4*row, keyWidth, keyHeight) );
232 m_keyList.append( new KTouchFingerKey( '+', "+", 3*col, row, keyWidth, 2*keyHeight+keySpacing) );
233 m_keyList.append( new KTouchControlKey(13, "Enter", 3*col, 3*row,keyWidth, 2*keyHeight+keySpacing) );
234 m_keyList.append( new KTouchControlKey(8, "BackSpace", 5*col, 0, 2*keyWidth+keySpacing, keyHeight) );
235 m_keyboardWidth = 7*col;
236 m_keyboardHeight = 5*row;
237 // now we need to create the connections between the characters that can be typed and the
238 // keys that need to be displayed on the keyboard
239 // The arguments to the constructor are: keychar, targetkey, fingerkey, controlkey
240 m_connectorList.clear();
241 m_connectorList.append( KTouchKeyConnector('/', '/', '5', 0) );
242 m_connectorList.append( KTouchKeyConnector('*', '*', '6', 0) );
243 m_connectorList.append( KTouchKeyConnector('-', '-', '+', 0) );
244 m_connectorList.append( KTouchKeyConnector('+', '+', 0, 0) );
245 m_connectorList.append( KTouchKeyConnector('.', '.', '6', 0) );
246 m_connectorList.append( KTouchKeyConnector(',', '.', '6', 0) );
247 m_connectorList.append( KTouchKeyConnector('7', '7', '4', 0) );
248 m_connectorList.append( KTouchKeyConnector('8', '8', '5', 0) );
249 m_connectorList.append( KTouchKeyConnector('9', '9', '6', 0) );
250 m_connectorList.append( KTouchKeyConnector('4', '4', 0, 0) );
251 m_connectorList.append( KTouchKeyConnector('5', '5', 0, 0) );
252 m_connectorList.append( KTouchKeyConnector('6', '6', 0, 0) );
253 m_connectorList.append( KTouchKeyConnector('1', '1', '4', 0) );
254 m_connectorList.append( KTouchKeyConnector('2', '2', '5', 0) );
255 m_connectorList.append( KTouchKeyConnector('3', '3', '6', 0) );
256 m_connectorList.append( KTouchKeyConnector('0', '0', 0, 0) );
257 m_connectorList.append( KTouchKeyConnector( 8, 8, 0, 0) );
258 m_connectorList.append( KTouchKeyConnector( 13, 13, '+', 0) );
259 updateColours();
260 m_currentLayout="number.keyboard";
264 bool KTouchKeyboard::readKeyboard(const QString& fileName, QString& errorMsg) {
265 QFile infile(fileName);
266 if ( !infile.open( IO_ReadOnly ) ) {
267 errorMsg = i18n("Could not open file.");
268 return false;
270 QTextStream in( &infile );
271 in.setEncoding(QTextStream::UnicodeUTF8);
272 QString line;
273 m_keyList.clear(); // empty the keyboard
274 m_connectorList.clear(); // clear the connections
275 m_keyboardWidth=0;
276 m_keyboardHeight=0;
277 // now loop until end of file is reached
278 do {
279 // skip all empty lines or lines containing a comment (starting with '#')
280 do { line = in.readLine().stripWhiteSpace(); }
281 while (!line.isNull() && (line.isEmpty() || line[0]=='#'));
282 // Check if end of file encountered and if that is the case -> bail out at next while
283 if (line.isNull()) continue;
285 // 'line' should now contain a key specification
286 QTextStream lineStream(line, IO_ReadOnly);
287 QString keyType;
288 int keyAscII;
289 QString keyText;
290 int x(0), y(0), w(0), h(0);
291 lineStream >> keyType >> keyAscII;
292 if (keyType=="FingerKey") {
293 lineStream >> keyText >> x >> y >> w >> h;
294 if (w==0 || h==0)
295 w=h=8; // default values for old keyboard files
296 m_keyList.append( new KTouchFingerKey(keyAscII, keyText, x+1, y+1, w, h) );
297 m_connectorList.append( KTouchKeyConnector(keyAscII, keyAscII, 0, 0) );
299 else if (keyType=="ControlKey") {
300 lineStream >> keyText >> x >> y >> w >> h;
301 m_keyList.append( new KTouchControlKey(keyAscII, keyText, x+1, y+1, w-2, h-2) );
302 m_connectorList.append( KTouchKeyConnector(keyAscII, keyAscII, 0, 0) );
304 else if (keyType=="NormalKey") {
305 int fingerCharCode;
306 lineStream >> keyText >> x >> y >> fingerCharCode;
307 w=h=8; // default values for old keyboard files
308 // retrieve the finger key with the matching char
309 m_keyList.append( new KTouchNormalKey(keyAscII, keyText, x+1, y+1, w, h) );
310 m_connectorList.append( KTouchKeyConnector(keyAscII, keyAscII, fingerCharCode, 0) );
311 } else if (keyType=="HiddenKey") {
312 int targetChar, fingerChar, controlChar;
313 lineStream >> targetChar >> fingerChar >> controlChar;
314 m_connectorList.append( KTouchKeyConnector(keyAscII, targetChar, fingerChar, controlChar) );
316 else {
317 errorMsg = i18n("Missing key type in line '%1'.").arg(line);
318 return false;
320 // calculate the maximum extent of the keyboard on the fly...
321 m_keyboardWidth = std::max(m_keyboardWidth, x+w);
322 m_keyboardHeight = std::max(m_keyboardHeight, y+h);
323 } while (!in.atEnd() && !line.isNull());
324 updateColours();
325 return (!m_keyList.isEmpty()); // empty file means error
329 void KTouchKeyboard::updateColours() {
330 // loop over all key connections
331 for (QValueList<KTouchKeyConnector>::iterator it = m_connectorList.begin(); it!=m_connectorList.end(); ++it) {
332 QChar fingerChar = (*it).m_fingerKeyChar;
333 if (fingerChar == QChar(0)) continue;
334 QChar targetChar = (*it).m_targetKeyChar;
335 KTouchKey * self=NULL;
336 KTouchKey * colorSource=NULL;
337 // loop over all keys to find the key pointers
338 for (KTouchKey * key = m_keyList.first(); key; key = m_keyList.next()) {
339 if (key->m_keyChar==targetChar) self=key;
340 else if (key->m_keyChar==fingerChar) colorSource=key;
342 if (self && colorSource) {
343 if (self->type()!=KTouchKey::NORMAL_KEY)
344 continue;
345 KTouchNormalKey *nk = dynamic_cast<KTouchNormalKey*>(self);
346 if (colorSource->type()!=KTouchKey::FINGER_KEY) {
347 kdDebug() << "[KTouchKeyboard::updateColours] Colour source key '" << colorSource->m_keyText
348 << "' is not a finger key!" << endl;
349 if (nk) {
350 nk->m_colorIndex = 0;
352 continue;
354 if (nk) {
355 KTouchFingerKey *fk = dynamic_cast<KTouchFingerKey*>(colorSource);
356 if (fk) {
357 nk->m_colorIndex = fk->m_colorIndex;