Ticket #9613: multikeybindings_v1.patch
File multikeybindings_v1.patch, 17.2 KB (added by , 13 years ago) |
---|
-
mythtv/libs/libmythtv/dbcheck.cpp
diff --git a/mythtv/libs/libmythtv/dbcheck.cpp b/mythtv/libs/libmythtv/dbcheck.cpp index f21ec29..62e16a7 100644
a b tmp.constData(), 5970 5970 " description varchar(255) default NULL," 5971 5971 " keylist varchar(128) default NULL," 5972 5972 " hostname varchar(64) NOT NULL default ''," 5973 " multikey varchar(128) default NULL," 5973 5974 " PRIMARY KEY (`context`,`action`,hostname)" 5974 5975 ");", 5975 5976 "CREATE TABLE keyword (" -
mythtv/libs/libmythtv/tv_play.cpp
diff --git a/mythtv/libs/libmythtv/tv_play.cpp b/mythtv/libs/libmythtv/tv_play.cpp index 3b8542c..e4f3c3d 100644
a b TV::TV(void) 873 873 endOfPlaybackTimerId(0), embedCheckTimerId(0), 874 874 endOfRecPromptTimerId(0), videoExitDialogTimerId(0), 875 875 pseudoChangeChanTimerId(0), speedChangeTimerId(0), 876 errorRecoveryTimerId(0), exitPlayerTimerId(0) 876 errorRecoveryTimerId(0), exitPlayerTimerId(0), 877 multikeyContext(new MultikeyContext()) 877 878 { 878 879 VERBOSE(VB_GENERAL, LOC + "Creating TV object"); 879 880 ctorTime.start(); … … TV::~TV(void) 1211 1212 } 1212 1213 ReturnPlayerLock(mctx); 1213 1214 1215 if (multikeyContext) 1216 { 1217 delete multikeyContext; 1218 multikeyContext = NULL; 1219 } 1220 1214 1221 GetMythMainWindow()->GetPaintWindow()->show(); 1215 1222 1216 1223 VERBOSE(VB_PLAYBACK, "TV::~TV() -- end"); … … bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 3296 3303 if (ignoreKeys) 3297 3304 { 3298 3305 handled = GetMythMainWindow()->TranslateKeyPress( 3299 "TV Playback", e, actions );3306 "TV Playback", e, actions, true, multikeyContext); 3300 3307 3301 3308 if (handled || actions.isEmpty()) 3309 { 3310 CommitMultikey(actx); 3302 3311 return true; 3312 } 3303 3313 3304 3314 bool esc = has_action("ESCAPE", actions) || 3305 3315 has_action("BACK", actions); … … bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 3307 3317 bool play = has_action(ACTION_PLAY, actions); 3308 3318 3309 3319 if ((!esc || browsehelper->IsBrowsing()) && !pause && !play) 3320 { 3321 CommitMultikey(actx); 3310 3322 return false; 3323 } 3311 3324 } 3312 3325 3313 3326 OSD *osd = GetOSDLock(actx); … … bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 3321 3334 if (editmode && !handled) 3322 3335 { 3323 3336 handled |= GetMythMainWindow()->TranslateKeyPress( 3324 "TV Editing", e, actions );3337 "TV Editing", e, actions, true, multikeyContext); 3325 3338 3326 3339 if (!handled) 3327 3340 { … … bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 3365 3378 } 3366 3379 3367 3380 if (handled) 3381 { 3382 CommitMultikey(actx); 3368 3383 return true; 3384 } 3369 3385 3370 3386 // If text is already queued up, be more lax on what is ok. 3371 3387 // This allows hex teletext entry and minor channel entry. … … bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 3387 3403 { 3388 3404 QStringList tt_actions; 3389 3405 handled = GetMythMainWindow()->TranslateKeyPress( 3390 "Teletext Menu", e, tt_actions );3406 "Teletext Menu", e, tt_actions, true, multikeyContext); 3391 3407 3392 3408 if (!handled && !tt_actions.isEmpty()) 3393 3409 { … … bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 3407 3423 { 3408 3424 QStringList itv_actions; 3409 3425 handled = GetMythMainWindow()->TranslateKeyPress( 3410 "TV Playback", e, itv_actions );3426 "TV Playback", e, itv_actions, true, multikeyContext); 3411 3427 3412 3428 if (!handled && !itv_actions.isEmpty()) 3413 3429 { … … bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 3416 3432 if (actx->player->ITVHandleAction(itv_actions[i])) 3417 3433 { 3418 3434 actx->UnlockDeletePlayer(__FILE__, __LINE__); 3435 CommitMultikey(actx); 3419 3436 return true; 3420 3437 } 3421 3438 } … … bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 3424 3441 actx->UnlockDeletePlayer(__FILE__, __LINE__); 3425 3442 3426 3443 handled = GetMythMainWindow()->TranslateKeyPress( 3427 "TV Playback", e, actions );3444 "TV Playback", e, actions, true, multikeyContext); 3428 3445 3429 3446 if (handled || actions.isEmpty()) 3447 { 3448 CommitMultikey(actx); 3430 3449 return true; 3450 } 3431 3451 3432 3452 handled = false; 3433 3453 … … bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 3454 3474 #endif // DEBUG_ACTIONS 3455 3475 3456 3476 if (handled) 3477 { 3478 CommitMultikey(actx); 3457 3479 return true; 3480 } 3458 3481 3459 3482 if (!handled) 3460 3483 { … … bool TV::ProcessKeypress(PlayerContext *actx, QKeyEvent *e) 3475 3498 return true; 3476 3499 } 3477 3500 3501 void TV::CommitMultikey(PlayerContext *ctx) 3502 { 3503 MultikeyContext::MultikeyStatus status; 3504 QString prefix; 3505 multikeyContext->Commit(status, prefix); 3506 switch (status) 3507 { 3508 case MultikeyContext::kMKnoPrefix: 3509 // clear the OSD text 3510 HideOSDWindow(ctx, "osd_input"); 3511 break; 3512 case MultikeyContext::kMKpartialMatch: 3513 SetOSDText(ctx, "osd_input", "osd_number_entry", prefix, kOSDTimeout_Med); 3514 break; 3515 case MultikeyContext::kMKfullMatch: 3516 // same as partial match but with short timeout 3517 SetOSDText(ctx, "osd_input", "osd_number_entry", prefix, kOSDTimeout_Short); 3518 break; 3519 case MultikeyContext::kMKbadMatch: 3520 // same as full match but with error text 3521 SetOSDText(ctx, "osd_input", "osd_number_entry", 3522 QObject::tr("Unknown key sequence: ") + prefix, kOSDTimeout_Short); 3523 break; 3524 } 3525 } 3526 3478 3527 bool TV::BrowseHandleAction(PlayerContext *ctx, const QStringList &actions) 3479 3528 { 3480 3529 if (!browsehelper->IsBrowsing()) -
mythtv/libs/libmythtv/tv_play.h
diff --git a/mythtv/libs/libmythtv/tv_play.h b/mythtv/libs/libmythtv/tv_play.h index 2c42abc..ff845cb 100644
a b using namespace std; 40 40 41 41 class QDateTime; 42 42 class OSD; 43 class MultikeyContext; 43 44 class RemoteEncoder; 44 45 class MythPlayer; 45 46 class DetectLetterbox; … … class MTV_PUBLIC TV : public QObject 385 386 void ClearInputQueues(const PlayerContext*, bool hideosd); 386 387 bool CommitQueuedInput(PlayerContext*); 387 388 bool ProcessSmartChannel(const PlayerContext*, QString&); 389 void CommitMultikey(PlayerContext*); 388 390 389 391 // query key queues 390 392 bool HasQueuedInput(void) const … … class MTV_PUBLIC TV : public QObject 841 843 TimerContextMap signalMonitorTimerId; 842 844 TimerContextMap tvchainUpdateTimerId; 843 845 846 MultikeyContext *multikeyContext; 847 844 848 public: 845 849 // Constants 846 850 static const int kInitFFRWSpeed; ///< 1x, default to normal speed -
mythtv/libs/libmythui/mythmainwindow.cpp
diff --git a/mythtv/libs/libmythui/mythmainwindow.cpp b/mythtv/libs/libmythui/mythmainwindow.cpp index 7ecb158..0c44ebd 100644
a b class KeyContext 104 104 return false; 105 105 } 106 106 107 void AddMultikeyMapping(const QString &multikey, QString action) 108 { 109 QKeySequence seq(multikey); 110 switch (seq.count()) 111 { 112 case 4: 113 ++multikeyPrefixes[QKeySequence(seq[0], seq[1], seq[2])]; 114 // fallthrough 115 case 3: 116 ++multikeyPrefixes[QKeySequence(seq[0], seq[1])]; 117 // fallthrough 118 case 2: 119 ++multikeyPrefixes[QKeySequence(seq[0])]; 120 // fallthrough 121 case 1: 122 multikeyActionMap[seq].append(action); 123 } 124 } 125 126 bool IsMultikeyPrefix(const QString &multikey) 127 { 128 return multikeyPrefixes.count(QKeySequence(multikey)); 129 } 130 131 bool GetMultikeyMapping(const QString &multikey, QStringList &actions) 132 { 133 QKeySequence seq(multikey); 134 if (multikeyActionMap.count(seq) > 0) 135 { 136 actions += multikeyActionMap[seq]; 137 return true; 138 } 139 return false; 140 } 141 142 QMap<QKeySequence, QStringList> multikeyActionMap; 143 QMap<QKeySequence, int> multikeyPrefixes; 107 144 QMap<int, QStringList> actionMap; 108 145 }; 109 146 … … class MythMainWindowPrivate 264 301 bool m_pendingUpdate; 265 302 }; 266 303 304 // Returns false if the key couldn't be added (prefix too long). 305 bool MultikeyContext::AddKey(int key) 306 { 307 currentPrefix = oldPrefix; 308 bool result = true; 309 QKeySequence seq(currentPrefix); 310 int k1 = seq[0]; 311 int k2 = seq[1]; 312 int k3 = seq[2]; 313 int k4 = seq[3]; 314 if (k1 == 0) 315 k1 = key; 316 else if (k2 == 0) 317 k2 = key; 318 else if (k3 == 0) 319 k3 = key; 320 else if (k4 == 0) 321 k4 = key; 322 else 323 result = false; 324 if (result) 325 currentPrefix = QKeySequence(k1, k2, k3, k4).toString(); 326 return result; 327 } 328 267 329 // Make keynum in QKeyEvent be equivalent to what's in QKeySequence 268 330 int MythMainWindowPrivate::TranslateKeyNum(QKeyEvent* e) 269 331 { … … void MythMainWindow::ExitToMainMenu(void) 1446 1508 */ 1447 1509 bool MythMainWindow::TranslateKeyPress(const QString &context, 1448 1510 QKeyEvent *e, QStringList &actions, 1449 bool allowJumps) 1511 bool allowJumps, 1512 MultikeyContext *prefixContext) 1450 1513 { 1451 1514 actions.clear(); 1452 1515 … … bool MythMainWindow::TranslateKeyPress(const QString &context, 1461 1524 1462 1525 int keynum = d->TranslateKeyNum(e); 1463 1526 1527 if (prefixContext) 1528 { 1529 // There is a potential race condition where an input timeout 1530 // thread resets the context at the same time this code is 1531 // operating on it. 1532 if (prefixContext->AddKey(keynum)) 1533 { 1534 const QString &prefix = prefixContext->GetPrefix(); 1535 bool wasEmpty = prefix.isEmpty(); 1536 bool found = false; 1537 KeyContext *kctx = d->keyContexts.value(context); 1538 KeyContext *gctx = d->keyContexts.value("Global"); 1539 // Check for exact multikey matches. 1540 if (kctx && kctx->GetMultikeyMapping(prefix, actions)) 1541 found = true; 1542 if (context != "Global" && gctx->GetMultikeyMapping(prefix, actions)) 1543 found = true; 1544 if (found) 1545 { 1546 prefixContext->SetStatus(MultikeyContext::kMKfullMatch); 1547 return false; 1548 } 1549 // Check for multikey prefix matches. 1550 if (kctx && kctx->IsMultikeyPrefix(prefix)) 1551 found = true; 1552 if (context != "Global" && gctx->IsMultikeyPrefix(prefix)) 1553 found = true; 1554 if (found) 1555 { 1556 prefixContext->SetStatus(MultikeyContext::kMKpartialMatch); 1557 return false; 1558 } 1559 // No exact or prefix matches. Abort if there was queued input. 1560 if (wasEmpty) 1561 { 1562 prefixContext->SetStatus(MultikeyContext::kMKnoPrefix); 1563 } 1564 else 1565 { 1566 prefixContext->SetStatus(MultikeyContext::kMKbadMatch); 1567 return false; 1568 } 1569 } 1570 else // prefix was already full, couldn't add a 5th key 1571 { 1572 prefixContext->SetStatus(MultikeyContext::kMKbadMatch); 1573 } 1574 } 1575 1464 1576 QStringList localActions; 1465 1577 if (allowJumps && (d->jumpMap.count(keynum) > 0) && 1466 1578 (!d->jumpMap[keynum]->localAction.isEmpty()) && … … void MythMainWindow::ClearKeyContext(const QString &context) 1523 1635 } 1524 1636 1525 1637 void MythMainWindow::BindKey(const QString &context, const QString &action, 1526 const QString &key )1638 const QString &key, const QString &multikey) 1527 1639 { 1528 1640 QKeySequence keyseq(key); 1529 1641 … … void MythMainWindow::BindKey(const QString &context, const QString &action, 1551 1663 if (action == "ESCAPE" && context == "Global" && i == 0) 1552 1664 d->escapekey = keynum; 1553 1665 } 1666 1667 QStringList multidummyaction(""); 1668 if (d->keyContexts.value(context)->GetMultikeyMapping(multikey, multidummyaction)) 1669 { 1670 VERBOSE(VB_GENERAL, QString("Multikey %1 is bound to multiple actions " 1671 "in context %2.") 1672 .arg(multikey).arg(context)); 1673 } 1674 d->keyContexts.value(context)->AddMultikeyMapping(multikey, action); 1554 1675 } 1555 1676 1556 1677 void MythMainWindow::RegisterKey(const QString &context, const QString &action, 1557 1678 const QString &description, const QString &key) 1558 1679 { 1559 1680 QString keybind = key; 1681 QString multikeybind = ""; 1560 1682 1561 1683 MSqlQuery query(MSqlQuery::InitCon()); 1562 1684 1563 1685 if (d->m_useDB && query.isConnected()) 1564 1686 { 1565 query.prepare("SELECT keylist, description FROM keybindings WHERE "1687 query.prepare("SELECT keylist, description, multikey FROM keybindings WHERE " 1566 1688 "context = :CONTEXT AND action = :ACTION AND " 1567 1689 "hostname = :HOSTNAME ;"); 1568 1690 query.bindValue(":CONTEXT", context); … … void MythMainWindow::RegisterKey(const QString &context, const QString &action, 1573 1695 { 1574 1696 keybind = query.value(0).toString(); 1575 1697 QString db_description = query.value(1).toString(); 1698 multikeybind = query.value(2).toString(); 1576 1699 1577 1700 // Update keybinding description if changed 1578 1701 if (db_description != description) … … void MythMainWindow::RegisterKey(const QString &context, const QString &action, 1599 1722 else 1600 1723 { 1601 1724 QString inskey = keybind; 1725 QString insmultikey = multikeybind; 1602 1726 1603 1727 query.prepare("INSERT INTO keybindings (context, action, " 1604 "description, keylist, hostname ) VALUES "1728 "description, keylist, hostname, multikey) VALUES " 1605 1729 "( :CONTEXT, :ACTION, :DESCRIPTION, :KEYLIST, " 1606 ":HOSTNAME );");1730 ":HOSTNAME, :MULTIKEY );"); 1607 1731 query.bindValue(":CONTEXT", context); 1608 1732 query.bindValue(":ACTION", action); 1609 1733 query.bindValue(":DESCRIPTION", description); 1610 1734 query.bindValue(":KEYLIST", inskey); 1611 1735 query.bindValue(":HOSTNAME", GetMythDB()->GetHostName()); 1736 query.bindValue(":MULTIKEY", insmultikey); 1612 1737 1613 1738 if (!query.exec() && !(GetMythDB()->SuppressDBMessages())) 1614 1739 { … … void MythMainWindow::RegisterKey(const QString &context, const QString &action, 1617 1742 } 1618 1743 } 1619 1744 1620 BindKey(context, action, keybind );1745 BindKey(context, action, keybind, multikeybind); 1621 1746 } 1622 1747 1623 1748 QString MythMainWindow::GetKey(const QString &context, -
mythtv/libs/libmythui/mythmainwindow.h
diff --git a/mythtv/libs/libmythui/mythmainwindow.h b/mythtv/libs/libmythui/mythmainwindow.h index c007067..457ecda 100644
a b class MythPainterWindowVDPAU; 27 27 class MythPainterWindowD3D9; 28 28 class MythRender; 29 29 30 class MultikeyContext 31 { 32 public: 33 enum MultikeyStatus { 34 kMKnoPrefix, 35 kMKpartialMatch, 36 kMKfullMatch, 37 kMKbadMatch 38 }; 39 MultikeyContext(void) 40 : oldPrefix(""), currentPrefix(""), status(kMKnoPrefix) {} 41 void SetStatus(MultikeyStatus why) { status = why; } 42 const QString &GetPrefix(void) { return currentPrefix; } 43 bool AddKey(int key); 44 void Commit(MultikeyStatus &s, QString &prefix) { 45 prefix = currentPrefix; 46 s = status; 47 if (status != kMKpartialMatch) 48 { 49 currentPrefix = ""; 50 status = kMKnoPrefix; 51 } 52 oldPrefix = currentPrefix; 53 } 54 private: 55 QString oldPrefix; 56 QString currentPrefix; 57 MultikeyStatus status; 58 }; 59 30 60 class MUI_PUBLIC MythMainWindow : public QWidget 31 61 { 32 62 Q_OBJECT … … class MUI_PUBLIC MythMainWindow : public QWidget 48 78 MythScreenStack *GetStackAt(int pos); 49 79 50 80 bool TranslateKeyPress(const QString &context, QKeyEvent *e, 51 QStringList &actions, bool allowJumps = true) 81 QStringList &actions, bool allowJumps = true, 82 MultikeyContext *prefixContext = NULL) 52 83 MUNUSED_RESULT; 53 84 54 85 void ResetKeys(void); 55 86 void ClearKey(const QString &context, const QString &action); 56 87 void ClearKeyContext(const QString &context); 57 88 void BindKey(const QString &context, const QString &action, 58 const QString &key );89 const QString &key, const QString &multikey); 59 90 void RegisterKey(const QString &context, const QString &action, 60 91 const QString &description, const QString &key); 61 92 QString GetKey(const QString &context, const QString &action) const; -
mythtv/programs/mythfrontend/keybindings.cpp
diff --git a/mythtv/programs/mythfrontend/keybindings.cpp b/mythtv/programs/mythfrontend/keybindings.cpp index e15de26..cffc320 100644
a b void KeyBindings::CommitAction(const ActionID &id) 283 283 } 284 284 285 285 GetMythMainWindow()->ClearKey(id.GetContext(), id.GetAction()); 286 GetMythMainWindow()->BindKey(id.GetContext(), id.GetAction(), keys );286 GetMythMainWindow()->BindKey(id.GetContext(), id.GetAction(), keys, ""); 287 287 } 288 288 289 289 /** \fn KeyBindings::CommitJumppoint(const ActionID&)