00001
00002
00003 #include "mythvirtualkeyboard.h"
00004
00005
00006 #include <iostream>
00007
00008
00009 #include <QKeyEvent>
00010 #include <QDomDocument>
00011 #include <QFile>
00012
00013
00014 #include "mythmainwindow.h"
00015 #include "mythlogging.h"
00016 #include "mythfontproperties.h"
00017 #include "mythuihelper.h"
00018 #include "mythuibutton.h"
00019 #include "mythuitextedit.h"
00020 #include "mythcorecontext.h"
00021
00022
00023 #define LOC QString("MythUIVirtualKeyboard: ")
00024
00025 static const int numcomps = 95;
00026
00027 static const QString comps[numcomps][3] = {
00028 {"!", "!", (QChar)0xa1}, {"c", "/", (QChar)0xa2},
00029 {"l", "-", (QChar)0xa3}, {"o", "x", (QChar)0xa4},
00030 {"y", "-", (QChar)0xa5}, {"|", "|", (QChar)0xa6},
00031 {"s", "o", (QChar)0xa7}, {"\"", "\"", (QChar)0xa8},
00032 {"c", "o", (QChar)0xa9}, {"-", "a", (QChar)0xaa},
00033 {"<", "<", (QChar)0xab}, {"-", "|", (QChar)0xac},
00034 {"-", "-", (QChar)0xad}, {"r", "o", (QChar)0xae},
00035 {"^", "-", (QChar)0xaf}, {"^", "0", (QChar)0xb0},
00036 {"+", "-", (QChar)0xb1}, {"^", "2", (QChar)0xb2},
00037 {"^", "3", (QChar)0xb3}, {"/", "/", (QChar)0xb4},
00038 {"/", "u", (QChar)0xb5}, {"P", "!", (QChar)0xb6},
00039 {"^", ".", (QChar)0xb7}, {",", ",", (QChar)0xb8},
00040 {"^", "1", (QChar)0xb9}, {"_", "o", (QChar)0xba},
00041 {">", ">", (QChar)0xbb}, {"1", "4", (QChar)0xbc},
00042 {"1", "2", (QChar)0xbd}, {"3", "4", (QChar)0xbe},
00043 {"?", "?", (QChar)0xbf}, {"A", "`", (QChar)0xc0},
00044 {"A", "'", (QChar)0xc1}, {"A", "^", (QChar)0xc2},
00045 {"A", "~", (QChar)0xc3}, {"A", "\"", (QChar)0xc4},
00046 {"A", "*", (QChar)0xc5}, {"A", "E", (QChar)0xc6},
00047 {"C", ",", (QChar)0xc7}, {"E", "`", (QChar)0xc8},
00048 {"E", "'", (QChar)0xc9}, {"E", "^", (QChar)0xca},
00049 {"E", "\"", (QChar)0xcb}, {"I", "`", (QChar)0xcc},
00050 {"I", "'", (QChar)0xcd}, {"I", "^", (QChar)0xce},
00051 {"I", "\"", (QChar)0xcf}, {"D", "-", (QChar)0xd0},
00052 {"N", "~", (QChar)0xd1}, {"O", "`", (QChar)0xd2},
00053 {"O", "'", (QChar)0xd3}, {"O", "^", (QChar)0xd4},
00054 {"O", "~", (QChar)0xd5}, {"O", "\"", (QChar)0xd6},
00055 {"x", "x", (QChar)0xd7}, {"O", "/", (QChar)0xd8},
00056 {"U", "`", (QChar)0xd9}, {"U", "'", (QChar)0xda},
00057 {"U", "^", (QChar)0xdb}, {"U", "\"", (QChar)0xdc},
00058 {"Y", "'", (QChar)0xdd}, {"T", "H", (QChar)0xde},
00059 {"s", "s", (QChar)0xdf}, {"a", "`", (QChar)0xe0},
00060 {"a", "'", (QChar)0xe1}, {"a", "^", (QChar)0xe2},
00061 {"a", "~", (QChar)0xe3}, {"a", "\"", (QChar)0xe4},
00062 {"a", "*", (QChar)0xe5}, {"a", "e", (QChar)0xe6},
00063 {"c", ",", (QChar)0xe7}, {"e", "`", (QChar)0xe8},
00064 {"e", "'", (QChar)0xe9}, {"e", "^", (QChar)0xea},
00065 {"e", "\"", (QChar)0xeb}, {"i", "`", (QChar)0xec},
00066 {"i", "'", (QChar)0xed}, {"i", "^", (QChar)0xee},
00067 {"i", "\"", (QChar)0xef}, {"d", "-", (QChar)0xf0},
00068 {"n", "~", (QChar)0xf1}, {"o", "`", (QChar)0xf2},
00069 {"o", "'", (QChar)0xf3}, {"o", "^", (QChar)0xf4},
00070 {"o", "~", (QChar)0xf5}, {"o", "\"", (QChar)0xf6},
00071 {"-", ":", (QChar)0xf7}, {"o", "/", (QChar)0xf8},
00072 {"u", "`", (QChar)0xf9}, {"u", "'", (QChar)0xfa},
00073 {"u", "^", (QChar)0xfb}, {"u", "\"", (QChar)0xfc},
00074 {"y", "'", (QChar)0xfd}, {"t", "h", (QChar)0xfe},
00075 {"y", "\"", (QChar)0xff}
00076 };
00077
00078 MythUIVirtualKeyboard::MythUIVirtualKeyboard(MythScreenStack *parentStack, MythUITextEdit *parentEdit)
00079 : MythScreenType(parentStack, "MythUIVirtualKeyboard")
00080 {
00081 m_parentEdit = parentEdit;
00082
00083 m_shift = false;
00084 m_alt = false;
00085 m_lock = false;
00086
00087 m_lockButton = NULL;
00088 m_altButton = NULL;
00089 m_compButton = NULL;
00090 m_shiftRButton = NULL;
00091 m_shiftLButton = NULL;
00092
00093 m_composing = false;
00094
00095 if (m_parentEdit)
00096 m_preferredPos = m_parentEdit->GetKeyboardPosition();
00097 else
00098 m_preferredPos = VK_POSBELOWEDIT;
00099 }
00100
00101 MythUIVirtualKeyboard::~MythUIVirtualKeyboard(void)
00102 {
00103 }
00104
00105 bool MythUIVirtualKeyboard::Create()
00106 {
00107 if (!LoadWindowFromXML("keyboard/keyboard.xml", "keyboard", this))
00108 return false;
00109
00110 BuildFocusList();
00111
00112 loadKeyDefinitions(gCoreContext->GetLanguageAndVariant());
00113 updateKeys(true);
00114
00115 int screenWidth, screenHeight;
00116 float xmult, ymult;
00117 GetMythUI()->GetScreenSettings(screenWidth, xmult, screenHeight, ymult);
00118 MythRect editArea = m_parentEdit->GetArea();
00119 MythRect area = GetArea();
00120 MythPoint newPos;
00121
00122
00123 MythUIType *parentScreen = NULL;
00124 parentScreen = dynamic_cast<MythUIType *>(m_parentEdit->parent());
00125 if (parentScreen)
00126 {
00127 editArea.moveTopLeft(QPoint(editArea.x() + parentScreen->GetArea().x(),
00128 editArea.y() + parentScreen->GetArea().y()));
00129 }
00130
00131 switch (m_preferredPos)
00132 {
00133 case VK_POSABOVEEDIT:
00134 if (editArea.y() - area.height() - 5 > 0)
00135 {
00136 newPos = QPoint(editArea.x() + editArea.width() / 2 - area.width() / 2,
00137 editArea.y() - area.height() - 5);
00138 }
00139 else
00140 {
00141 newPos = QPoint(editArea.x() + editArea.width() / 2 - area.width() / 2,
00142 editArea.y() + editArea.height() + 5);
00143 }
00144 break;
00145
00146 case VK_POSTOPDIALOG:
00147 newPos = QPoint(screenWidth / 2 - area.width() / 2, 5);
00148 break;
00149
00150 case VK_POSBOTTOMDIALOG:
00151 newPos = QPoint(screenWidth / 2 - area.width() / 2, screenHeight - 5 - area.height());
00152 break;
00153
00154 case VK_POSCENTERDIALOG:
00155 newPos = QPoint(screenWidth / 2 - area.width() / 2, screenHeight / 2 - area.height() / 2);
00156 break;
00157
00158 default:
00159
00160 if (editArea.y() + editArea.height() + area.height() + 5 < screenHeight)
00161 {
00162 newPos = QPoint(editArea.x() + editArea.width() / 2 - area.width() / 2,
00163 editArea.y() + editArea.height() + 5);
00164 }
00165 else
00166 {
00167 newPos = QPoint(editArea.x() + editArea.width() / 2 - area.width() / 2,
00168 editArea.y() - area.height() - 5);
00169 }
00170 break;
00171 }
00172
00173
00174 if (newPos.x() < 5)
00175 newPos.setX(5);
00176 if (newPos.x() + area.width() + 5 > screenWidth)
00177 newPos.setX(screenWidth - area.width() - 5);
00178 if (newPos.y() < 5)
00179 newPos.setY(5);
00180 if (newPos.y() + area.height() + 5 > screenHeight)
00181 newPos.setY(screenHeight - area.height() - 5);
00182
00183 SetPosition(newPos);
00184
00185 return true;
00186 }
00187
00188 void MythUIVirtualKeyboard::loadKeyDefinitions(const QString &lang)
00189 {
00190 QString language = lang.toLower();
00191
00192 QString defFile = QString("keyboard/%1.xml").arg(language);
00193
00194 if (!GetMythUI()->FindThemeFile(defFile))
00195 {
00196 LOG(VB_GENERAL, LOG_ERR,
00197 "No keyboard definition file found for: " + language);
00198
00199
00200 defFile = "keyboard/en_us.xml";
00201 if (!GetMythUI()->FindThemeFile(defFile))
00202 {
00203 LOG(VB_GENERAL, LOG_ERR,
00204 "Cannot find definitions file: " + defFile);
00205 return;
00206 }
00207 }
00208
00209 LOG(VB_GENERAL, LOG_NOTICE, "Loading definitions from: " + defFile);
00210
00211 QDomDocument doc("keydefinitions");
00212 QFile file(defFile);
00213 if (!file.open(QIODevice::ReadOnly))
00214 {
00215 LOG(VB_GENERAL, LOG_ERR, "Failed to open definitions file: " + defFile);
00216 return;
00217 }
00218 if (!doc.setContent(&file))
00219 {
00220 LOG(VB_GENERAL, LOG_ERR,
00221 "Failed to parse definitions file: " + defFile);
00222 file.close();
00223 return;
00224 }
00225 file.close();
00226
00227 QDomElement docElem = doc.documentElement();
00228 QDomNode n = docElem.firstChild();
00229 while(!n.isNull())
00230 {
00231 QDomElement e = n.toElement();
00232 if(!e.isNull())
00233 {
00234 if (e.tagName() == "key")
00235 parseKey(e);
00236 }
00237 n = n.nextSibling();
00238 }
00239 }
00240
00241 void MythUIVirtualKeyboard::parseKey(const QDomElement &element)
00242 {
00243 QString left, right, up, down;
00244 QString normal, shift, alt, altshift;
00245
00246 QString name = element.attribute("name");
00247 QString type = element.attribute("type");
00248
00249 QDomNode n = element.firstChild();
00250 while(!n.isNull())
00251 {
00252 QDomElement e = n.toElement();
00253 if(!e.isNull())
00254 {
00255 if (e.tagName() == "move")
00256 {
00257 left = e.attribute("left");
00258 right = e.attribute("right");
00259 up = e.attribute("up");
00260 down = e.attribute("down");
00261 }
00262 else if (e.tagName() == "char")
00263 {
00264 normal = e.attribute("normal");
00265 shift = e.attribute("shift");
00266 alt = e.attribute("alt");
00267 altshift = e.attribute("altshift");
00268 }
00269 else
00270 LOG(VB_GENERAL, LOG_ERR, "Unknown element in key definition");
00271 }
00272 n = n.nextSibling();
00273 }
00274
00275 KeyDefinition key;
00276 key.name = name;
00277 key.type = type;
00278 key.left = left;
00279 key.right = right;
00280 key.up = up;
00281 key.down = down;
00282 key.normal = decodeChar(normal);
00283 key.alt = decodeChar(alt);
00284 key.shift = decodeChar(shift);
00285 key.altshift = decodeChar(altshift);
00286
00287 m_keyMap[name] = key;
00288 }
00289
00290 void MythUIVirtualKeyboard::updateKeys(bool connectSignals)
00291 {
00292 QList<MythUIType *> *children = GetAllChildren();
00293 for (int i = 0; i < children->size(); ++i)
00294 {
00295 MythUIButton *button = dynamic_cast<MythUIButton *>(children->at(i));
00296 if (button)
00297 {
00298 if (m_keyMap.contains(button->objectName()))
00299 {
00300 KeyDefinition key = m_keyMap.value(button->objectName());
00301 button->SetText(getKeyText(key));
00302
00303 if (connectSignals)
00304 {
00305 if (key.type == "shift")
00306 {
00307 if (!m_shiftLButton)
00308 m_shiftLButton = button;
00309 else if (!m_shiftRButton)
00310 m_shiftRButton = button;
00311
00312 button->SetLockable(true);
00313 connect(button, SIGNAL(Clicked()), SLOT(shiftClicked()));
00314 }
00315 else if (key.type == "char")
00316 connect(button, SIGNAL(Clicked()), SLOT(charClicked()));
00317 else if (key.type == "done")
00318 connect(button, SIGNAL(Clicked()), SLOT(returnClicked()));
00319 else if (key.type == "del")
00320 connect(button, SIGNAL(Clicked()), SLOT(delClicked()));
00321 else if (key.type == "lock")
00322 {
00323 m_lockButton = button;
00324 m_lockButton->SetLockable(true);
00325 connect(m_lockButton, SIGNAL(Clicked()), SLOT(lockClicked()));
00326 }
00327 else if (key.type == "alt")
00328 {
00329 m_altButton = button;
00330 m_altButton->SetLockable(true);
00331 connect(m_altButton, SIGNAL(Clicked()), SLOT(altClicked()));
00332 }
00333 else if (key.type == "comp")
00334 {
00335 m_compButton = button;
00336 m_compButton->SetLockable(true);
00337 connect(m_compButton, SIGNAL(Clicked()), SLOT(compClicked()));
00338 }
00339 else if (key.type == "moveleft")
00340 connect(button, SIGNAL(Clicked()), SLOT(moveleftClicked()));
00341 else if (key.type == "moveright")
00342 connect(button, SIGNAL(Clicked()), SLOT(moverightClicked()));
00343 else if (key.type == "back")
00344 connect(button, SIGNAL(Clicked()), SLOT(backClicked()));
00345 }
00346 }
00347 else
00348 LOG(VB_GENERAL, LOG_WARNING,
00349 QString("WARNING - Key '%1' not found in map")
00350 .arg(button->objectName()));
00351 }
00352 }
00353 }
00354
00355 bool MythUIVirtualKeyboard::keyPressEvent(QKeyEvent *e)
00356 {
00357 bool handled = false;
00358 QStringList actions;
00359 handled = GetMythMainWindow()->TranslateKeyPress("TV Frontend", e, actions);
00360
00361 if (handled)
00362 return true;
00363
00364 bool keyFound = false;
00365 KeyDefinition key;
00366 if (GetFocusWidget())
00367 {
00368 if (m_keyMap.contains(GetFocusWidget()->objectName()))
00369 {
00370 key = m_keyMap.value(GetFocusWidget()->objectName());
00371 keyFound = true;;
00372 }
00373 }
00374
00375 for (int i = 0; i < actions.size() && !handled; i++)
00376 {
00377 QString action = actions[i];
00378 handled = true;
00379
00380 if (action == "UP")
00381 {
00382 if (keyFound)
00383 SetFocusWidget(GetChild(key.up));
00384 }
00385 else if (action == "DOWN")
00386 {
00387 if (keyFound)
00388 SetFocusWidget(GetChild(key.down));
00389 }
00390 else if (action == "LEFT")
00391 {
00392 if (keyFound)
00393 SetFocusWidget(GetChild(key.left));
00394 }
00395 else if (action == "RIGHT")
00396 {
00397 if (keyFound)
00398 SetFocusWidget(GetChild(key.right));
00399 }
00400 else
00401 handled = false;
00402 }
00403
00404 if (!handled && MythScreenType::keyPressEvent(e))
00405 handled = true;
00406
00407 return handled;
00408 }
00409
00410 void MythUIVirtualKeyboard::charClicked(void)
00411 {
00412 if (!GetFocusWidget())
00413 return;
00414
00415 KeyDefinition key = m_keyMap.value(GetFocusWidget()->objectName());
00416 QString c = getKeyText(key);
00417
00418 if (m_composing)
00419 {
00420 if (m_composeStr.isEmpty())
00421 m_composeStr = c;
00422 else
00423 {
00424
00425 for (int i = 0; i < numcomps; i++)
00426 {
00427 if ((m_composeStr == comps[i][0]) && (c == comps[i][1]))
00428 {
00429 c = comps[i][2];
00430
00431 emit keyPressed(c);
00432
00433 if (m_parentEdit)
00434 {
00435 QKeyEvent *event = new QKeyEvent(QEvent::KeyPress, 0, Qt::NoModifier, c);
00436 m_parentEdit->keyPressEvent(event);
00437 }
00438
00439 break;
00440 }
00441 }
00442
00443 m_composeStr.clear();
00444 m_composing = false;
00445 if (m_compButton)
00446 m_compButton->SetLocked(false);
00447 }
00448 }
00449 else
00450 {
00451 emit keyPressed(c);
00452
00453 if (m_parentEdit)
00454 {
00455 QKeyEvent *event = new QKeyEvent(QEvent::KeyPress, 0, Qt::NoModifier, c);
00456 m_parentEdit->keyPressEvent(event);
00457 }
00458
00459 if (m_shift && !m_lock)
00460 {
00461 m_shift = false;
00462 if (m_shiftLButton)
00463 m_shiftLButton->SetLocked(false);
00464 if (m_shiftRButton)
00465 m_shiftRButton->SetLocked(false);
00466
00467 updateKeys();
00468 }
00469 }
00470 }
00471
00472 void MythUIVirtualKeyboard::shiftClicked(void)
00473 {
00474 m_shift = !m_shift;
00475
00476 if (m_shiftLButton)
00477 m_shiftLButton->SetLocked(m_shift);
00478 if (m_shiftRButton)
00479 m_shiftRButton->SetLocked(m_shift);
00480 if (m_lockButton && m_lock)
00481 {
00482 m_lockButton->SetLocked(false);
00483 m_lock = false;
00484 }
00485
00486 updateKeys();
00487 }
00488
00489 void MythUIVirtualKeyboard::delClicked(void)
00490 {
00491 emit keyPressed("{DELETE}");
00492
00493 if (m_parentEdit)
00494 {
00495
00496 QKeyEvent *event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Backspace, Qt::NoModifier, "");
00497 m_parentEdit->keyPressEvent(event);
00498 }
00499 }
00500
00501 void MythUIVirtualKeyboard::backClicked(void)
00502 {
00503 emit keyPressed("{BACK}");
00504
00505 if (m_parentEdit)
00506 {
00507 QKeyEvent *event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Backspace, Qt::NoModifier, "");
00508 m_parentEdit->keyPressEvent(event);
00509 }
00510 }
00511
00512 void MythUIVirtualKeyboard::lockClicked(void)
00513 {
00514 m_lock = !m_lock;
00515 m_shift = m_lock;
00516
00517 if (m_shiftLButton)
00518 m_shiftLButton->SetLocked(m_shift);
00519 if (m_shiftRButton)
00520 m_shiftRButton->SetLocked(m_shift);
00521
00522 updateKeys();
00523 }
00524
00525 void MythUIVirtualKeyboard::altClicked(void)
00526 {
00527 m_alt = !m_alt;
00528
00529 updateKeys();
00530 }
00531
00532 void MythUIVirtualKeyboard::compClicked(void)
00533 {
00534 m_composing = !m_composing;
00535 m_composeStr.clear();
00536 }
00537
00538 void MythUIVirtualKeyboard::returnClicked(void)
00539 {
00540 Close();
00541 }
00542
00543 void MythUIVirtualKeyboard::moveleftClicked(void)
00544 {
00545 emit keyPressed("{MOVELEFT}");
00546
00547 if (m_parentEdit)
00548 {
00549 QKeyEvent *event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Left, Qt::NoModifier, "");
00550 m_parentEdit->keyPressEvent(event);
00551 }
00552 }
00553
00554 void MythUIVirtualKeyboard::moverightClicked(void)
00555 {
00556 emit keyPressed("{MOVERIGHT}");
00557
00558 if (m_parentEdit)
00559 {
00560 QKeyEvent *event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier, "");
00561 m_parentEdit->keyPressEvent(event);
00562 }
00563 }
00564
00565 QString MythUIVirtualKeyboard::decodeChar(QString c)
00566 {
00567 QString res;
00568
00569 while (c.length() > 0)
00570 {
00571 if (c.startsWith("0x"))
00572 {
00573 QString sCode = c.left(6);
00574 bool bOK;
00575 short nCode = sCode.toShort(&bOK, 16);
00576
00577 c = c.mid(6);
00578
00579 if (bOK)
00580 {
00581 QChar uc(nCode);
00582 res += QString(uc);
00583 }
00584 else
00585 LOG(VB_GENERAL, LOG_ERR, QString("bad char code (%1)")
00586 .arg(sCode));
00587 }
00588 else
00589 {
00590 res += c.left(1);
00591 c = c.mid(1);
00592 }
00593 }
00594
00595 return res;
00596 }
00597
00598 QString MythUIVirtualKeyboard::getKeyText(KeyDefinition key)
00599 {
00600
00601 if (m_shift)
00602 {
00603 if (m_alt)
00604 return key.altshift;
00605 else
00606 return key.shift;
00607 }
00608
00609 if (m_alt)
00610 return key.alt;
00611
00612 return key.normal;
00613 }