00001
00002
00003 #include "mythuibuttontree.h"
00004
00005
00006 #include <QDomDocument>
00007
00008
00009 #include "mythlogging.h"
00010
00011
00012 #include "mythmainwindow.h"
00013
00014 MythUIButtonTree::MythUIButtonTree(MythUIType *parent, const QString &name)
00015 : MythUIType(parent, name)
00016 {
00017 m_initialized = false;
00018
00019 m_numLists = 1;
00020 m_visibleLists = 0;
00021 m_currentDepth = m_oldDepth = m_depthOffset = 0;
00022 m_rootNode = m_currentNode = NULL;
00023 m_listSpacing = 0;
00024 m_activeList = NULL;
00025 m_activeListID = 0;
00026
00027 m_active = true;
00028
00029 m_listTemplate = NULL;
00030 SetCanTakeFocus(true);
00031
00032 connect(this, SIGNAL(TakingFocus()), this, SLOT(Select()));
00033 connect(this, SIGNAL(LosingFocus()), this, SLOT(Deselect()));
00034 }
00035
00036 MythUIButtonTree::~MythUIButtonTree()
00037 {
00038 }
00039
00044 void MythUIButtonTree::Init()
00045 {
00046 if (!m_listTemplate)
00047 m_listTemplate = dynamic_cast<MythUIButtonList *>
00048 (GetChild("listtemplate"));
00049
00050 if (!m_listTemplate)
00051 {
00052 LOG(VB_GENERAL, LOG_ERR, QString("(%1) MythUIButtonList listtemplate "
00053 "is required in mythuibuttonlist: %2")
00054 .arg(GetXMLLocation().arg(objectName())));
00055 return;
00056 }
00057
00058 m_listTemplate->SetVisible(false);
00059
00060 int width = (m_Area.width() - (m_listSpacing * (m_numLists - 1))) / m_numLists;
00061 int height = m_Area.height();
00062
00063 int i = 0;
00064
00065 while (i < (int)m_numLists)
00066 {
00067 QString listname = QString("buttontree list %1").arg(i);
00068 MythUIButtonList *list = new MythUIButtonList(this, listname);
00069 list->CopyFrom(m_listTemplate);
00070 list->SetVisible(false);
00071 list->SetActive(false);
00072 list->SetCanTakeFocus(false);
00073 int x = i * (width + m_listSpacing);
00074 MythRect listArea = MythRect(x, 0, width, height);
00075 list->SetArea(listArea);
00076 m_buttonlists.append(list);
00077 i++;
00078 }
00079
00080 m_initialized = true;
00081 }
00082
00086 void MythUIButtonTree::SetTreeState(bool refreshAll)
00087 {
00088 if (!m_initialized)
00089 Init();
00090
00091 if (!m_rootNode)
00092 return;
00093
00094 if (!m_currentNode)
00095 DoSetCurrentNode(m_rootNode->getSelectedChild());
00096
00097 QList<MythGenericTree *> route = m_currentNode->getRoute();
00098
00099
00100 if (m_depthOffset >= route.size())
00101 m_depthOffset = 0;
00102
00103 if (((int)m_currentDepth + m_depthOffset) >= route.size())
00104 m_currentDepth = 0;
00105
00106 MythGenericTree *node = route.at(m_currentDepth + m_depthOffset);
00107
00108 if (m_currentDepth != m_oldDepth)
00109 refreshAll = true;
00110
00111 m_oldDepth = m_currentDepth;
00112
00113 m_visibleLists = 0;
00114 uint listid = 0;
00115
00116 while (listid < (uint)m_buttonlists.count())
00117 {
00118 MythUIButtonList *list = m_buttonlists.at(listid);
00119
00120 list->SetVisible(false);
00121 list->SetActive(false);
00122
00123 MythGenericTree *selectedNode = NULL;
00124
00125 if (node)
00126 selectedNode = node->getSelectedChild(true);
00127
00128 if (refreshAll || m_activeListID < listid)
00129 {
00130 if (!UpdateList(list, node))
00131 {
00132 listid++;
00133 continue;
00134 }
00135 }
00136
00137 if (m_active && (listid == m_activeListID))
00138 {
00139 m_activeList = list;
00140 list->SetActive(true);
00141 DoSetCurrentNode(selectedNode);
00142 emit itemSelected(list->GetItemCurrent());
00143 }
00144
00145 listid++;
00146
00147 list->SetVisible(true);
00148 m_visibleLists++;
00149
00150 node = selectedNode;
00151 }
00152 }
00153
00162 bool MythUIButtonTree::UpdateList(MythUIButtonList *list, MythGenericTree *node)
00163 {
00164 disconnect(list, 0, 0, 0);
00165
00166 list->Reset();
00167
00168 QList<MythGenericTree *> *nodelist = NULL;
00169
00170 if (node)
00171 nodelist = node->getAllChildren();
00172
00173 if (!nodelist || nodelist->isEmpty())
00174 return false;
00175
00176 MythGenericTree *selectedNode = node->getSelectedChild(true);
00177
00178 MythUIButtonListItem *selectedItem = NULL;
00179 QList<MythGenericTree *>::iterator it;
00180
00181 for (it = nodelist->begin(); it != nodelist->end(); ++it)
00182 {
00183 MythGenericTree *childnode = *it;
00184
00185 if (!childnode->IsVisible())
00186 continue;
00187
00188 MythUIButtonListItem *item = childnode->CreateListButton(list);
00189
00190 if (childnode == selectedNode)
00191 selectedItem = item;
00192 }
00193
00194 if (list->IsEmpty())
00195 return false;
00196
00197 if (selectedItem)
00198 list->SetItemCurrent(selectedItem);
00199
00200 connect(list, SIGNAL(itemSelected(MythUIButtonListItem *)),
00201 SLOT(handleSelect(MythUIButtonListItem *)));
00202 connect(list, SIGNAL(itemClicked(MythUIButtonListItem *)),
00203 SLOT(handleClick(MythUIButtonListItem *)));
00204 connect(list, SIGNAL(itemVisible(MythUIButtonListItem *)),
00205 SLOT(handleVisible(MythUIButtonListItem *)));
00206
00207 return true;
00208 }
00209
00224 bool MythUIButtonTree::AssignTree(MythGenericTree *tree)
00225 {
00226 if (!tree || !tree->visibleChildCount())
00227 return false;
00228
00229 if (m_rootNode)
00230 Reset();
00231
00232 m_rootNode = tree;
00233 m_currentNode = m_rootNode->getSelectedChild();
00234
00235
00236
00237 m_depthOffset = m_rootNode->currentDepth();
00238 SetTreeState(true);
00239 emit rootChanged(m_rootNode);
00240
00241 return true;
00242 }
00243
00247 void MythUIButtonTree::Reset(void)
00248 {
00249 m_rootNode = m_currentNode = NULL;
00250 m_visibleLists = 0;
00251 m_currentDepth = m_oldDepth = 0;
00252 m_activeList = NULL;
00253 m_activeListID = 0;
00254 m_active = true;
00255
00256 SetTreeState(true);
00257 emit rootChanged(m_rootNode);
00258
00259 MythUIType::Reset();
00260 }
00261
00270 bool MythUIButtonTree::SetNodeById(QList<int> route)
00271 {
00272 MythGenericTree *node = m_rootNode->findNode(route);
00273
00274 if (node && node->isSelectable())
00275 {
00276 DoSetCurrentNode(node);
00277 SetTreeState();
00278 return true;
00279 }
00280
00281 return false;
00282 }
00283
00292 bool MythUIButtonTree::SetNodeByString(QStringList route)
00293 {
00294 if (!m_rootNode)
00295 {
00296 DoSetCurrentNode(NULL);
00297 return false;
00298 }
00299
00300 MythGenericTree *foundNode = m_rootNode;
00301
00302 bool foundit = false;
00303
00304 if (!route.isEmpty())
00305 {
00306 if (route[0] == m_rootNode->getString())
00307 {
00308 if (route.size() > 1)
00309 {
00310 for (int i = 1; i < route.size(); i ++)
00311 {
00312 MythGenericTree *node = foundNode->getChildByName(route[i]);
00313
00314 if (node)
00315 {
00316 node->becomeSelectedChild();
00317 foundNode = node;
00318 foundit = true;
00319 }
00320 else
00321 {
00322 node = foundNode->getChildAt(0);
00323
00324 if (node)
00325 {
00326 node->becomeSelectedChild();
00327 foundNode = node;
00328 }
00329
00330 break;
00331 }
00332 }
00333 }
00334 else
00335 foundit = true;
00336 }
00337 }
00338
00339 DoSetCurrentNode(foundNode);
00340
00341 m_currentDepth = qMax(0, (int)(foundNode->currentDepth() - m_depthOffset - m_numLists));
00342 m_activeListID = qMin(foundNode->currentDepth() - m_depthOffset - 1, (int)(m_numLists - 1));
00343
00344 SetTreeState(true);
00345
00346 return foundit;
00347 }
00348
00357 bool MythUIButtonTree::SetCurrentNode(MythGenericTree *node)
00358 {
00359 if (!node)
00360 return false;
00361
00362 if (node == m_currentNode)
00363 return true;
00364
00365 QStringList route = node->getRouteByString();
00366
00367 return SetNodeByString(route);
00368 }
00369
00370 void MythUIButtonTree::ShowSearchDialog(void)
00371 {
00372 if (m_activeList)
00373 m_activeList->ShowSearchDialog();
00374 }
00375
00376 bool MythUIButtonTree::DoSetCurrentNode(MythGenericTree *node)
00377 {
00378 if (node)
00379 {
00380 if (node == m_currentNode)
00381 return true;
00382
00383 m_currentNode = node;
00384 node->becomeSelectedChild();
00385 emit nodeChanged(m_currentNode);
00386 return true;
00387 }
00388
00389 return false;
00390 }
00391
00400 void MythUIButtonTree::RemoveItem(MythUIButtonListItem *item, bool deleteNode)
00401 {
00402 if (!item || !m_rootNode)
00403 return;
00404
00405 MythGenericTree *node = qVariantValue<MythGenericTree *>(item->GetData());
00406
00407 if (node && node->getParent())
00408 {
00409 DoSetCurrentNode(node->getParent());
00410
00411 if (deleteNode)
00412 node->getParent()->deleteNode(node);
00413 else
00414 node->SetVisible(false);
00415 }
00416
00417 MythUIButtonList *list = item->parent();
00418
00419 list->RemoveItem(item);
00420
00421 if (list->IsEmpty())
00422 {
00423 if (m_currentDepth > 0)
00424 m_currentDepth--;
00425 else if (m_activeListID > 1)
00426 m_activeListID--;
00427
00428 SetTreeState(true);
00429 }
00430 }
00431
00439 void MythUIButtonTree::RemoveCurrentItem(bool deleteNode)
00440 {
00441 RemoveItem(GetItemCurrent(), deleteNode);
00442 }
00443
00449 void MythUIButtonTree::SetActive(bool active)
00450 {
00451 m_active = active;
00452
00453 if (m_initialized)
00454 SetTreeState();
00455 }
00456
00457 void MythUIButtonTree::Select()
00458 {
00459 SetActive(true);
00460 }
00461
00462 void MythUIButtonTree::Deselect()
00463 {
00464 SetActive(false);
00465 }
00466
00467
00474 void MythUIButtonTree::SwitchList(bool right)
00475 {
00476 bool doUpdate = false;
00477
00478 if (right)
00479 {
00480 if ((m_activeListID + 1 < m_visibleLists) &&
00481 (m_activeListID + 1 < (uint)m_buttonlists.count()))
00482 m_activeListID++;
00483 else if (m_currentNode && m_currentNode->visibleChildCount() > 0)
00484 {
00485 m_currentDepth++;
00486 doUpdate = true;
00487 }
00488 else
00489 return;
00490 }
00491 else if (!right)
00492 {
00493 if (m_activeListID > 0)
00494 m_activeListID--;
00495 else if (m_currentDepth > 0)
00496 {
00497 m_currentDepth--;
00498 doUpdate = true;
00499 }
00500 else
00501 return;
00502 }
00503
00504 if (doUpdate)
00505 SetTreeState();
00506 else
00507 {
00508 if (m_activeList)
00509 m_activeList->Deselect();
00510
00511 if (m_activeListID < (uint)m_buttonlists.count())
00512 {
00513 m_activeList = m_buttonlists[m_activeListID];
00514 m_activeList->Select();
00515 }
00516 }
00517 }
00518
00524 void MythUIButtonTree::handleSelect(MythUIButtonListItem *item)
00525 {
00526 if (!item)
00527 return;
00528
00529 MythUIButtonList *list = item->parent();
00530 QString name = list->objectName();
00531
00532
00533
00534 if (m_activeList)
00535 m_activeList->Deselect();
00536
00537 m_activeListID = name.section(' ', 2, 2).toInt();
00538 m_activeList = list;
00539
00540
00541 MythGenericTree *node = qVariantValue<MythGenericTree *> (item->GetData());
00542 DoSetCurrentNode(node);
00543 SetTreeState();
00544 }
00545
00551 void MythUIButtonTree::handleClick(MythUIButtonListItem *item)
00552 {
00553 if (!item)
00554 return;
00555
00556 MythGenericTree *node = qVariantValue<MythGenericTree *> (item->GetData());
00557
00558 if (DoSetCurrentNode(node))
00559 emit itemClicked(item);
00560 }
00561
00567 MythUIButtonListItem *MythUIButtonTree::GetItemCurrent() const
00568 {
00569 if (m_activeList)
00570 return m_activeList->GetItemCurrent();
00571
00572 return NULL;
00573 }
00574
00580 void MythUIButtonTree::handleVisible(MythUIButtonListItem *item)
00581 {
00582 if (!item)
00583 return;
00584
00585 emit itemVisible(item);
00586 }
00587
00591 bool MythUIButtonTree::keyPressEvent(QKeyEvent *event)
00592 {
00593 QStringList actions;
00594 bool handled = false;
00595 handled = GetMythMainWindow()->TranslateKeyPress("Global", event, actions);
00596
00597 for (int i = 0; i < actions.size() && !handled; i++)
00598 {
00599 QString action = actions[i];
00600 handled = true;
00601
00602 if (m_activeList && m_activeList->m_layout == MythUIButtonList::LayoutGrid)
00603 {
00604 if (action == "SELECT" && m_currentNode->visibleChildCount() > 0)
00605 {
00606 SwitchList(true);
00607 }
00608 else if (action == "ESCAPE" && m_currentDepth > 0)
00609 {
00610 SwitchList(false);
00611 }
00612 else
00613 handled = false;
00614 }
00615 else
00616 {
00617 if (action == "RIGHT" && m_currentNode->visibleChildCount() > 0)
00618 {
00619 SwitchList(true);
00620 }
00621 else if (action == "LEFT" && !(m_currentDepth == 0 && m_activeListID == 0))
00622 {
00623 SwitchList(false);
00624 }
00625 else
00626 handled = false;
00627 }
00628 }
00629
00630 if (!handled && m_activeList)
00631 handled = m_activeList->keyPressEvent(event);
00632
00633 return handled;
00634 }
00635
00639 bool MythUIButtonTree::ParseElement(
00640 const QString &filename, QDomElement &element, bool showWarnings)
00641 {
00642 if (element.tagName() == "spacing")
00643 {
00644 m_listSpacing = NormX(getFirstText(element).toInt());
00645 }
00646 else if (element.tagName() == "numlists")
00647 {
00648 m_numLists = getFirstText(element).toInt();
00649 }
00650 else
00651 {
00652 return MythUIType::ParseElement(filename, element, showWarnings);
00653 }
00654
00655 return true;
00656 }
00657
00661 void MythUIButtonTree::CreateCopy(MythUIType *parent)
00662 {
00663 MythUIButtonTree *bt = new MythUIButtonTree(parent, objectName());
00664 bt->CopyFrom(this);
00665 }
00666
00670 void MythUIButtonTree::CopyFrom(MythUIType *base)
00671 {
00672 MythUIButtonTree *bt = dynamic_cast<MythUIButtonTree *>(base);
00673
00674 if (!bt)
00675 return;
00676
00677 m_numLists = bt->m_numLists;
00678 m_listSpacing = bt->m_listSpacing;
00679 m_active = bt->m_active;
00680
00681 MythUIType::CopyFrom(base);
00682
00683 m_listTemplate = dynamic_cast<MythUIButtonList *>(GetChild("listtemplate"));
00684
00685 m_initialized = false;
00686 }