|
MythTV
0.26-pre
|
00001 #include "mythuibuttonlist.h" 00002 00003 #include <math.h> 00004 00005 // QT headers 00006 #include <QCoreApplication> 00007 #include <QDomDocument> 00008 #include <QKeyEvent> 00009 00010 // libmyth headers 00011 #include "mythlogging.h" 00012 00013 // mythui headers 00014 #include "mythmainwindow.h" 00015 #include "mythuiscrollbar.h" 00016 #include "mythuistatetype.h" 00017 #include "lcddevice.h" 00018 #include "mythuibutton.h" 00019 #include "mythuitext.h" 00020 #include "mythuitextedit.h" 00021 00022 #define LOC QString("MythUIButtonList(%1): ").arg(objectName()) 00023 00024 MythUIButtonList::MythUIButtonList(MythUIType *parent, const QString &name) 00025 : MythUIType(parent, name) 00026 { 00027 m_showArrow = true; 00028 m_showScrollBar = true; 00029 00030 Const(); 00031 } 00032 00033 MythUIButtonList::MythUIButtonList(MythUIType *parent, const QString &name, 00034 const QRect &area, bool showArrow, 00035 bool showScrollArrows, bool showScrollBar) 00036 : MythUIType(parent, name) 00037 { 00038 m_Area = area; 00039 m_showArrow = showArrow; 00040 m_showScrollBar = showScrollBar; 00041 00042 Const(); 00043 } 00044 00045 void MythUIButtonList::Const(void) 00046 { 00047 m_contentsRect = MythRect(0, 0, 0, 0); 00048 00049 m_layout = LayoutVertical; 00050 m_arrange = ArrangeFixed; 00051 m_alignment = Qt::AlignLeft | Qt::AlignTop; 00052 m_scrollStyle = ScrollFree; 00053 m_wrapStyle = WrapNone; 00054 00055 m_active = false; 00056 m_drawFromBottom = false; 00057 00058 m_selPosition = 0; 00059 m_topPosition = 0; 00060 m_itemCount = 0; 00061 m_keepSelAtBottom = false; 00062 00063 m_initialized = false; 00064 m_needsUpdate = false; 00065 m_clearing = false; 00066 m_itemHorizSpacing = 0; 00067 m_itemVertSpacing = 0; 00068 m_itemHeight = 0; 00069 m_itemWidth = 0; 00070 m_itemsVisible = 0; 00071 m_maxVisible = 0; 00072 m_columns = 0; 00073 m_rows = 0; 00074 m_leftColumns = 0; 00075 m_rightColumns = 0; 00076 m_topRows = 0; 00077 m_bottomRows = 0; 00078 m_lcdTitle = ""; 00079 00080 m_searchPosition = MythPoint(-2, -2); 00081 m_searchFields = "**ALL**"; 00082 m_searchStartsWith = false; 00083 00084 m_upArrow = m_downArrow = NULL; 00085 m_scrollBar = NULL; 00086 00087 m_buttontemplate = NULL; 00088 00089 m_nextItemLoaded = 0; 00090 00091 SetCanTakeFocus(true); 00092 00093 connect(this, SIGNAL(TakingFocus()), this, SLOT(Select())); 00094 connect(this, SIGNAL(LosingFocus()), this, SLOT(Deselect())); 00095 } 00096 00097 MythUIButtonList::~MythUIButtonList() 00098 { 00099 m_ButtonToItem.clear(); 00100 m_clearing = true; 00101 00102 while (!m_itemList.isEmpty()) 00103 delete m_itemList.takeFirst(); 00104 } 00105 00106 void MythUIButtonList::Select() 00107 { 00108 MythUIButtonListItem *item = GetItemCurrent(); 00109 00110 if (item) 00111 emit itemSelected(item); 00112 00113 SetActive(true); 00114 } 00115 00116 void MythUIButtonList::Deselect() 00117 { 00118 SetActive(false); 00119 } 00120 00121 void MythUIButtonList::SetDrawFromBottom(bool draw) 00122 { 00123 m_drawFromBottom = draw; 00124 } 00125 00126 void MythUIButtonList::SetActive(bool active) 00127 { 00128 if (m_active == active) 00129 return; 00130 00131 m_active = active; 00132 00133 if (m_initialized) 00134 Update(); 00135 } 00136 00140 void MythUIButtonList::Reset() 00141 { 00142 m_ButtonToItem.clear(); 00143 00144 if (m_itemList.isEmpty()) 00145 return; 00146 00147 m_clearing = true; 00148 00149 while (!m_itemList.isEmpty()) 00150 delete m_itemList.takeFirst(); 00151 00152 m_clearing = false; 00153 00154 m_selPosition = 0; 00155 m_topPosition = 0; 00156 m_itemCount = 0; 00157 00158 StopLoad(); 00159 Update(); 00160 MythUIType::Reset(); 00161 } 00162 00163 void MythUIButtonList::Update() 00164 { 00165 m_needsUpdate = true; 00166 SetRedraw(); 00167 } 00168 00169 /* 00170 * The "width" of a button determines it relative position when using 00171 * Dynamic-Layout. 00172 * 00173 * If a button has a negative offset, that offset needs accounted for 00174 * to position the button in proper releation to the surrounding buttons. 00175 */ 00176 int MythUIButtonList::minButtonWidth(const MythRect &area) 00177 { 00178 int width = area.width(); 00179 00180 if (area.x() < 0) 00181 { 00182 /* 00183 * Assume if an overlap is allowed on the left, the same overlap 00184 * is on the right 00185 */ 00186 width += (area.x() * 2 - 1); // x is negative 00187 00188 while (width < 0) 00189 width -= area.x(); // Oops 00190 } 00191 else if (m_layout == LayoutHorizontal) 00192 width -= area.x(); // Get rid of any "space" betwen the buttons 00193 00194 return width; 00195 } 00196 00197 /* 00198 * The "height" of a button determines it relative position when using 00199 * Dynamic-Layout. 00200 * 00201 * If a button has a negative offset, that offset needs accounted for 00202 * to position the button in proper releation to the surrounding buttons. 00203 */ 00204 int MythUIButtonList::minButtonHeight(const MythRect &area) 00205 { 00206 int height = area.height(); 00207 00208 if (area.y() < 0) 00209 { 00210 /* 00211 * Assume if an overlap is allowed on the top, the same overlap 00212 * is on the bottom 00213 */ 00214 height += (area.y() * 2 - 1); 00215 00216 while (height < 0) 00217 height -= area.y(); // Oops 00218 } 00219 else if (m_layout == LayoutVertical) 00220 height -= area.y(); // Get rid of any "space" betwen the buttons 00221 00222 return height; 00223 } 00224 00225 /* 00226 * For Dynamic-Layout, buttons are allocated as needed. If the list is 00227 * being redrawn, re-use any previously allocated buttons. 00228 */ 00229 MythUIGroup *MythUIButtonList::PrepareButton(int buttonIdx, int itemIdx, 00230 int &selectedIdx, 00231 int &button_shift) 00232 { 00233 MythUIStateType *realButton; 00234 MythUIGroup *buttonstate; 00235 MythUIButtonListItem *buttonItem = m_itemList[itemIdx]; 00236 00237 buttonIdx += button_shift; 00238 00239 if (buttonIdx < 0 || buttonIdx + 1 > m_maxVisible) 00240 { 00241 QString name = QString("buttonlist button %1").arg(m_maxVisible); 00242 MythUIStateType *button = new MythUIStateType(this, name); 00243 button->CopyFrom(m_buttontemplate); 00244 button->ConnectDependants(true); 00245 00246 if (buttonIdx < 0) 00247 { 00248 /* 00249 * If a new button is needed in the front of the list, previously 00250 * existing buttons need shifted to the right. 00251 */ 00252 m_ButtonList.prepend(button); 00253 buttonIdx = 0; 00254 ++button_shift; 00255 00256 if (selectedIdx >= 0) 00257 ++selectedIdx; 00258 } 00259 else 00260 m_ButtonList.append(button); 00261 00262 ++m_maxVisible; 00263 } 00264 00265 realButton = m_ButtonList[buttonIdx]; 00266 m_ButtonToItem[buttonIdx] = buttonItem; 00267 buttonItem->SetToRealButton(realButton, itemIdx == m_selPosition); 00268 buttonstate = dynamic_cast<MythUIGroup *>(realButton->GetCurrentState()); 00269 00270 if (itemIdx == m_selPosition) 00271 selectedIdx = buttonIdx; 00272 00273 return buttonstate; 00274 } 00275 00276 /* 00277 * Dynamically layout the buttons on a row. 00278 */ 00279 bool MythUIButtonList::DistributeRow(int &first_button, int &last_button, 00280 int &first_item, int &last_item, 00281 int &selected_column, int &skip_cols, 00282 bool grow_left, bool grow_right, 00283 int **col_widths, int &row_height, 00284 int total_height, int split_height, 00285 int &col_cnt, bool &wrapped) 00286 { 00287 MythUIGroup *buttonstate; 00288 int initial_first_button, initial_last_button; 00289 int initial_first_item, initial_last_item; 00290 int col_idx, left_cnt = 0; 00291 int width, height; 00292 int max_width, max_height; 00293 int left_width, right_width; 00294 int begin, end; 00295 bool underflow = false; // keep from being uninitialized 00296 bool added; 00297 bool hsplit, vsplit; 00298 int selectedIdx; 00299 int button_shift; 00300 00301 selectedIdx = -1; 00302 button_shift = 0; 00303 col_cnt = 1; 00304 skip_cols = 0; 00305 00306 if (last_item + 1 > m_itemCount || last_item < 0 || first_item < 0) 00307 return false; 00308 00309 /* 00310 * Allocate a button on the row. With a vertical layout, there is 00311 * only one button per row, and this would be it. 00312 */ 00313 if (grow_right) 00314 buttonstate = PrepareButton(last_button, last_item, 00315 selectedIdx, button_shift); 00316 else 00317 buttonstate = PrepareButton(first_button, first_item, 00318 selectedIdx, button_shift); 00319 00320 if (buttonstate == NULL) 00321 { 00322 LOG(VB_GENERAL, LOG_ERR, QString("Failed to query buttonlist state: %1") 00323 .arg(last_button)); 00324 return false; 00325 } 00326 00327 // Note size of initial button. 00328 max_width = m_contentsRect.width(); 00329 max_height = m_contentsRect.height(); 00330 row_height = minButtonHeight(buttonstate->GetArea()); 00331 width = minButtonWidth(buttonstate->GetArea()); 00332 00333 /* 00334 * If the selected button should be centered, don't allow new buttons 00335 * to take up more than half the allowed area. 00336 */ 00337 vsplit = (m_scrollStyle == ScrollCenter); 00338 hsplit = vsplit && grow_left && grow_right; 00339 00340 if (hsplit) 00341 { 00342 max_width /= 2; 00343 left_width = right_width = (width / 2); 00344 } 00345 else 00346 { 00347 if (grow_right) 00348 { 00349 left_width = 0; 00350 right_width = width; 00351 } 00352 else 00353 { 00354 left_width = width; 00355 right_width = 0; 00356 } 00357 } 00358 00359 if (vsplit) 00360 max_height /= 2; 00361 00362 /* 00363 * If total_height == 0, then this is the first row, so allow any height. 00364 * Otherwise, If adding a button to a column would exceed the 00365 * parent height, abort 00366 */ 00367 if (total_height > 0 && 00368 ((vsplit ? split_height : total_height) + 00369 m_itemVertSpacing + row_height > max_height)) 00370 { 00371 LOG(VB_GUI, LOG_DEBUG, 00372 QString("%1 Height exceeded %2 + (%3) + %4 = %5 which is > %6") 00373 .arg(vsplit ? "Centering" : "Total") 00374 .arg(split_height).arg(m_itemVertSpacing).arg(row_height) 00375 .arg(split_height + m_itemVertSpacing + row_height) 00376 .arg(max_height)); 00377 first_button += button_shift; 00378 last_button += button_shift; 00379 return false; 00380 } 00381 00382 LOG(VB_GUI, LOG_DEBUG, QString("Added button item %1 width %2 height %3") 00383 .arg(grow_right ? last_item : first_item) 00384 .arg(width).arg(row_height)); 00385 00386 initial_first_button = first_button; 00387 initial_last_button = last_button; 00388 initial_first_item = first_item; 00389 initial_last_item = last_item; 00390 00391 /* 00392 * if col_widths is not NULL, then grow_left & grow_right 00393 * are mutually exclusive. So, col_idx can be anchored from 00394 * the left or right. 00395 */ 00396 if (grow_right) 00397 col_idx = 0; 00398 else 00399 col_idx = m_columns - 1; 00400 00401 // Add butons until no more fit. 00402 added = (m_layout != LayoutVertical); 00403 00404 while (added) 00405 { 00406 added = false; 00407 00408 // If a grid, maintain same number of columns on each row. 00409 if (grow_right && col_cnt < m_columns) 00410 { 00411 if (wrapped) 00412 end = first_item; 00413 else 00414 { 00415 // Are we allowed to wrap when we run out of items? 00416 if (m_wrapStyle == WrapItems && 00417 (hsplit || m_scrollStyle != ScrollFree) && 00418 last_item + 1 == m_itemCount) 00419 { 00420 last_item = -1; 00421 wrapped = true; 00422 end = first_item; 00423 } 00424 else 00425 end = m_itemCount; 00426 } 00427 00428 if (last_item + 1 < end) 00429 { 00430 // Allocate next button to the right. 00431 buttonstate = PrepareButton(last_button + 1, last_item + 1, 00432 selectedIdx, button_shift); 00433 00434 if (buttonstate == NULL) 00435 continue; 00436 00437 width = minButtonWidth(buttonstate->GetArea()); 00438 00439 // For grids, use the widest button in a column 00440 if (*col_widths && width < (*col_widths)[col_idx]) 00441 width = (*col_widths)[col_idx]; 00442 00443 // Does the button fit? 00444 if ((hsplit ? right_width : left_width + right_width) + 00445 m_itemHorizSpacing + width > max_width) 00446 { 00447 int total = hsplit ? right_width : left_width + right_width; 00448 LOG(VB_GUI, LOG_DEBUG, 00449 QString("button on right would exceed width: " 00450 "%1+(%2)+%3 == %4 which is > %5") 00451 .arg(total).arg(m_itemHorizSpacing).arg(width) 00452 .arg(total + m_itemHorizSpacing + width) 00453 .arg(max_width)); 00454 } 00455 else 00456 { 00457 added = true; 00458 ++col_cnt; 00459 ++last_button; 00460 ++last_item; 00461 ++col_idx; 00462 right_width += m_itemHorizSpacing + width; 00463 height = minButtonHeight(buttonstate->GetArea()); 00464 00465 if (row_height < height) 00466 row_height = height; 00467 00468 LOG(VB_GUI, LOG_DEBUG, 00469 QString("Added button item %1 " 00470 "R.width %2 height %3 total width %4+%5" 00471 " (max %6)") 00472 .arg(last_item).arg(width).arg(height) 00473 .arg(left_width).arg(right_width).arg(max_width)); 00474 } 00475 } 00476 else 00477 underflow = true; 00478 } 00479 00480 // If a grid, maintain same number of columns on each row. 00481 if (grow_left && col_cnt < m_columns) 00482 { 00483 if (wrapped) 00484 end = last_item + 1; 00485 else 00486 { 00487 // Are we allowed to wrap when we run out of items? 00488 if (m_wrapStyle == WrapItems && 00489 (hsplit || m_scrollStyle != ScrollFree) && 00490 first_item == 0) 00491 { 00492 first_item = m_itemCount; 00493 wrapped = true; 00494 end = last_item + 1; 00495 } 00496 else 00497 end = 0; 00498 } 00499 00500 if (first_item > end) 00501 { 00502 buttonstate = PrepareButton(first_button - 1, first_item - 1, 00503 selectedIdx, button_shift); 00504 00505 if (buttonstate == NULL) 00506 continue; 00507 00508 width = minButtonWidth(buttonstate->GetArea()); 00509 00510 // For grids, use the widest button in a column 00511 if (*col_widths && width < (*col_widths)[col_idx]) 00512 width = (*col_widths)[col_idx]; 00513 00514 // Does the button fit? 00515 if ((hsplit ? left_width : left_width + right_width) + 00516 m_itemHorizSpacing + width > max_width) 00517 { 00518 int total = hsplit ? left_width : left_width + right_width; 00519 LOG(VB_GUI, LOG_DEBUG, 00520 QString("button on left would exceed width: " 00521 "%1+(%2)+%3 == %4 which is > %5") 00522 .arg(total).arg(m_itemHorizSpacing).arg(width) 00523 .arg(total + m_itemHorizSpacing + width) 00524 .arg(max_width)); 00525 } 00526 else 00527 { 00528 added = true; 00529 --first_button; 00530 --first_item; 00531 --col_idx; 00532 ++col_cnt; 00533 ++left_cnt; 00534 left_width += m_itemHorizSpacing + width; 00535 height = minButtonHeight(buttonstate->GetArea()); 00536 00537 if (row_height < height) 00538 row_height = height; 00539 00540 LOG(VB_GUI, LOG_DEBUG, 00541 QString("Added button item %1 " 00542 "L.width %2 height %3 total width %4+%5" 00543 " (max %6)") 00544 .arg(first_item).arg(width).arg(height) 00545 .arg(left_width).arg(right_width).arg(max_width)); 00546 } 00547 } 00548 else 00549 { 00550 underflow = true; 00551 if (m_layout == LayoutGrid) 00552 skip_cols = m_columns - col_cnt; 00553 } 00554 } 00555 } 00556 00557 /* 00558 * If total_height == 0, then this is the first row, so allow any height. 00559 * Otherwise, If adding a button to a column would exceed the 00560 * parent height, abort 00561 */ 00562 if (total_height > 0 && 00563 ((vsplit ? split_height : total_height) + 00564 m_itemVertSpacing + row_height > max_height)) 00565 { 00566 LOG(VB_GUI, LOG_DEBUG, 00567 QString("%1 Height exceeded %2 + (%3) + %4 = %5 which is > %6") 00568 .arg(vsplit ? "Centering" : "Total") 00569 .arg(split_height).arg(m_itemVertSpacing).arg(row_height) 00570 .arg(split_height + m_itemVertSpacing + row_height) 00571 .arg(max_height)); 00572 first_button = initial_first_button + button_shift; 00573 last_button = initial_last_button + button_shift; 00574 first_item = initial_first_item; 00575 last_item = initial_last_item; 00576 return false; 00577 } 00578 00579 if (*col_widths == 0) 00580 { 00581 /* 00582 * Allocate array to hold columns widths, now that we know 00583 * how many columns there are. 00584 */ 00585 *col_widths = new int[col_cnt]; 00586 00587 for (col_idx = 0; col_idx < col_cnt; ++col_idx) 00588 (*col_widths)[col_idx] = 0; 00589 00590 } 00591 00592 // Adjust for insertions on the front. 00593 first_button += button_shift; 00594 last_button += button_shift; 00595 00596 // It fits, so so note max column widths 00597 MythUIStateType *realButton; 00598 int buttonIdx; 00599 00600 if (grow_left) 00601 { 00602 begin = first_button; 00603 end = first_button + col_cnt; 00604 } 00605 else 00606 { 00607 end = last_button + 1; 00608 begin = end - col_cnt; 00609 } 00610 00611 for (buttonIdx = begin, col_idx = 0; 00612 buttonIdx < end; ++buttonIdx, ++col_idx) 00613 { 00614 realButton = m_ButtonList[buttonIdx]; 00615 buttonstate = dynamic_cast<MythUIGroup *> 00616 (realButton->GetCurrentState()); 00617 if (!buttonstate) 00618 break; 00619 width = minButtonWidth(buttonstate->GetArea()); 00620 00621 if ((*col_widths)[col_idx] < width) 00622 (*col_widths)[col_idx] = width; 00623 00624 // Make note of which column has the selected button 00625 if (selectedIdx == buttonIdx) 00626 selected_column = col_idx; 00627 } 00628 00629 /* 00630 An underflow indicates we ran out of items, not that the 00631 buttons did not fit on the row. 00632 */ 00633 if (total_height && underflow && col_cnt < m_columns) 00634 col_cnt = m_columns; 00635 00636 return true; 00637 } 00638 00639 /* 00640 * Dynamically layout columns 00641 */ 00642 bool MythUIButtonList::DistributeCols(int &first_button, int &last_button, 00643 int &first_item, int &last_item, 00644 int &selected_column, int &selected_row, 00645 int &skip_cols, int **col_widths, 00646 QList<int> & row_heights, 00647 int &top_height, int &bottom_height, 00648 bool &wrapped) 00649 { 00650 int col_cnt; 00651 int height; 00652 int row_cnt = 1; 00653 int end; 00654 bool added; 00655 00656 do 00657 { 00658 added = false; 00659 00660 if (wrapped) 00661 end = first_item; 00662 else 00663 { 00664 // Are we allowed to wrap when we run out of items? 00665 if (m_wrapStyle == WrapItems && 00666 (m_scrollStyle == ScrollCenter || 00667 m_scrollStyle == ScrollGroupCenter) && 00668 last_item + 1 == m_itemCount) 00669 { 00670 last_item = -1; 00671 wrapped = true; 00672 end = first_item; 00673 } 00674 else 00675 end = m_itemCount; 00676 } 00677 00678 if (last_item + 1 < end) 00679 { 00680 // Does another row fit? 00681 if (DistributeRow(first_button, ++last_button, 00682 first_item, ++last_item, selected_column, 00683 skip_cols, false, true, col_widths, height, 00684 top_height + bottom_height, bottom_height, 00685 col_cnt, wrapped)) 00686 { 00687 if (col_cnt < m_columns) 00688 return false; // Need to try again with fewer cols 00689 00690 if (selected_row == -1 && selected_column != -1) 00691 selected_row = row_heights.size(); 00692 00693 ++row_cnt; 00694 row_heights.push_back(height); 00695 bottom_height += (height + m_itemVertSpacing); 00696 added = true; 00697 } 00698 else 00699 { 00700 --last_button; 00701 --last_item; 00702 } 00703 } 00704 00705 if (wrapped) 00706 end = last_item + 1; 00707 else 00708 { 00709 // Are we allowed to wrap when we run out of items? 00710 if (m_wrapStyle == WrapItems && 00711 (m_scrollStyle == ScrollCenter || 00712 m_scrollStyle == ScrollGroupCenter) && 00713 first_item == 0) 00714 { 00715 first_item = m_itemCount; 00716 wrapped = true; 00717 end = last_item + 1; 00718 } 00719 else 00720 end = 0; 00721 } 00722 00723 if (first_item > end) 00724 { 00725 // Can we insert another row? 00726 if (DistributeRow(--first_button, last_button, 00727 --first_item, last_item, selected_column, 00728 skip_cols, true, false, col_widths, height, 00729 top_height + bottom_height, top_height, 00730 col_cnt, wrapped)) 00731 { 00732 if (col_cnt < m_columns) 00733 return false; // Need to try again with fewer cols 00734 00735 if (selected_row == -1 && selected_column != -1) 00736 selected_row = row_heights.size(); 00737 else if (selected_row != -1) 00738 ++selected_row; 00739 00740 ++row_cnt; 00741 row_heights.push_front(height); 00742 top_height += (height + m_itemVertSpacing); 00743 added = true; 00744 } 00745 else 00746 { 00747 ++first_button; 00748 ++first_item; 00749 } 00750 } 00751 } 00752 while (added); 00753 00754 return true; 00755 } 00756 00757 /* 00758 * Dynamically layout as many buttons as will fit in the area. 00759 */ 00760 bool MythUIButtonList::DistributeButtons(void) 00761 { 00762 int first_button, last_button, start_button, start_item; 00763 int first_item, last_item, skip_cols = 0; 00764 int *col_widths; 00765 int col_cnt; 00766 int selected_column, selected_row; 00767 bool wrapped = false; 00768 bool grow_left = true; 00769 00770 QList<int> row_heights; 00771 QList<int>::iterator Iheight; 00772 int height, top_height, bottom_height; 00773 00774 start_item = m_selPosition; 00775 selected_column = selected_row = -1; 00776 top_height = bottom_height = 0; 00777 col_widths = 0; 00778 first_button = last_button = start_button = 0; 00779 00780 LOG(VB_GUI, LOG_DEBUG, QString("DistributeButtons: " 00781 "selected item %1 total items %2") 00782 .arg(start_item).arg(m_itemCount)); 00783 00784 /* 00785 * Try fewer and fewer columns until each row can fit the same 00786 * number of columns. 00787 */ 00788 for (m_columns = m_itemCount; m_columns > 0;) 00789 { 00790 first_item = last_item = start_item; 00791 00792 /* 00793 * Drawing starts at start_button, and radiates from there. 00794 * Attempt to pick a start_button which will minimize the need for new 00795 * button allocations. 00796 */ 00797 switch (m_scrollStyle) 00798 { 00799 case ScrollCenter: 00800 case ScrollGroupCenter: 00801 start_button = qMax((m_maxVisible / 2) - 1, 0); 00802 break; 00803 case ScrollFree: 00804 00805 if (m_layout == LayoutGrid) 00806 { 00807 start_button = 0; 00808 first_item = last_item = 0; 00809 grow_left = false; 00810 } 00811 else if (!m_ButtonList.empty()) 00812 { 00813 if (m_itemCount - m_selPosition - 1 < 00814 static_cast<int>(m_ButtonList.size()) / 2) 00815 start_button = m_ButtonList.size() - 00816 (m_itemCount - m_selPosition) + 1; 00817 else if (m_selPosition > 00818 static_cast<int>(m_ButtonList.size()) / 2) 00819 start_button = (m_ButtonList.size() / 2); 00820 else 00821 start_button = m_selPosition; 00822 } 00823 else 00824 start_button = 0; 00825 00826 break; 00827 } 00828 00829 first_button = last_button = start_button; 00830 row_heights.clear(); 00831 00832 // Process row with selected button, and set starting val for m_columns. 00833 if (!DistributeRow(first_button, last_button, 00834 first_item, last_item, selected_column, 00835 skip_cols, grow_left, true, &col_widths, 00836 height, 0, 0, col_cnt, wrapped)) 00837 { 00838 delete[] col_widths; 00839 return false; 00840 } 00841 00842 m_columns = col_cnt; 00843 00844 if (m_layout == LayoutGrid && m_scrollStyle == ScrollFree) 00845 { 00846 /* 00847 * Now that we know how many columns there are, we can start 00848 * the grid layout for real. 00849 */ 00850 start_item = (m_selPosition / m_columns) * m_columns; 00851 first_item = last_item = start_item; 00852 00853 /* 00854 * Attempt to pick a start_button which will minimize the need 00855 * for new button allocations. 00856 */ 00857 start_button = qMax((int)m_ButtonList.size() / 2, 0); 00858 start_button = (start_button / qMax(m_columns, 1)) * m_columns; 00859 00860 if (start_button < m_itemCount / 2 && 00861 m_itemCount - m_selPosition - 1 < (int)m_ButtonList.size() / 2) 00862 start_button += m_columns; 00863 00864 first_button = last_button = start_button; 00865 00866 // Now do initial row layout again with our new knowledge 00867 selected_column = selected_row = -1; 00868 00869 if (!DistributeRow(first_button, last_button, 00870 first_item, last_item, selected_column, 00871 skip_cols, grow_left, true, &col_widths, 00872 height, 0, 0, col_cnt, wrapped)) 00873 { 00874 delete[] col_widths; 00875 return false; 00876 } 00877 } 00878 00879 if (selected_column != -1) 00880 selected_row = 0; 00881 00882 row_heights.push_back(height); 00883 00884 if (m_scrollStyle == ScrollCenter) 00885 top_height = bottom_height = (height / 2); 00886 else 00887 bottom_height = height; 00888 00889 if (m_layout == LayoutHorizontal) 00890 break; 00891 00892 // As as many columns as will fit. 00893 if (DistributeCols(first_button, last_button, 00894 first_item, last_item, 00895 selected_column, selected_row, 00896 skip_cols, &col_widths, row_heights, 00897 top_height, bottom_height, wrapped)) 00898 break; // Buttons fit on each row, so done 00899 00900 delete[] col_widths; 00901 col_widths = 0; 00902 00903 --m_columns; 00904 start_item = m_selPosition; 00905 } 00906 00907 m_rows = row_heights.size(); 00908 00909 LOG(VB_GUI, LOG_DEBUG, 00910 QString("%1 rows, %2 columns fit inside parent area %3x%4") 00911 .arg(m_rows).arg(m_columns).arg(m_contentsRect.width()) 00912 .arg(m_contentsRect.height())); 00913 00914 if (col_widths == 0) 00915 return false; 00916 00917 int total, row, col; 00918 int left_spacing, right_spacing, top_spacing, bottom_spacing; 00919 int x, y, x_init, x_adj, y_adj; 00920 QString status_msg; 00921 00922 /* 00923 * Calculate heights of buttons on each side of selected button 00924 */ 00925 top_height = bottom_height = m_topRows = m_bottomRows = 0; 00926 00927 status_msg = "Row heights: "; 00928 00929 for (row = 0; row < m_rows; ++row) 00930 { 00931 if (row != 0) 00932 status_msg += ", "; 00933 00934 if (row == selected_row) 00935 { 00936 status_msg += '['; 00937 top_height += (row_heights[row] / 2); 00938 bottom_height += ((row_heights[row] / 2) + (row_heights[row] % 2)); 00939 } 00940 else 00941 { 00942 if (bottom_height) 00943 { 00944 bottom_height += m_itemVertSpacing + row_heights[row]; 00945 ++m_bottomRows; 00946 } 00947 else 00948 { 00949 top_height += row_heights[row] + m_itemVertSpacing; 00950 ++m_topRows; 00951 } 00952 } 00953 00954 status_msg += QString("%1").arg(row_heights[row]); 00955 00956 if (row == selected_row) 00957 status_msg += ']'; 00958 } 00959 00960 /* 00961 * How much extra space should there be between buttons? 00962 */ 00963 if (m_arrange == ArrangeStack || m_layout == LayoutHorizontal) 00964 { 00965 // None 00966 top_spacing = bottom_spacing = 0; 00967 } 00968 else 00969 { 00970 if (m_rows < 2) 00971 { 00972 // Equal space on both sides of single row 00973 top_spacing = bottom_spacing = 00974 (m_contentsRect.height() - top_height) / 2; 00975 } 00976 else 00977 { 00978 if (m_scrollStyle == ScrollCenter) 00979 { 00980 // Selected button needs to end up in the middle of area 00981 top_spacing = m_topRows ? (m_contentsRect.height() / 2 - 00982 top_height) / m_topRows : 0; 00983 bottom_spacing = m_bottomRows ? (m_contentsRect.height() / 2 - 00984 bottom_height) / m_bottomRows : 0; 00985 00986 if (m_arrange == ArrangeSpread) 00987 { 00988 // Use same spacing on both sides of selected button 00989 if (!m_topRows || top_spacing > bottom_spacing) 00990 top_spacing = bottom_spacing; 00991 else 00992 bottom_spacing = top_spacing; 00993 } 00994 } 00995 else 00996 { 00997 // Buttons will be evenly spread out to fill entire area 00998 top_spacing = bottom_spacing = (m_contentsRect.height() - 00999 (top_height + bottom_height)) / 01000 (m_topRows + m_bottomRows); 01001 } 01002 } 01003 01004 // Add in intra-button space size 01005 top_height += (top_spacing * m_topRows); 01006 bottom_height += (bottom_spacing * m_bottomRows); 01007 } 01008 01009 /* 01010 * Calculate top margin 01011 */ 01012 y = m_contentsRect.y(); 01013 01014 if ((m_alignment & Qt::AlignVCenter) && m_arrange != ArrangeFill) 01015 { 01016 if (m_scrollStyle == ScrollCenter) 01017 { 01018 // Adjust to compensate for top height less than bottom height 01019 y += qMax(bottom_height - top_height, 0); 01020 total = qMax(top_height, bottom_height) * 2; 01021 } 01022 else 01023 total = top_height + bottom_height; 01024 01025 // Adjust top margin so selected button ends up in the middle 01026 y += (qMax(m_contentsRect.height() - total, 2) / 2); 01027 } 01028 else if ((m_alignment & Qt::AlignBottom) && m_arrange == ArrangeStack) 01029 { 01030 // Adjust top margin so buttons are bottom justified 01031 y += qMax(m_contentsRect.height() - 01032 (top_height + bottom_height), 0); 01033 } 01034 01035 status_msg += QString(" spacing top %1 bottom %2 fixed %3 offset %4") 01036 .arg(top_spacing).arg(bottom_spacing) 01037 .arg(m_itemVertSpacing).arg(y); 01038 01039 LOG(VB_GUI, LOG_DEBUG, status_msg); 01040 01041 /* 01042 * Calculate width of buttons on each side of selected button 01043 */ 01044 int left_width, right_width; 01045 01046 left_width = right_width = m_leftColumns = m_rightColumns = 0; 01047 01048 status_msg = "Col widths: "; 01049 01050 for (col = 0; col < m_columns; ++col) 01051 { 01052 if (col != 0) 01053 status_msg += ", "; 01054 01055 if (col == selected_column) 01056 { 01057 status_msg += '['; 01058 left_width += (col_widths[col] / 2); 01059 right_width += ((col_widths[col] / 2) + (col_widths[col] % 2)); 01060 } 01061 else 01062 { 01063 if (right_width) 01064 { 01065 right_width += m_itemHorizSpacing + col_widths[col]; 01066 ++m_rightColumns; 01067 } 01068 else 01069 { 01070 left_width += col_widths[col] + m_itemHorizSpacing; 01071 ++m_leftColumns; 01072 } 01073 } 01074 01075 status_msg += QString("%1").arg(col_widths[col]); 01076 01077 if (col == selected_column) 01078 status_msg += ']'; 01079 } 01080 01081 /* 01082 * How much extra space should there be between buttons? 01083 */ 01084 if (m_arrange == ArrangeStack || m_layout == LayoutVertical) 01085 { 01086 // None 01087 left_spacing = right_spacing = 0; 01088 } 01089 else 01090 { 01091 if (m_columns < 2) 01092 { 01093 // Equal space on both sides of single column 01094 left_spacing = right_spacing = 01095 (m_contentsRect.width() - left_width) / 2; 01096 } 01097 else 01098 { 01099 if (m_scrollStyle == ScrollCenter) 01100 { 01101 // Selected button needs to end up in the middle 01102 left_spacing = m_leftColumns ? (m_contentsRect.width() / 2 - 01103 left_width) / m_leftColumns : 0; 01104 right_spacing = m_rightColumns ? (m_contentsRect.width() / 2 - 01105 right_width) / m_rightColumns : 0; 01106 01107 if (m_arrange == ArrangeSpread) 01108 { 01109 // Use same spacing on both sides of selected button 01110 if (!m_leftColumns || left_spacing > right_spacing) 01111 left_spacing = right_spacing; 01112 else 01113 right_spacing = left_spacing; 01114 } 01115 } 01116 else 01117 { 01118 // Buttons will be evenly spread out to fill entire area 01119 left_spacing = right_spacing = (m_contentsRect.width() - 01120 (left_width + right_width)) / 01121 (m_leftColumns + m_rightColumns); 01122 } 01123 } 01124 01125 // Add in intra-button space size 01126 left_width += (left_spacing * m_leftColumns); 01127 right_width += (right_spacing * m_rightColumns); 01128 } 01129 01130 /* 01131 * Calculate left margin 01132 */ 01133 x_init = m_contentsRect.x(); 01134 01135 if ((m_alignment & Qt::AlignHCenter) && m_arrange != ArrangeFill) 01136 { 01137 if (m_scrollStyle == ScrollCenter) 01138 { 01139 // Compensate for left being smaller than right 01140 x_init += qMax(right_width - left_width, 0); 01141 total = qMax(left_width, right_width) * 2; 01142 } 01143 else 01144 total = left_width + right_width; 01145 01146 // Adjust left margin so selected button ends up in the middle 01147 x_init += (qMax(m_contentsRect.width() - total, 2) / 2); 01148 } 01149 else if ((m_alignment & Qt::AlignRight) && m_arrange == ArrangeStack) 01150 { 01151 // Adjust left margin, so buttons are right justified 01152 x_init += qMax(m_contentsRect.width() - 01153 (left_width + right_width), 0); 01154 } 01155 01156 status_msg += QString(" spacing left %1 right %2 fixed %3 offset %4") 01157 .arg(left_spacing).arg(right_spacing) 01158 .arg(m_itemHorizSpacing).arg(x_init); 01159 LOG(VB_GUI, LOG_DEBUG, status_msg); 01160 01161 top_spacing += m_itemVertSpacing; 01162 bottom_spacing += m_itemVertSpacing; 01163 left_spacing += m_itemHorizSpacing; 01164 right_spacing += m_itemHorizSpacing; 01165 01166 MythUIStateType *realButton = NULL; 01167 MythUIGroup *buttonstate; 01168 01169 // Calculate position of each button 01170 int vertical_spacing, horizontal_spacing; 01171 int buttonIdx = first_button - skip_cols; 01172 01173 vertical_spacing = top_spacing; 01174 01175 for (row = 0; row < m_rows; ++row) 01176 { 01177 x = x_init; 01178 horizontal_spacing = left_spacing; 01179 01180 for (col = 0; col < m_columns && buttonIdx <= last_button; ++col) 01181 { 01182 if (buttonIdx >= first_button) 01183 { 01184 realButton = m_ButtonList[buttonIdx]; 01185 buttonstate = dynamic_cast<MythUIGroup *> 01186 (realButton->GetCurrentState()); 01187 if (!buttonstate) 01188 break; // Not continue 01189 01190 MythRect area = buttonstate->GetArea(); 01191 01192 // Center button within width of column 01193 if (m_alignment & Qt::AlignHCenter) 01194 x_adj = (col_widths[col] - minButtonWidth(area)) / 2; 01195 else if (m_alignment & Qt::AlignRight) 01196 x_adj = (col_widths[col] - minButtonWidth(area)); 01197 else 01198 x_adj = 0; 01199 if (m_layout == LayoutHorizontal) 01200 x_adj -= area.x(); // Negate button's own offset 01201 01202 // Center button within height of row. 01203 if (m_alignment & Qt::AlignVCenter) 01204 y_adj = (row_heights[row] - minButtonHeight(area)) / 2; 01205 else if (m_alignment & Qt::AlignBottom) 01206 y_adj = (row_heights[row] - minButtonHeight(area)); 01207 else 01208 y_adj = 0; 01209 if (m_layout == LayoutVertical) 01210 y_adj -= area.y(); // Negate button's own offset 01211 01212 // Set position of button 01213 realButton->SetPosition(x + x_adj, y + y_adj); 01214 realButton->SetVisible(true); 01215 01216 if (col == selected_column) 01217 { 01218 horizontal_spacing = right_spacing; 01219 if (row == selected_row) 01220 realButton->MoveToTop(); 01221 } 01222 } 01223 x += col_widths[col] + horizontal_spacing; 01224 ++buttonIdx; 01225 } 01226 01227 if (row == selected_row) 01228 vertical_spacing = bottom_spacing; 01229 01230 y += row_heights[row] + vertical_spacing; 01231 } 01232 01233 m_itemsVisible = m_columns * m_rows; 01234 01235 // Hide buttons before first active button 01236 for (buttonIdx = 0; buttonIdx < first_button; ++buttonIdx) 01237 m_ButtonList[buttonIdx]->SetVisible(false); 01238 01239 // Hide buttons after last active buttons. 01240 for (buttonIdx = m_maxVisible - 1; buttonIdx > last_button; --buttonIdx) 01241 m_ButtonList[buttonIdx]->SetVisible(false); 01242 01243 // Set m_topPosition so arrows are displayed correctly. 01244 if (m_scrollStyle == ScrollCenter || m_scrollStyle == ScrollGroupCenter) 01245 m_topPosition = static_cast<int>(m_itemsVisible) < m_itemCount; 01246 else 01247 m_topPosition = first_item; 01248 01249 delete[] col_widths; 01250 return true; 01251 } 01252 01253 void MythUIButtonList::SetPosition(void) 01254 { 01255 if (m_ButtonList.size() == 0) 01256 return; 01257 01258 int button = 0; 01259 01260 switch (m_scrollStyle) 01261 { 01262 case ScrollCenter: 01263 case ScrollGroupCenter: 01264 m_topPosition = qMax(m_selPosition - 01265 (int)((float)m_itemsVisible / 2), 0); 01266 break; 01267 case ScrollFree: 01268 { 01269 int adjust = 0; 01270 01271 if (m_topPosition == -1 || m_keepSelAtBottom) 01272 { 01273 if (m_topPosition == -1) 01274 m_topPosition = 0; 01275 01276 if (m_layout == LayoutHorizontal) 01277 adjust = 1 - m_itemsVisible; 01278 else 01279 adjust = m_columns - m_itemsVisible; 01280 01281 m_keepSelAtBottom = false; 01282 } 01283 01284 if (m_selPosition < m_topPosition || 01285 m_topPosition + (int)m_itemsVisible <= m_selPosition) 01286 { 01287 if (m_layout == LayoutHorizontal) 01288 m_topPosition = m_selPosition + adjust; 01289 else 01290 m_topPosition = (m_selPosition + adjust) / 01291 m_columns * m_columns; 01292 } 01293 01294 m_topPosition = qMax(m_topPosition, 0); 01295 break; 01296 } 01297 } 01298 01299 QList<MythUIButtonListItem *>::iterator it = m_itemList.begin() + m_topPosition; 01300 01301 if (m_scrollStyle == ScrollCenter || m_scrollStyle == ScrollGroupCenter) 01302 { 01303 if (m_selPosition <= (int)(m_itemsVisible / 2)) 01304 { 01305 button = (m_itemsVisible / 2) - m_selPosition; 01306 01307 if (m_wrapStyle == WrapItems && button > 0 && 01308 m_itemCount >= (int)m_itemsVisible) 01309 { 01310 it = m_itemList.end() - button; 01311 button = 0; 01312 } 01313 } 01314 else if ((m_itemCount - m_selPosition) < (int)(m_itemsVisible / 2)) 01315 { 01316 it = m_itemList.begin() + m_selPosition - (m_itemsVisible / 2); 01317 } 01318 } 01319 else if (m_drawFromBottom && m_itemCount < (int)m_itemsVisible) 01320 button = m_itemsVisible - m_itemCount; 01321 01322 for (int i = 0; i < button; i++) 01323 m_ButtonList[i]->SetVisible(false); 01324 01325 bool seenSelected = false; 01326 01327 MythUIStateType *realButton = NULL; 01328 MythUIButtonListItem *buttonItem = NULL; 01329 01330 if (it < m_itemList.begin()) 01331 it = m_itemList.begin(); 01332 01333 int curItem = GetItemPos(*it); 01334 01335 while (it < m_itemList.end() && button < (int)m_itemsVisible) 01336 { 01337 realButton = m_ButtonList[button]; 01338 buttonItem = *it; 01339 01340 if (!realButton || !buttonItem) 01341 break; 01342 01343 bool selected = false; 01344 01345 if (!seenSelected && (curItem == m_selPosition)) 01346 { 01347 seenSelected = true; 01348 selected = true; 01349 } 01350 01351 m_ButtonToItem[button] = buttonItem; 01352 buttonItem->SetToRealButton(realButton, selected); 01353 realButton->SetVisible(true); 01354 01355 if (m_wrapStyle == WrapItems && it == (m_itemList.end() - 1) && 01356 m_itemCount >= (int)m_itemsVisible) 01357 { 01358 it = m_itemList.begin(); 01359 curItem = 0; 01360 } 01361 else 01362 { 01363 ++it; 01364 ++curItem; 01365 } 01366 01367 button++; 01368 } 01369 01370 for (; button < (int)m_itemsVisible; button++) 01371 m_ButtonList[button]->SetVisible(false); 01372 01373 } 01374 01375 void MythUIButtonList::SanitizePosition(void) 01376 { 01377 if (m_selPosition < 0) 01378 m_selPosition = (m_wrapStyle > WrapNone) ? m_itemList.size() - 1 : 0; 01379 else if (m_selPosition >= m_itemList.size()) 01380 m_selPosition = (m_wrapStyle > WrapNone) ? 0 : m_itemList.size() - 1; 01381 } 01382 01383 void MythUIButtonList::SetPositionArrowStates() 01384 { 01385 if (!m_initialized) 01386 Init(); 01387 01388 if (!m_initialized) 01389 return; 01390 01391 if (m_clearing) 01392 return; 01393 01394 m_needsUpdate = false; 01395 01396 // set topitem, top position 01397 SanitizePosition(); 01398 m_ButtonToItem.clear(); 01399 01400 if (m_arrange == ArrangeFixed) 01401 SetPosition(); 01402 else 01403 DistributeButtons(); 01404 01405 updateLCD(); 01406 01407 m_needsUpdate = false; 01408 01409 if (!m_downArrow || !m_upArrow) 01410 return; 01411 01412 if (m_itemCount == 0) 01413 { 01414 m_downArrow->DisplayState(MythUIStateType::Off); 01415 m_upArrow->DisplayState(MythUIStateType::Off); 01416 } 01417 else 01418 { 01419 if (m_topPosition != 0) 01420 m_upArrow->DisplayState(MythUIStateType::Full); 01421 else 01422 m_upArrow->DisplayState(MythUIStateType::Off); 01423 01424 if (m_topPosition + (int)m_itemsVisible < m_itemCount) 01425 m_downArrow->DisplayState(MythUIStateType::Full); 01426 else 01427 m_downArrow->DisplayState(MythUIStateType::Off); 01428 01429 m_upArrow->MoveToTop(); 01430 m_downArrow->MoveToTop(); 01431 } 01432 } 01433 01434 void MythUIButtonList::ItemVisible(MythUIButtonListItem *item) 01435 { 01436 if (item) 01437 emit itemVisible(item); 01438 } 01439 01440 void MythUIButtonList::InsertItem(MythUIButtonListItem *item, int listPosition) 01441 { 01442 bool wasEmpty = m_itemList.isEmpty(); 01443 01444 if (listPosition >= 0 && listPosition <= m_itemList.count()) 01445 { 01446 m_itemList.insert(listPosition, item); 01447 01448 if (listPosition <= m_selPosition) 01449 m_selPosition++; 01450 01451 if (listPosition <= m_topPosition) 01452 m_topPosition++; 01453 } 01454 else 01455 m_itemList.append(item); 01456 01457 m_itemCount++; 01458 01459 if (wasEmpty) 01460 { 01461 m_selPosition = m_topPosition = 0; 01462 emit itemSelected(item); 01463 } 01464 01465 Update(); 01466 } 01467 01468 void MythUIButtonList::RemoveItem(MythUIButtonListItem *item) 01469 { 01470 if (m_clearing) 01471 return; 01472 01473 int curIndex = m_itemList.indexOf(item); 01474 01475 if (curIndex == -1) 01476 return; 01477 01478 if (curIndex == m_topPosition && 01479 m_topPosition > 0 && 01480 m_topPosition == m_itemCount - 1) 01481 { 01482 m_topPosition--; 01483 } 01484 01485 if (curIndex == m_selPosition && 01486 m_selPosition > 0 && 01487 m_selPosition == m_itemCount - 1) 01488 { 01489 m_selPosition--; 01490 } 01491 01492 m_itemList.removeAt(curIndex); 01493 m_itemCount--; 01494 01495 Update(); 01496 01497 if (m_selPosition < m_itemCount) 01498 emit itemSelected(m_itemList.at(m_selPosition)); 01499 else 01500 emit itemSelected(NULL); 01501 } 01502 01503 void MythUIButtonList::SetValueByData(QVariant data) 01504 { 01505 if (!m_initialized) 01506 Init(); 01507 01508 for (int i = 0; i < m_itemList.size(); ++i) 01509 { 01510 MythUIButtonListItem *item = m_itemList.at(i); 01511 01512 if (item->GetData() == data) 01513 { 01514 SetItemCurrent(item); 01515 return; 01516 } 01517 } 01518 } 01519 01520 void MythUIButtonList::SetItemCurrent(MythUIButtonListItem *item) 01521 { 01522 int newIndex = m_itemList.indexOf(item); 01523 SetItemCurrent(newIndex); 01524 } 01525 01526 void MythUIButtonList::SetItemCurrent(int current, int topPosition) 01527 { 01528 if (!m_initialized) 01529 Init(); 01530 01531 if (current == -1 || current >= m_itemList.size()) 01532 return; 01533 01534 if (current == m_selPosition && 01535 (topPosition == -1 || topPosition == m_topPosition)) 01536 return; 01537 01538 m_topPosition = topPosition; 01539 01540 if (topPosition > 0 && m_layout == LayoutGrid) 01541 m_topPosition -= (topPosition % m_columns); 01542 01543 m_selPosition = current; 01544 01545 Update(); 01546 01547 emit itemSelected(GetItemCurrent()); 01548 } 01549 01550 MythUIButtonListItem *MythUIButtonList::GetItemCurrent() const 01551 { 01552 if (m_itemList.isEmpty() || m_selPosition > m_itemList.size() || 01553 m_selPosition < 0) 01554 return NULL; 01555 01556 return m_itemList.at(m_selPosition); 01557 } 01558 01559 int MythUIButtonList::GetIntValue() const 01560 { 01561 MythUIButtonListItem *item = GetItemCurrent(); 01562 01563 if (item) 01564 return item->GetText().toInt(); 01565 01566 return 0; 01567 } 01568 01569 QString MythUIButtonList::GetValue() const 01570 { 01571 MythUIButtonListItem *item = GetItemCurrent(); 01572 01573 if (item) 01574 return item->GetText(); 01575 01576 return QString(); 01577 } 01578 01579 QVariant MythUIButtonList::GetDataValue() const 01580 { 01581 MythUIButtonListItem *item = GetItemCurrent(); 01582 01583 if (item) 01584 return item->GetData(); 01585 01586 return QVariant(); 01587 } 01588 01589 MythRect MythUIButtonList::GetButtonArea(void) const 01590 { 01591 if (m_contentsRect.isValid()) 01592 return m_contentsRect; 01593 return m_Area; 01594 } 01595 01596 MythUIButtonListItem *MythUIButtonList::GetItemFirst() const 01597 { 01598 if (!m_itemList.empty()) 01599 return m_itemList[0]; 01600 01601 return NULL; 01602 } 01603 01604 MythUIButtonListItem *MythUIButtonList::GetItemNext(MythUIButtonListItem *item) 01605 const 01606 { 01607 QListIterator<MythUIButtonListItem *> it(m_itemList); 01608 01609 if (!it.findNext(item)) 01610 return 0; 01611 01612 return it.previous(); 01613 } 01614 01615 int MythUIButtonList::GetCount() const 01616 { 01617 return m_itemCount; 01618 } 01619 01620 uint MythUIButtonList::GetVisibleCount() 01621 { 01622 if (m_needsUpdate) 01623 { 01624 SetPositionArrowStates(); 01625 SetScrollBarPosition(); 01626 } 01627 01628 return m_itemsVisible; 01629 } 01630 01631 bool MythUIButtonList::IsEmpty() const 01632 { 01633 if (m_itemCount > 0) 01634 return false; 01635 else 01636 return true; 01637 } 01638 01639 MythUIButtonListItem *MythUIButtonList::GetItemAt(int pos) const 01640 { 01641 if (pos < 0 || pos >= m_itemList.size()) 01642 return NULL; 01643 01644 return m_itemList.at(pos); 01645 } 01646 01647 MythUIButtonListItem *MythUIButtonList::GetItemByData(QVariant data) 01648 { 01649 if (!m_initialized) 01650 Init(); 01651 01652 for (int i = 0; i < m_itemList.size(); ++i) 01653 { 01654 MythUIButtonListItem *item = m_itemList.at(i); 01655 01656 if (item->GetData() == data) 01657 return item; 01658 } 01659 01660 return NULL; 01661 } 01662 01663 int MythUIButtonList::GetItemPos(MythUIButtonListItem *item) const 01664 { 01665 if (!item) 01666 return -1; 01667 01668 return m_itemList.indexOf(item); 01669 } 01670 01671 void MythUIButtonList::InitButton(int itemIdx, MythUIStateType* & realButton, 01672 MythUIButtonListItem* & buttonItem) 01673 { 01674 buttonItem = m_itemList[itemIdx]; 01675 01676 if (m_maxVisible == 0) 01677 { 01678 QString name("buttonlist button 0"); 01679 MythUIStateType *button = new MythUIStateType(this, name); 01680 button->CopyFrom(m_buttontemplate); 01681 button->ConnectDependants(true); 01682 m_ButtonList.append(button); 01683 ++m_maxVisible; 01684 } 01685 01686 realButton = m_ButtonList[0]; 01687 m_ButtonToItem[0] = buttonItem; 01688 } 01689 01690 /* 01691 * PageUp and PageDown are helpers when Dynamic layout is being used. 01692 * 01693 * When buttons are layed out dynamically, the number of buttons on the next 01694 * page, may not equal the number of buttons on the current page. Dynamic 01695 * layout is always center-weighted, so attempt to figure out which button 01696 * is near the middle on the next page. 01697 */ 01698 01699 int MythUIButtonList::PageUp(void) 01700 { 01701 int pos = m_selPosition; 01702 int total = 0; 01703 MythUIGroup *buttonstate; 01704 MythUIStateType *realButton; 01705 MythUIButtonListItem *buttonItem; 01706 01707 /* 01708 * /On the new page/ 01709 * If the number of buttons before the selected button does not equal 01710 * the number of buttons after the selected button, this logic can 01711 * undershoot the new selected button. That is better than overshooting 01712 * though. 01713 * 01714 * To fix this would require laying out the new page and then figuring 01715 * out which button should be selected, but this is already complex enough. 01716 */ 01717 01718 if (m_layout == LayoutHorizontal) 01719 { 01720 pos -= (m_leftColumns + 1); 01721 01722 int max_width = m_contentsRect.width() / 2; 01723 01724 for (; pos >= 0; --pos) 01725 { 01726 InitButton(pos, realButton, buttonItem); 01727 buttonItem->SetToRealButton(realButton, true); 01728 buttonstate = dynamic_cast<MythUIGroup *> 01729 (realButton->GetCurrentState()); 01730 01731 if (buttonstate == NULL) 01732 { 01733 LOG(VB_GENERAL, LOG_ERR, 01734 "PageUp: Failed to query buttonlist state"); 01735 return pos; 01736 } 01737 01738 if (total + m_itemHorizSpacing + 01739 buttonstate->GetArea().width() / 2 >= max_width) 01740 return pos + 1; 01741 01742 buttonItem->SetToRealButton(realButton, false); 01743 buttonstate = dynamic_cast<MythUIGroup *> 01744 (realButton->GetCurrentState()); 01745 total += m_itemHorizSpacing + buttonstate->GetArea().width(); 01746 } 01747 01748 return 0; 01749 } 01750 01751 // Grid or Vertical 01752 int dec; 01753 01754 if (m_layout == LayoutGrid) 01755 { 01756 /* 01757 * Adjusting using bottomRow:TopRow only works if new page 01758 * has the same ratio as the previous page, but that is common 01759 * with the grid layout, so go for it. If themers start doing 01760 * grids where this is not true, then this will need to be modified. 01761 */ 01762 pos -= (m_columns * (m_topRows + 2 + 01763 qMax(m_bottomRows - m_topRows, 0))); 01764 dec = m_columns; 01765 } 01766 else 01767 { 01768 pos -= (m_topRows + 1); 01769 dec = 1; 01770 } 01771 01772 int max_height = m_contentsRect.height() / 2; 01773 01774 for (; pos >= 0; pos -= dec) 01775 { 01776 InitButton(pos, realButton, buttonItem); 01777 buttonItem->SetToRealButton(realButton, true); 01778 buttonstate = dynamic_cast<MythUIGroup *> 01779 (realButton->GetCurrentState()); 01780 01781 if (buttonstate == NULL) 01782 { 01783 LOG(VB_GENERAL, LOG_ERR, 01784 "PageUp: Failed to query buttonlist state"); 01785 return pos; 01786 } 01787 01788 if (total + m_itemHorizSpacing + 01789 buttonstate->GetArea().height() / 2 >= max_height) 01790 return pos + dec; 01791 01792 buttonItem->SetToRealButton(realButton, false); 01793 buttonstate = dynamic_cast<MythUIGroup *> 01794 (realButton->GetCurrentState()); 01795 total += m_itemHorizSpacing + buttonstate->GetArea().height(); 01796 } 01797 01798 return 0; 01799 } 01800 01801 int MythUIButtonList::PageDown(void) 01802 { 01803 int pos = m_selPosition; 01804 int num_items = m_itemList.size(); 01805 int total = 0; 01806 MythUIGroup *buttonstate; 01807 MythUIStateType *realButton; 01808 MythUIButtonListItem *buttonItem; 01809 01810 /* 01811 * /On the new page/ 01812 * If the number of buttons before the selected button does not equal 01813 * the number of buttons after the selected button, this logic can 01814 * undershoot the new selected button. That is better than overshooting 01815 * though. 01816 * 01817 * To fix this would require laying out the new page and then figuring 01818 * out which button should be selected, but this is already complex enough. 01819 */ 01820 01821 if (m_layout == LayoutHorizontal) 01822 { 01823 pos += (m_rightColumns + 1); 01824 01825 int max_width = m_contentsRect.width() / 2; 01826 01827 for (; pos < num_items; ++pos) 01828 { 01829 InitButton(pos, realButton, buttonItem); 01830 buttonItem->SetToRealButton(realButton, true); 01831 buttonstate = dynamic_cast<MythUIGroup *> 01832 (realButton->GetCurrentState()); 01833 01834 if (buttonstate == NULL) 01835 { 01836 LOG(VB_GENERAL, LOG_ERR, 01837 "PageDown: Failed to query buttonlist state"); 01838 return pos; 01839 } 01840 01841 if (total + m_itemHorizSpacing + 01842 buttonstate->GetArea().width() / 2 >= max_width) 01843 return pos - 1; 01844 01845 buttonItem->SetToRealButton(realButton, false); 01846 buttonstate = dynamic_cast<MythUIGroup *> 01847 (realButton->GetCurrentState()); 01848 total += m_itemHorizSpacing + buttonstate->GetArea().width(); 01849 } 01850 01851 return num_items - 1; 01852 } 01853 01854 // Grid or Vertical 01855 int inc; 01856 01857 if (m_layout == LayoutGrid) 01858 { 01859 /* 01860 * Adjusting using bottomRow:TopRow only works if new page 01861 * has the same ratio as the previous page, but that is common 01862 * with the grid layout, so go for it. If themers start doing 01863 * grids where this is not true, then this will need to be modified. 01864 */ 01865 pos += (m_columns * (m_bottomRows + 2 + 01866 qMax(m_topRows - m_bottomRows, 0))); 01867 inc = m_columns; 01868 } 01869 else 01870 { 01871 pos += (m_bottomRows + 1); 01872 inc = 1; 01873 } 01874 01875 int max_height = m_contentsRect.height() / 2; 01876 01877 for (; pos < num_items; pos += inc) 01878 { 01879 InitButton(pos, realButton, buttonItem); 01880 buttonItem->SetToRealButton(realButton, true); 01881 buttonstate = dynamic_cast<MythUIGroup *> 01882 (realButton->GetCurrentState()); 01883 01884 if (!buttonstate) 01885 { 01886 LOG(VB_GENERAL, LOG_ERR, 01887 "PageDown: Failed to query buttonlist state"); 01888 return pos; 01889 } 01890 01891 if (total + m_itemHorizSpacing + 01892 buttonstate->GetArea().height() / 2 >= max_height) 01893 return pos - inc; 01894 01895 buttonItem->SetToRealButton(realButton, false); 01896 buttonstate = dynamic_cast<MythUIGroup *> 01897 (realButton->GetCurrentState()); 01898 if (buttonstate) 01899 total += m_itemHorizSpacing + buttonstate->GetArea().height(); 01900 } 01901 01902 return num_items - 1; 01903 } 01904 01905 bool MythUIButtonList::MoveUp(MovementUnit unit, uint amount) 01906 { 01907 int pos = m_selPosition; 01908 01909 if (pos == -1 || m_itemList.isEmpty() || !m_initialized) 01910 return false; 01911 01912 switch (unit) 01913 { 01914 case MoveItem: 01915 if (m_selPosition > 0) 01916 --m_selPosition; 01917 else if (m_wrapStyle > WrapNone) 01918 m_selPosition = m_itemList.size() - 1; 01919 else if (m_wrapStyle == WrapCaptive) 01920 return true; 01921 01922 break; 01923 01924 case MoveColumn: 01925 if (pos % m_columns > 0) 01926 --m_selPosition; 01927 else if (m_wrapStyle == WrapFlowing) 01928 if (m_selPosition == 0) 01929 --m_selPosition = m_itemList.size() - 1; 01930 else 01931 --m_selPosition; 01932 else if (m_wrapStyle > WrapNone) 01933 m_selPosition = pos + (m_columns - 1); 01934 else if (m_wrapStyle == WrapCaptive) 01935 return true; 01936 01937 break; 01938 01939 case MoveRow: 01940 if (m_scrollStyle != ScrollFree) 01941 { 01942 m_selPosition -= m_columns; 01943 if (m_selPosition < 0) 01944 m_selPosition += m_itemList.size(); 01945 else 01946 m_selPosition %= m_itemList.size(); 01947 } 01948 else if ((pos - m_columns) >= 0) 01949 m_selPosition -= m_columns; 01950 else if (m_wrapStyle > WrapNone) 01951 { 01952 m_selPosition = ((m_itemList.size() - 1) / m_columns) * 01953 m_columns + pos; 01954 01955 if ((m_selPosition / m_columns) 01956 < ((m_itemList.size() - 1) / m_columns)) 01957 m_selPosition = m_itemList.size() - 1; 01958 01959 if (m_layout == LayoutVertical) 01960 m_topPosition = qMax(0, m_selPosition - (int)m_itemsVisible + 1); 01961 } 01962 else if (m_wrapStyle == WrapCaptive) 01963 return true; 01964 01965 break; 01966 01967 case MovePage: 01968 if (m_arrange == ArrangeFixed) 01969 m_selPosition = qMax(0, m_selPosition - (int)m_itemsVisible); 01970 else 01971 m_selPosition = PageUp(); 01972 01973 break; 01974 01975 case MoveMid: 01976 m_selPosition = (int)(m_itemList.size() / 2); 01977 break; 01978 01979 case MoveMax: 01980 m_selPosition = 0; 01981 break; 01982 01983 case MoveByAmount: 01984 for (uint i = 0; i < amount; ++i) 01985 { 01986 if (m_selPosition > 0) 01987 --m_selPosition; 01988 else if (m_wrapStyle > WrapNone) 01989 m_selPosition = m_itemList.size() - 1; 01990 } 01991 01992 break; 01993 } 01994 01995 SanitizePosition(); 01996 01997 if (pos != m_selPosition) 01998 { 01999 Update(); 02000 emit itemSelected(GetItemCurrent()); 02001 } 02002 else 02003 return false; 02004 02005 return true; 02006 } 02007 02008 bool MythUIButtonList::MoveDown(MovementUnit unit, uint amount) 02009 { 02010 int pos = m_selPosition; 02011 02012 if (pos == -1 || m_itemList.isEmpty() || !m_initialized) 02013 return false; 02014 02015 switch (unit) 02016 { 02017 case MoveItem: 02018 if (m_selPosition < m_itemList.size() - 1) 02019 ++m_selPosition; 02020 else if (m_wrapStyle > WrapNone) 02021 m_selPosition = 0; 02022 else if (m_wrapStyle == WrapCaptive) 02023 return true; 02024 02025 break; 02026 02027 case MoveColumn: 02028 if ((pos + 1) % m_columns > 0) 02029 ++m_selPosition; 02030 else if (m_wrapStyle == WrapFlowing) 02031 if (m_selPosition < m_itemList.size() - 1) 02032 ++m_selPosition; 02033 else 02034 m_selPosition = 0; 02035 else if (m_wrapStyle > WrapNone) 02036 m_selPosition = pos - (m_columns - 1); 02037 else if (m_wrapStyle == WrapCaptive) 02038 return true; 02039 02040 break; 02041 02042 case MoveRow: 02043 if (m_scrollStyle != ScrollFree) 02044 { 02045 m_selPosition += m_columns; 02046 m_selPosition %= m_itemList.size(); 02047 } 02048 else if (((m_itemList.size() - 1) / qMax(m_columns, 0)) 02049 > (pos / m_columns)) 02050 { 02051 m_selPosition += m_columns; 02052 if (m_selPosition > m_itemList.size() - 1) 02053 m_selPosition = m_itemList.size() - 1; 02054 } 02055 else if (m_wrapStyle > WrapNone) 02056 m_selPosition = (pos % m_columns); 02057 else if (m_wrapStyle == WrapCaptive) 02058 return true; 02059 02060 break; 02061 02062 case MovePage: 02063 if (m_arrange == ArrangeFixed) 02064 m_selPosition = qMin(m_itemCount - 1, 02065 m_selPosition + (int)m_itemsVisible); 02066 else 02067 m_selPosition = PageDown(); 02068 02069 break; 02070 02071 case MoveMax: 02072 m_selPosition = m_itemCount - 1; 02073 break; 02074 02075 case MoveByAmount: 02076 for (uint i = 0; i < amount; ++i) 02077 { 02078 if (m_selPosition < m_itemList.size() - 1) 02079 ++m_selPosition; 02080 else if (m_wrapStyle > WrapNone) 02081 m_selPosition = 0; 02082 } 02083 02084 break; 02085 } 02086 02087 SanitizePosition(); 02088 02089 if (pos != m_selPosition) 02090 { 02091 m_keepSelAtBottom = true; 02092 Update(); 02093 emit itemSelected(GetItemCurrent()); 02094 } 02095 else 02096 return false; 02097 02098 return true; 02099 } 02100 02101 bool MythUIButtonList::MoveToNamedPosition(const QString &position_name) 02102 { 02103 if (!m_initialized) 02104 Init(); 02105 02106 if (m_selPosition < 0 || m_itemList.isEmpty() || !m_initialized) 02107 return false; 02108 02109 bool found_it = false; 02110 int selectedPosition = 0; 02111 QList<MythUIButtonListItem *>::iterator it = m_itemList.begin(); 02112 02113 while (it != m_itemList.end()) 02114 { 02115 if ((*it)->GetText() == position_name) 02116 { 02117 found_it = true; 02118 break; 02119 } 02120 02121 ++it; 02122 ++selectedPosition; 02123 } 02124 02125 if (!found_it || m_selPosition == selectedPosition) 02126 return false; 02127 02128 SetItemCurrent(selectedPosition); 02129 return true; 02130 } 02131 02132 bool MythUIButtonList::MoveItemUpDown(MythUIButtonListItem *item, bool up) 02133 { 02134 if (GetItemCurrent() != item) 02135 return false; 02136 02137 if (item == m_itemList.first() && up) 02138 return false; 02139 02140 if (item == m_itemList.last() && !up) 02141 return false; 02142 02143 int oldpos = m_selPosition; 02144 int insertat = 0; 02145 bool dolast = false; 02146 02147 if (up) 02148 { 02149 insertat = m_selPosition - 1; 02150 02151 if (item == m_itemList.last()) 02152 dolast = true; 02153 else 02154 ++m_selPosition; 02155 02156 if (item == m_itemList.at(m_topPosition)) 02157 ++m_topPosition; 02158 } 02159 else 02160 insertat = m_selPosition + 1; 02161 02162 m_itemList.removeAt(oldpos); 02163 m_itemList.insert(insertat, item); 02164 02165 if (up) 02166 { 02167 MoveUp(); 02168 02169 if (!dolast) 02170 MoveUp(); 02171 } 02172 else 02173 MoveDown(); 02174 02175 return true; 02176 } 02177 02178 void MythUIButtonList::SetAllChecked(MythUIButtonListItem::CheckState state) 02179 { 02180 QMutableListIterator<MythUIButtonListItem *> it(m_itemList); 02181 02182 while (it.hasNext()) 02183 it.next()->setChecked(state); 02184 } 02185 02186 void MythUIButtonList::Init() 02187 { 02188 if (m_initialized) 02189 return; 02190 02191 m_upArrow = dynamic_cast<MythUIStateType *>(GetChild("upscrollarrow")); 02192 m_downArrow = dynamic_cast<MythUIStateType *>(GetChild("downscrollarrow")); 02193 m_scrollBar = dynamic_cast<MythUIScrollBar *>(GetChild("scrollbar")); 02194 02195 if (m_upArrow) 02196 m_upArrow->SetVisible(true); 02197 02198 if (m_downArrow) 02199 m_downArrow->SetVisible(true); 02200 02201 if (m_scrollBar) 02202 m_scrollBar->SetVisible(m_showScrollBar); 02203 02204 m_contentsRect.CalculateArea(m_Area); 02205 02206 m_buttontemplate = dynamic_cast<MythUIStateType *>(GetChild("buttonitem")); 02207 02208 if (!m_buttontemplate) 02209 { 02210 LOG(VB_GENERAL, LOG_ERR, QString("(%1) Statetype buttonitem is " 02211 "required in mythuibuttonlist: %2") 02212 .arg(GetXMLLocation()).arg(objectName())); 02213 return; 02214 } 02215 02216 m_buttontemplate->SetVisible(false); 02217 02218 MythRect buttonItemArea; 02219 02220 MythUIGroup *buttonActiveState = dynamic_cast<MythUIGroup *> 02221 (m_buttontemplate->GetState("active")); 02222 02223 if (buttonActiveState) 02224 buttonItemArea = buttonActiveState->GetArea(); 02225 else 02226 buttonItemArea = m_buttontemplate->GetArea(); 02227 02228 buttonItemArea.CalculateArea(m_contentsRect); 02229 02230 m_itemHeight = buttonItemArea.height(); 02231 m_itemWidth = buttonItemArea.width(); 02232 02233 /* 02234 * If fixed spacing is defined, then use the "active" state size 02235 * to predictively determine the position of each button. 02236 */ 02237 if (m_arrange == ArrangeFixed) 02238 { 02239 02240 CalculateVisibleItems(); 02241 02242 int col = 1; 02243 int row = 1; 02244 02245 for (int i = 0; i < (int)m_itemsVisible; i++) 02246 { 02247 QString name = QString("buttonlist button %1").arg(i); 02248 MythUIStateType *button = new MythUIStateType(this, name); 02249 button->CopyFrom(m_buttontemplate); 02250 button->ConnectDependants(true); 02251 02252 if (col > m_columns) 02253 { 02254 col = 1; 02255 row++; 02256 } 02257 02258 button->SetPosition(GetButtonPosition(col, row)); 02259 col++; 02260 02261 m_ButtonList.push_back(button); 02262 } 02263 } 02264 02265 // The following is pretty much a hack for the benefit of MythGallery 02266 // it scales images based on the button size and we need to give it the 02267 // largest button state so that the images are not too small 02268 // This can be removed once the disk based image caching is added to 02269 // mythui, since the mythgallery thumbnail generator can be ditched. 02270 MythUIGroup *buttonSelectedState = dynamic_cast<MythUIGroup *> 02271 (m_buttontemplate->GetState("selected")); 02272 02273 if (buttonSelectedState) 02274 { 02275 MythRect itemArea = buttonSelectedState->GetArea(); 02276 itemArea.CalculateArea(m_contentsRect); 02277 02278 if (m_itemHeight < itemArea.height()) 02279 m_itemHeight = itemArea.height(); 02280 02281 if (m_itemWidth < itemArea.width()) 02282 m_itemWidth = itemArea.width(); 02283 } 02284 02285 // End Hack 02286 02287 m_initialized = true; 02288 } 02289 02290 uint MythUIButtonList::ItemWidth(void) 02291 { 02292 if (!m_initialized) 02293 Init(); 02294 02295 return m_itemWidth; 02296 } 02297 02298 uint MythUIButtonList::ItemHeight(void) 02299 { 02300 if (!m_initialized) 02301 Init(); 02302 02303 return m_itemHeight; 02304 } 02305 02309 bool MythUIButtonList::keyPressEvent(QKeyEvent *e) 02310 { 02311 QStringList actions; 02312 bool handled = false; 02313 handled = GetMythMainWindow()->TranslateKeyPress("Global", e, actions); 02314 02315 // Handle action remappings 02316 for (int i = 0; i < actions.size(); i++) 02317 { 02318 if (!m_actionRemap.contains(actions[i])) 02319 continue; 02320 02321 QString key = m_actionRemap[actions[i]]; 02322 if (key.isEmpty()) 02323 return true; 02324 02325 QKeySequence a(key); 02326 if (a.isEmpty()) 02327 continue; 02328 02329 int keyCode = a[0]; 02330 Qt::KeyboardModifiers modifiers = Qt::NoModifier; 02331 QStringList parts = key.split('+'); 02332 for (int j = 0; j < parts.count(); j++) 02333 { 02334 if (parts[j].toUpper() == "CTRL") 02335 modifiers |= Qt::ControlModifier; 02336 if (parts[j].toUpper() == "SHIFT") 02337 modifiers |= Qt::ShiftModifier; 02338 if (parts[j].toUpper() == "ALT") 02339 modifiers |= Qt::AltModifier; 02340 if (parts[j].toUpper() == "META") 02341 modifiers |= Qt::MetaModifier; 02342 } 02343 02344 QCoreApplication::postEvent( 02345 GetMythMainWindow(), 02346 new QKeyEvent(QEvent::KeyPress, keyCode, modifiers, key)); 02347 QCoreApplication::postEvent( 02348 GetMythMainWindow(), 02349 new QKeyEvent(QEvent::KeyRelease, keyCode, modifiers, key)); 02350 02351 return true; 02352 } 02353 02354 // handle actions for this container 02355 for (int i = 0; i < actions.size() && !handled; i++) 02356 { 02357 QString action = actions[i]; 02358 handled = true; 02359 02360 if (action == "UP") 02361 { 02362 if ((m_layout == LayoutVertical) || (m_layout == LayoutGrid)) 02363 handled = MoveUp(MoveRow); 02364 else 02365 handled = false; 02366 } 02367 else if (action == "DOWN") 02368 { 02369 if ((m_layout == LayoutVertical) || (m_layout == LayoutGrid)) 02370 handled = MoveDown(MoveRow); 02371 else 02372 handled = false; 02373 } 02374 else if (action == "RIGHT") 02375 { 02376 if (m_layout == LayoutHorizontal) 02377 handled = MoveDown(MoveItem); 02378 else if (m_layout == LayoutGrid) 02379 { 02380 if (m_scrollStyle == ScrollFree) 02381 handled = MoveDown(MoveColumn); 02382 else 02383 handled = MoveDown(MoveItem); 02384 } 02385 else 02386 handled = false; 02387 } 02388 else if (action == "LEFT") 02389 { 02390 if (m_layout == LayoutHorizontal) 02391 handled = MoveUp(MoveItem); 02392 else if (m_layout == LayoutGrid) 02393 { 02394 if (m_scrollStyle == ScrollFree) 02395 handled = MoveUp(MoveColumn); 02396 else 02397 handled = MoveUp(MoveItem); 02398 } 02399 else 02400 handled = false; 02401 } 02402 else if (action == "PAGEUP") 02403 { 02404 MoveUp(MovePage); 02405 } 02406 else if (action == "PAGEDOWN") 02407 { 02408 MoveDown(MovePage); 02409 } 02410 else if (action == "PAGETOP") 02411 { 02412 MoveUp(MoveMax); 02413 } 02414 else if (action == "PAGEMIDDLE") 02415 { 02416 MoveUp(MoveMid); 02417 } 02418 else if (action == "PAGEBOTTOM") 02419 { 02420 MoveDown(MoveMax); 02421 } 02422 else if (action == "SELECT") 02423 { 02424 MythUIButtonListItem *item = GetItemCurrent(); 02425 02426 if (item) 02427 emit itemClicked(item); 02428 } 02429 else if (action == "SEARCH") 02430 { 02431 ShowSearchDialog(); 02432 } 02433 else 02434 handled = false; 02435 } 02436 02437 return handled; 02438 } 02439 02443 bool MythUIButtonList::gestureEvent(MythGestureEvent *event) 02444 { 02445 bool handled = false; 02446 02447 if (event->gesture() == MythGestureEvent::Click) 02448 { 02449 // We want the relative position of the click 02450 QPoint position = event->GetPosition() - 02451 m_Parent->GetArea().topLeft(); 02452 02453 MythUIType *type = GetChildAt(position, false, false); 02454 02455 if (!type) 02456 return false; 02457 02458 MythUIStateType *object = dynamic_cast<MythUIStateType *>(type); 02459 02460 if (object) 02461 { 02462 handled = true; 02463 QString name = object->objectName(); 02464 02465 if (name == "upscrollarrow") 02466 { 02467 MoveUp(MovePage); 02468 } 02469 else if (name == "downscrollarrow") 02470 { 02471 MoveDown(MovePage); 02472 } 02473 else if (name.startsWith("buttonlist button")) 02474 { 02475 int pos = name.section(' ', 2, 2).toInt(); 02476 MythUIButtonListItem *item = m_ButtonToItem[pos]; 02477 02478 if (item) 02479 { 02480 if (item == GetItemCurrent()) 02481 emit itemClicked(item); 02482 else 02483 SetItemCurrent(item); 02484 } 02485 } 02486 else 02487 handled = false; 02488 } 02489 } 02490 02491 return handled; 02492 } 02493 02494 class NextButtonListPageEvent : public QEvent 02495 { 02496 public: 02497 NextButtonListPageEvent(int start, int pageSize) : 02498 QEvent(kEventType), m_start(start), m_pageSize(pageSize) {} 02499 const int m_start; 02500 const int m_pageSize; 02501 static Type kEventType; 02502 }; 02503 02504 QEvent::Type NextButtonListPageEvent::kEventType = 02505 (QEvent::Type) QEvent::registerEventType(); 02506 02507 void MythUIButtonList::customEvent(QEvent *event) 02508 { 02509 if (event->type() == NextButtonListPageEvent::kEventType) 02510 { 02511 NextButtonListPageEvent *npe = 02512 static_cast<NextButtonListPageEvent*>(event); 02513 int cur = npe->m_start; 02514 for (; cur < npe->m_start + npe->m_pageSize && cur < GetCount(); ++cur) 02515 { 02516 const int loginterval = (cur < 1000 ? 100 : 500); 02517 if (cur % loginterval == 0) 02518 LOG(VB_GENERAL, LOG_INFO, 02519 QString("Build background buttonlist item %1").arg(cur)); 02520 emit itemLoaded(GetItemAt(cur)); 02521 } 02522 m_nextItemLoaded = cur; 02523 if (cur < GetCount()) 02524 LoadInBackground(cur, npe->m_pageSize); 02525 } 02526 } 02527 02528 void MythUIButtonList::LoadInBackground(int start, int pageSize) 02529 { 02530 m_nextItemLoaded = start; 02531 QCoreApplication:: 02532 postEvent(this, new NextButtonListPageEvent(start, pageSize)); 02533 } 02534 02535 int MythUIButtonList::StopLoad(void) 02536 { 02537 QCoreApplication:: 02538 removePostedEvents(this, NextButtonListPageEvent::kEventType); 02539 return m_nextItemLoaded; 02540 } 02541 02542 QPoint MythUIButtonList::GetButtonPosition(int column, int row) const 02543 { 02544 int x = m_contentsRect.x() + 02545 ((column - 1) * (m_itemWidth + m_itemHorizSpacing)); 02546 int y = m_contentsRect.y() + 02547 ((row - 1) * (m_itemHeight + m_itemVertSpacing)); 02548 02549 return QPoint(x, y); 02550 } 02551 02552 void MythUIButtonList::CalculateVisibleItems(void) 02553 { 02554 m_itemsVisible = 0; 02555 m_rows = 0; 02556 m_columns = 0; 02557 02558 if ((m_layout == LayoutHorizontal) || (m_layout == LayoutGrid)) 02559 { 02560 int x = 0; 02561 02562 while (x <= m_contentsRect.width() - m_itemWidth) 02563 { 02564 x += m_itemWidth + m_itemHorizSpacing; 02565 m_columns++; 02566 } 02567 } 02568 02569 if ((m_layout == LayoutVertical) || (m_layout == LayoutGrid)) 02570 { 02571 int y = 0; 02572 02573 while (y <= m_contentsRect.height() - m_itemHeight) 02574 { 02575 y += m_itemHeight + m_itemVertSpacing; 02576 m_rows++; 02577 } 02578 } 02579 02580 if (m_rows <= 0) 02581 m_rows = 1; 02582 02583 if (m_columns <= 0) 02584 m_columns = 1; 02585 02586 m_itemsVisible = m_columns * m_rows; 02587 } 02588 02589 void MythUIButtonList::SetButtonArea(const MythRect &rect) 02590 { 02591 if (rect == m_contentsRect) 02592 return; 02593 02594 m_contentsRect = rect; 02595 02596 if (m_Area.isValid()) 02597 m_contentsRect.CalculateArea(m_Area); 02598 else if (m_Parent) 02599 m_contentsRect.CalculateArea(m_Parent->GetFullArea()); 02600 else 02601 m_contentsRect.CalculateArea(GetMythMainWindow()->GetUIScreenRect()); 02602 } 02603 02607 bool MythUIButtonList::ParseElement( 02608 const QString &filename, QDomElement &element, bool showWarnings) 02609 { 02610 if (element.tagName() == "buttonarea") 02611 SetButtonArea(parseRect(element)); 02612 else if (element.tagName() == "layout") 02613 { 02614 QString layout = getFirstText(element).toLower(); 02615 02616 if (layout == "grid") 02617 m_layout = LayoutGrid; 02618 else if (layout == "horizontal") 02619 m_layout = LayoutHorizontal; 02620 else 02621 m_layout = LayoutVertical; 02622 } 02623 else if (element.tagName() == "arrange") 02624 { 02625 QString arrange = getFirstText(element).toLower(); 02626 02627 if (arrange == "fill") 02628 m_arrange = ArrangeFill; 02629 else if (arrange == "spread") 02630 m_arrange = ArrangeSpread; 02631 else if (arrange == "stack") 02632 m_arrange = ArrangeStack; 02633 else 02634 m_arrange = ArrangeFixed; 02635 02636 } 02637 else if (element.tagName() == "align") 02638 { 02639 QString align = getFirstText(element).toLower(); 02640 m_alignment = parseAlignment(align); 02641 } 02642 else if (element.tagName() == "scrollstyle") 02643 { 02644 QString layout = getFirstText(element).toLower(); 02645 02646 if (layout == "center") 02647 m_scrollStyle = ScrollCenter; 02648 else if (layout == "groupcenter") 02649 m_scrollStyle = ScrollGroupCenter; 02650 else if (layout == "free") 02651 m_scrollStyle = ScrollFree; 02652 } 02653 else if (element.tagName() == "wrapstyle") 02654 { 02655 QString wrapstyle = getFirstText(element).toLower(); 02656 02657 if (wrapstyle == "captive") 02658 m_wrapStyle = WrapCaptive; 02659 else if (wrapstyle == "none") 02660 m_wrapStyle = WrapNone; 02661 else if (wrapstyle == "selection") 02662 m_wrapStyle = WrapSelect; 02663 else if (wrapstyle == "flowing") 02664 m_wrapStyle = WrapFlowing; 02665 else if (wrapstyle == "items") 02666 m_wrapStyle = WrapItems; 02667 } 02668 else if (element.tagName() == "showarrow") 02669 m_showArrow = parseBool(element); 02670 else if (element.tagName() == "showscrollbar") 02671 m_showScrollBar = parseBool(element); 02672 else if (element.tagName() == "spacing") 02673 { 02674 m_itemHorizSpacing = NormX(getFirstText(element).toInt()); 02675 m_itemVertSpacing = NormY(getFirstText(element).toInt()); 02676 } 02677 else if (element.tagName() == "drawfrombottom") 02678 { 02679 m_drawFromBottom = parseBool(element); 02680 02681 if (m_drawFromBottom) 02682 m_alignment |= Qt::AlignBottom; 02683 } 02684 else if (element.tagName() == "searchposition") 02685 { 02686 m_searchPosition = parsePoint(element); 02687 } 02688 else if (element.tagName() == "triggerevent") 02689 { 02690 QString trigger = getFirstText(element); 02691 if (!trigger.isEmpty()) 02692 { 02693 QString action = element.attribute("action", ""); 02694 if (action.isEmpty()) 02695 { 02696 m_actionRemap[trigger] = ""; 02697 } 02698 else 02699 { 02700 QString context = element.attribute("context", ""); 02701 QString keylist = GetMythMainWindow()->GetKey(context, action); 02702 QStringList keys = keylist.split(',', QString::SkipEmptyParts); 02703 if (!keys.empty()) 02704 m_actionRemap[trigger] = keys[0]; 02705 } 02706 } 02707 } 02708 else 02709 { 02710 return MythUIType::ParseElement(filename, element, showWarnings); 02711 } 02712 02713 return true; 02714 } 02715 02719 void MythUIButtonList::DrawSelf(MythPainter *, int, int, int, QRect) 02720 { 02721 if (m_needsUpdate) 02722 { 02723 SetPositionArrowStates(); 02724 SetScrollBarPosition(); 02725 } 02726 } 02727 02731 void MythUIButtonList::CreateCopy(MythUIType *parent) 02732 { 02733 MythUIButtonList *lb = new MythUIButtonList(parent, objectName()); 02734 lb->CopyFrom(this); 02735 } 02736 02740 void MythUIButtonList::CopyFrom(MythUIType *base) 02741 { 02742 MythUIButtonList *lb = dynamic_cast<MythUIButtonList *>(base); 02743 02744 if (!lb) 02745 return; 02746 02747 m_layout = lb->m_layout; 02748 m_arrange = lb->m_arrange; 02749 m_alignment = lb->m_alignment; 02750 02751 m_contentsRect = lb->m_contentsRect; 02752 02753 m_itemHeight = lb->m_itemHeight; 02754 m_itemWidth = lb->m_itemWidth; 02755 m_itemHorizSpacing = lb->m_itemHorizSpacing; 02756 m_itemVertSpacing = lb->m_itemVertSpacing; 02757 m_itemsVisible = lb->m_itemsVisible; 02758 m_maxVisible = lb->m_maxVisible; 02759 02760 m_active = lb->m_active; 02761 m_showArrow = lb->m_showArrow; 02762 m_showScrollBar = lb->m_showScrollBar; 02763 02764 m_drawFromBottom = lb->m_drawFromBottom; 02765 02766 m_scrollStyle = lb->m_scrollStyle; 02767 m_wrapStyle = lb->m_wrapStyle; 02768 02769 m_clearing = false; 02770 m_selPosition = m_topPosition = m_itemCount = 0; 02771 02772 m_searchPosition = lb->m_searchPosition; 02773 m_searchFields = lb->m_searchFields; 02774 02775 MythUIType::CopyFrom(base); 02776 02777 m_upArrow = dynamic_cast<MythUIStateType *>(GetChild("upscrollarrow")); 02778 m_downArrow = dynamic_cast<MythUIStateType *>(GetChild("downscrollarrow")); 02779 m_scrollBar = dynamic_cast<MythUIScrollBar *>(GetChild("scrollbar")); 02780 02781 for (int i = 0; i < (int)m_itemsVisible; i++) 02782 { 02783 MythUIType *deltype; 02784 QString name = QString("buttonlist button %1").arg(i); 02785 deltype = GetChild(name); 02786 delete deltype; 02787 } 02788 02789 m_ButtonList.clear(); 02790 02791 m_actionRemap = lb->m_actionRemap; 02792 02793 m_initialized = false; 02794 } 02795 02799 void MythUIButtonList::Finalize(void) 02800 { 02801 MythUIType::Finalize(); 02802 } 02803 02804 void MythUIButtonList::SetLCDTitles(const QString &title, const QString &columnList) 02805 { 02806 m_lcdTitle = title; 02807 m_lcdColumns = columnList.split('|'); 02808 } 02809 02810 void MythUIButtonList::updateLCD(void) 02811 { 02812 if (!m_HasFocus) 02813 return; 02814 02815 LCD *lcddev = LCD::Get(); 02816 02817 if (lcddev == NULL) 02818 return; 02819 02820 // Build a list of the menu items 02821 QList<LCDMenuItem> menuItems; 02822 bool selected; 02823 02824 int start = std::max(0, (int)(m_selPosition - lcddev->getLCDHeight())); 02825 int end = std::min(m_itemCount, (int)(start + (lcddev->getLCDHeight() * 2))); 02826 02827 for (int r = start; r < end; r++) 02828 { 02829 if (r == GetCurrentPos()) 02830 selected = true; 02831 else 02832 selected = false; 02833 02834 MythUIButtonListItem *item = GetItemAt(r); 02835 CHECKED_STATE state = NOTCHECKABLE; 02836 02837 if (item->checkable()) 02838 state = (item->state() == MythUIButtonListItem::NotChecked) ? UNCHECKED : CHECKED; 02839 02840 QString text; 02841 02842 for (int x = 0; x < m_lcdColumns.count(); x++) 02843 { 02844 if (!m_lcdColumns[x].isEmpty() && item->m_strings.contains(m_lcdColumns[x])) 02845 { 02846 // named text column 02847 TextProperties props = item->m_strings[m_lcdColumns[x]]; 02848 02849 if (text.isEmpty()) 02850 text = props.text; 02851 else 02852 text += " ~ " + props.text; 02853 } 02854 else 02855 { 02856 // default text column 02857 if (text.isEmpty()) 02858 text = item->GetText(); 02859 else 02860 text += " ~ " + item->GetText(); 02861 } 02862 } 02863 02864 if (!text.isEmpty()) 02865 menuItems.append(LCDMenuItem(selected, state, text)); 02866 else 02867 menuItems.append(LCDMenuItem(selected, state, item->GetText())); 02868 } 02869 02870 if (!menuItems.isEmpty()) 02871 lcddev->switchToMenu(menuItems, m_lcdTitle); 02872 } 02873 02874 void MythUIButtonList::ShowSearchDialog(void) 02875 { 02876 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack"); 02877 02878 SearchButtonListDialog *dlg = new SearchButtonListDialog(popupStack, "MythSearchListDialog", this, ""); 02879 02880 if (dlg->Create()) 02881 { 02882 if (m_searchPosition.x() != -2 || m_searchPosition.y() != -2) 02883 { 02884 int x = m_searchPosition.x(); 02885 int y = m_searchPosition.y(); 02886 QRect screenArea = GetMythMainWindow()->GetUIScreenRect(); 02887 QRect dialogArea = dlg->GetArea(); 02888 02889 if (x == -1) 02890 x = (screenArea.width() - dialogArea.width()) / 2; 02891 02892 if (y == -1) 02893 y = (screenArea.height() - dialogArea.height()) / 2; 02894 02895 dlg->SetPosition(x, y); 02896 } 02897 02898 popupStack->AddScreen(dlg); 02899 } 02900 else 02901 delete dlg; 02902 } 02903 02904 bool MythUIButtonList::Find(const QString &searchStr, bool startsWith) 02905 { 02906 m_searchStr = searchStr; 02907 m_searchStartsWith = startsWith; 02908 return DoFind(false, true); 02909 } 02910 02911 bool MythUIButtonList::FindNext(void) 02912 { 02913 return DoFind(true, true); 02914 } 02915 02916 bool MythUIButtonList::FindPrev(void) 02917 { 02918 return DoFind(true, false); 02919 } 02920 02921 bool MythUIButtonList::DoFind(bool doMove, bool searchForward) 02922 { 02923 if (m_searchStr.isEmpty()) 02924 return true; 02925 02926 if (GetCount() == 0) 02927 return false; 02928 02929 int startPos = GetCurrentPos(); 02930 int currPos = startPos; 02931 bool found = false; 02932 02933 if (doMove) 02934 { 02935 if (searchForward) 02936 { 02937 currPos++; 02938 02939 if (currPos >= GetCount()) 02940 currPos = 0; 02941 } 02942 else 02943 { 02944 currPos--; 02945 02946 if (currPos < 0) 02947 currPos = GetCount() - 1; 02948 } 02949 } 02950 02951 while (true) 02952 { 02953 found = GetItemAt(currPos)->FindText(m_searchStr, m_searchFields, m_searchStartsWith); 02954 02955 if (found) 02956 { 02957 SetItemCurrent(currPos); 02958 return true; 02959 } 02960 02961 if (searchForward) 02962 { 02963 currPos++; 02964 02965 if (currPos >= GetCount()) 02966 currPos = 0; 02967 } 02968 else 02969 { 02970 currPos--; 02971 02972 if (currPos < 0) 02973 currPos = GetCount() - 1; 02974 } 02975 02976 if (startPos == currPos) 02977 break; 02978 } 02979 02980 return false; 02981 } 02982 02984 02985 MythUIButtonListItem::MythUIButtonListItem(MythUIButtonList *lbtype, 02986 const QString &text, const QString &image, 02987 bool checkable, CheckState state, 02988 bool showArrow, int listPosition) 02989 { 02990 if (!lbtype) 02991 LOG(VB_GENERAL, LOG_ERR, "Cannot add a button to a non-existent list!"); 02992 02993 m_parent = lbtype; 02994 m_text = text; 02995 m_image = NULL; 02996 m_imageFilename = image; 02997 m_checkable = checkable; 02998 m_state = state; 02999 m_showArrow = showArrow; 03000 m_data = 0; 03001 03002 if (state >= NotChecked) 03003 m_checkable = true; 03004 03005 if (m_parent) 03006 m_parent->InsertItem(this, listPosition); 03007 } 03008 03009 MythUIButtonListItem::MythUIButtonListItem(MythUIButtonList *lbtype, 03010 const QString &text, 03011 QVariant data, int listPosition) 03012 { 03013 if (!lbtype) 03014 LOG(VB_GENERAL, LOG_ERR, "Cannot add a button to a non-existent list!"); 03015 03016 m_parent = lbtype; 03017 m_text = text; 03018 m_data = data; 03019 03020 m_image = NULL; 03021 03022 m_checkable = false; 03023 m_state = CantCheck; 03024 m_showArrow = false; 03025 03026 if (m_parent) 03027 m_parent->InsertItem(this, listPosition); 03028 } 03029 03030 MythUIButtonListItem::~MythUIButtonListItem() 03031 { 03032 if (m_parent) 03033 m_parent->RemoveItem(this); 03034 03035 if (m_image) 03036 m_image->DownRef(); 03037 03038 QMapIterator<QString, MythImage *> it(m_images); 03039 03040 while (it.hasNext()) 03041 { 03042 it.next(); 03043 it.value()->DownRef(); 03044 } 03045 } 03046 03047 void MythUIButtonListItem::SetText(const QString &text, const QString &name, 03048 const QString &state) 03049 { 03050 if (!name.isEmpty()) 03051 { 03052 TextProperties textprop; 03053 textprop.text = text; 03054 textprop.state = state; 03055 m_strings.insert(name, textprop); 03056 } 03057 else 03058 m_text = text; 03059 03060 if (m_parent) 03061 m_parent->Update(); 03062 } 03063 03064 void MythUIButtonListItem::SetTextFromMap(InfoMap &infoMap, 03065 const QString &state) 03066 { 03067 QHash<QString, QString>::iterator map_it = infoMap.begin(); 03068 03069 while (map_it != infoMap.end()) 03070 { 03071 TextProperties textprop; 03072 textprop.text = (*map_it); 03073 textprop.state = state; 03074 m_strings[map_it.key()] = textprop; 03075 ++map_it; 03076 } 03077 03078 if (m_parent) 03079 m_parent->Update(); 03080 } 03081 03082 void MythUIButtonListItem::SetTextFromMap(QMap<QString, TextProperties> &stringMap) 03083 { 03084 m_strings.clear(); 03085 m_strings = stringMap; 03086 } 03087 03088 QString MythUIButtonListItem::GetText(const QString &name) const 03089 { 03090 if (name.isEmpty()) 03091 return m_text; 03092 else if (m_strings.contains(name)) 03093 return m_strings[name].text; 03094 else 03095 return QString(); 03096 } 03097 03098 bool MythUIButtonListItem::FindText(const QString &searchStr, const QString &fieldList, 03099 bool startsWith) const 03100 { 03101 if (fieldList.isEmpty()) 03102 { 03103 if (startsWith) 03104 return m_text.startsWith(searchStr, Qt::CaseInsensitive); 03105 else 03106 return m_text.contains(searchStr, Qt::CaseInsensitive); 03107 } 03108 else if (fieldList == "**ALL**") 03109 { 03110 if (startsWith) 03111 { 03112 if (m_text.startsWith(searchStr, Qt::CaseInsensitive)) 03113 return true; 03114 } 03115 else 03116 { 03117 if (m_text.contains(searchStr, Qt::CaseInsensitive)) 03118 return true; 03119 } 03120 03121 QMap<QString, TextProperties>::const_iterator i = m_strings.constBegin(); 03122 03123 while (i != m_strings.constEnd()) 03124 { 03125 if (startsWith) 03126 { 03127 if (i.value().text.startsWith(searchStr, Qt::CaseInsensitive)) 03128 return true; 03129 } 03130 else 03131 { 03132 if (i.value().text.contains(searchStr, Qt::CaseInsensitive)) 03133 return true; 03134 } 03135 03136 ++i; 03137 } 03138 } 03139 else 03140 { 03141 QStringList fields = fieldList.split(',', QString::SkipEmptyParts); 03142 03143 for (int x = 0; x < fields.count(); x++) 03144 { 03145 if (m_strings.contains(fields.at(x).trimmed())) 03146 { 03147 if (startsWith) 03148 { 03149 if (m_strings[fields.at(x)].text.startsWith(searchStr, Qt::CaseInsensitive)) 03150 return true; 03151 } 03152 else 03153 { 03154 if (m_strings[fields.at(x)].text.contains(searchStr, Qt::CaseInsensitive)) 03155 return true; 03156 } 03157 } 03158 } 03159 } 03160 03161 return false; 03162 } 03163 03164 void MythUIButtonListItem::SetFontState(const QString &state, 03165 const QString &name) 03166 { 03167 if (!name.isEmpty()) 03168 { 03169 if (m_strings.contains(name)) 03170 m_strings[name].state = state; 03171 } 03172 else 03173 m_fontState = state; 03174 03175 if (m_parent) 03176 m_parent->Update(); 03177 } 03178 03179 void MythUIButtonListItem::setImage(MythImage *image, const QString &name) 03180 { 03181 if (!name.isEmpty()) 03182 { 03183 if (m_images.contains(name)) 03184 { 03185 if (m_images.value(name)) 03186 m_images.value(name)->DownRef(); 03187 } 03188 03189 if (image) 03190 { 03191 m_images.insert(name, image); 03192 image->UpRef(); 03193 } 03194 else 03195 m_images.remove(name); 03196 } 03197 else 03198 { 03199 if (image == m_image) 03200 return; 03201 03202 if (m_image) 03203 m_image->DownRef(); 03204 03205 m_image = image; 03206 03207 if (image) 03208 image->UpRef(); 03209 } 03210 03211 if (m_parent) 03212 m_parent->Update(); 03213 } 03214 03215 void MythUIButtonListItem::SetImageFromMap(const InfoMap &imageMap) 03216 { 03217 m_imageFilenames.clear(); 03218 m_imageFilenames = imageMap; 03219 } 03220 03221 MythImage *MythUIButtonListItem::getImage(const QString &name) 03222 { 03223 if (!name.isEmpty()) 03224 { 03225 if (m_images.contains(name)) 03226 return m_images.value(name); 03227 } 03228 else 03229 { 03230 return m_image; 03231 } 03232 03233 return NULL; 03234 } 03235 03236 void MythUIButtonListItem::SetImage( 03237 const QString &filename, const QString &name, bool force_reload) 03238 { 03239 bool do_update = force_reload; 03240 03241 if (!name.isEmpty()) 03242 { 03243 InfoMap::iterator it = m_imageFilenames.find(name); 03244 03245 if (it == m_imageFilenames.end()) 03246 { 03247 m_imageFilenames.insert(name, filename); 03248 do_update = true; 03249 } 03250 else if (*it != filename) 03251 { 03252 *it = filename; 03253 do_update = true; 03254 } 03255 } 03256 else if (m_imageFilename != filename) 03257 { 03258 m_imageFilename = filename; 03259 do_update = true; 03260 } 03261 03262 if (m_parent && do_update) 03263 m_parent->Update(); 03264 } 03265 03266 QString MythUIButtonListItem::GetImage(const QString &name) const 03267 { 03268 if (name.isEmpty()) 03269 return m_imageFilename; 03270 03271 InfoMap::const_iterator it = m_imageFilenames.find(name); 03272 03273 if (it != m_imageFilenames.end()) 03274 return *it; 03275 03276 return QString(); 03277 } 03278 03279 void MythUIButtonListItem::DisplayState(const QString &state, 03280 const QString &name) 03281 { 03282 if (name.isEmpty()) 03283 return; 03284 03285 bool do_update = false; 03286 InfoMap::iterator it = m_states.find(name); 03287 03288 if (it == m_states.end()) 03289 { 03290 m_states.insert(name, state); 03291 do_update = true; 03292 } 03293 else if (*it != state) 03294 { 03295 *it = state; 03296 do_update = true; 03297 } 03298 03299 if (m_parent && do_update) 03300 m_parent->Update(); 03301 } 03302 03303 void MythUIButtonListItem::SetStatesFromMap(const InfoMap &stateMap) 03304 { 03305 m_states.clear(); 03306 m_states = stateMap; 03307 } 03308 03309 bool MythUIButtonListItem::checkable() const 03310 { 03311 return m_checkable; 03312 } 03313 03314 MythUIButtonListItem::CheckState MythUIButtonListItem::state() const 03315 { 03316 return m_state; 03317 } 03318 03319 MythUIButtonList *MythUIButtonListItem::parent() const 03320 { 03321 return m_parent; 03322 } 03323 03324 void MythUIButtonListItem::setChecked(MythUIButtonListItem::CheckState state) 03325 { 03326 if (!m_checkable || m_state == state) 03327 return; 03328 03329 m_state = state; 03330 03331 if (m_parent) 03332 m_parent->Update(); 03333 } 03334 03335 void MythUIButtonListItem::setCheckable(bool flag) 03336 { 03337 m_checkable = flag; 03338 } 03339 03340 void MythUIButtonListItem::setDrawArrow(bool flag) 03341 { 03342 m_showArrow = flag; 03343 } 03344 03345 void MythUIButtonListItem::SetData(QVariant data) 03346 { 03347 m_data = data; 03348 } 03349 03350 QVariant MythUIButtonListItem::GetData() 03351 { 03352 return m_data; 03353 } 03354 03355 bool MythUIButtonListItem::MoveUpDown(bool flag) 03356 { 03357 if (m_parent) 03358 return m_parent->MoveItemUpDown(this, flag); 03359 else 03360 return false; 03361 } 03362 03363 void MythUIButtonListItem::SetToRealButton(MythUIStateType *button, bool selected) 03364 { 03365 if (!m_parent) 03366 return; 03367 03368 m_parent->ItemVisible(this); 03369 03370 QString state; 03371 03372 if (selected) 03373 { 03374 button->MoveToTop(); 03375 state = m_parent->m_active ? "selectedactive" : "selectedinactive"; 03376 } 03377 else 03378 state = m_parent->m_active ? "active" : "inactive"; 03379 03380 if (!button->DisplayState(state) && state == "inactive") 03381 { 03382 state = "active"; 03383 button->DisplayState(state); 03384 } 03385 03386 MythUIGroup *buttonstate = dynamic_cast<MythUIGroup *> 03387 (button->GetCurrentState()); 03388 03389 if (!buttonstate) 03390 { 03391 LOG(VB_GENERAL, LOG_ERR, QString("Failed to query buttonlist state: %1") 03392 .arg(state)); 03393 return; 03394 } 03395 03396 buttonstate->SetVisible(true); 03397 buttonstate->Reset(); 03398 03399 MythUIText *buttontext = dynamic_cast<MythUIText *> 03400 (buttonstate->GetChild("buttontext")); 03401 03402 if (buttontext) 03403 { 03404 buttontext->SetText(m_text); 03405 buttontext->SetFontState(m_fontState); 03406 } 03407 03408 MythUIImage *buttonimage = dynamic_cast<MythUIImage *> 03409 (buttonstate->GetChild("buttonimage")); 03410 03411 if (buttonimage) 03412 { 03413 if (!m_imageFilename.isEmpty()) 03414 { 03415 buttonimage->SetFilename(m_imageFilename); 03416 buttonimage->Load(); 03417 } 03418 else if (m_image) 03419 buttonimage->SetImage(m_image); 03420 } 03421 03422 MythUIImage *buttonarrow = dynamic_cast<MythUIImage *> 03423 (buttonstate->GetChild("buttonarrow")); 03424 03425 if (buttonarrow) 03426 buttonarrow->SetVisible(m_showArrow); 03427 03428 MythUIStateType *buttoncheck = dynamic_cast<MythUIStateType *> 03429 (buttonstate->GetChild("buttoncheck")); 03430 03431 if (buttoncheck) 03432 { 03433 buttoncheck->SetVisible(m_checkable); 03434 03435 if (m_checkable) 03436 { 03437 if (m_state == NotChecked) 03438 buttoncheck->DisplayState(MythUIStateType::Off); 03439 else if (m_state == HalfChecked) 03440 buttoncheck->DisplayState(MythUIStateType::Half); 03441 else 03442 buttoncheck->DisplayState(MythUIStateType::Full); 03443 } 03444 } 03445 03446 MythUIText *text; 03447 QMap<QString, TextProperties>::iterator string_it = m_strings.begin(); 03448 03449 while (string_it != m_strings.end()) 03450 { 03451 text = dynamic_cast<MythUIText *> 03452 (buttonstate->GetChild(string_it.key())); 03453 03454 if (text) 03455 { 03456 TextProperties textprop = string_it.value(); 03457 03458 QString newText = text->GetTemplateText(); 03459 03460 QRegExp regexp("%(([^\\|%]+)?\\||\\|(.))?(\\w+)(\\|(.+))?%"); 03461 regexp.setMinimal(true); 03462 03463 if (!newText.isEmpty() && newText.contains(regexp)) 03464 { 03465 int pos = 0; 03466 QString tempString = newText; 03467 03468 while ((pos = regexp.indexIn(newText, pos)) != -1) 03469 { 03470 QString key = regexp.cap(4).toLower().trimmed(); 03471 QString replacement; 03472 QString value = m_strings.value(key).text; 03473 03474 if (!value.isEmpty()) 03475 { 03476 replacement = QString("%1%2%3%4") 03477 .arg(regexp.cap(2)) 03478 .arg(regexp.cap(3)) 03479 .arg(m_strings.value(key).text) 03480 .arg(regexp.cap(6)); 03481 } 03482 03483 tempString.replace(regexp.cap(0), replacement); 03484 pos += regexp.matchedLength(); 03485 } 03486 03487 newText = tempString; 03488 } 03489 else 03490 newText = textprop.text; 03491 03492 if (newText.isEmpty()) 03493 text->Reset(); 03494 else 03495 text->SetText(newText); 03496 03497 text->SetFontState(textprop.state.isEmpty() ? m_fontState : textprop.state); 03498 } 03499 03500 ++string_it; 03501 } 03502 03503 MythUIImage *image; 03504 InfoMap::iterator imagefile_it = m_imageFilenames.begin(); 03505 03506 while (imagefile_it != m_imageFilenames.end()) 03507 { 03508 image = dynamic_cast<MythUIImage *> 03509 (buttonstate->GetChild(imagefile_it.key())); 03510 03511 if (image) 03512 { 03513 if (!imagefile_it.value().isEmpty()) 03514 { 03515 image->SetFilename(imagefile_it.value()); 03516 image->Load(); 03517 } 03518 else 03519 image->Reset(); 03520 } 03521 03522 ++imagefile_it; 03523 } 03524 03525 QMap<QString, MythImage *>::iterator image_it = m_images.begin(); 03526 03527 while (image_it != m_images.end()) 03528 { 03529 image = dynamic_cast<MythUIImage *> 03530 (buttonstate->GetChild(image_it.key())); 03531 03532 if (image) 03533 { 03534 if (image_it.value()) 03535 image->SetImage(image_it.value()); 03536 else 03537 image->Reset(); 03538 } 03539 03540 ++image_it; 03541 } 03542 03543 MythUIStateType *statetype; 03544 InfoMap::iterator state_it = m_states.begin(); 03545 03546 while (state_it != m_states.end()) 03547 { 03548 statetype = dynamic_cast<MythUIStateType *> 03549 (buttonstate->GetChild(state_it.key())); 03550 03551 if (statetype) 03552 { 03553 if (!statetype->DisplayState(state_it.value())) 03554 statetype->Reset(); 03555 } 03556 03557 ++state_it; 03558 } 03559 } 03560 03561 //--------------------------------------------------------- 03562 // SearchButtonListDialog 03563 //--------------------------------------------------------- 03564 SearchButtonListDialog::SearchButtonListDialog(MythScreenStack *parent, const char *name, 03565 MythUIButtonList *parentList, QString searchText) 03566 : MythScreenType(parent, name, false), 03567 m_startsWith(false), m_parentList(parentList), 03568 m_searchText(searchText), m_searchEdit(NULL), 03569 m_prevButton(NULL), m_nextButton(NULL), 03570 m_searchState(NULL) 03571 { 03572 } 03573 03574 SearchButtonListDialog::~SearchButtonListDialog(void) 03575 { 03576 } 03577 03578 bool SearchButtonListDialog::Create(void) 03579 { 03580 if (!CopyWindowFromBase("MythSearchListDialog", this)) 03581 return false; 03582 03583 bool err = false; 03584 UIUtilE::Assign(this, m_searchEdit, "searchedit", &err); 03585 UIUtilE::Assign(this, m_prevButton, "prevbutton", &err); 03586 UIUtilE::Assign(this, m_nextButton, "nextbutton", &err); 03587 UIUtilW::Assign(this, m_searchState, "searchstate"); 03588 03589 if (err) 03590 { 03591 LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'MythSearchListDialog'"); 03592 return false; 03593 } 03594 03595 m_searchEdit->SetText(m_searchText); 03596 03597 connect(m_searchEdit, SIGNAL(valueChanged()), SLOT(searchChanged())); 03598 connect(m_prevButton, SIGNAL(Clicked()), SLOT(prevClicked())); 03599 connect(m_nextButton, SIGNAL(Clicked()), SLOT(nextClicked())); 03600 03601 BuildFocusList(); 03602 03603 return true; 03604 } 03605 03606 bool SearchButtonListDialog::keyPressEvent(QKeyEvent *event) 03607 { 03608 if (GetFocusWidget() && GetFocusWidget()->keyPressEvent(event)) 03609 return true; 03610 03611 QStringList actions; 03612 bool handled = GetMythMainWindow()->TranslateKeyPress("Global", event, actions, false); 03613 03614 for (int i = 0; i < actions.size() && !handled; i++) 03615 { 03616 QString action = actions[i]; 03617 handled = true; 03618 03619 if (action == "0") 03620 { 03621 m_startsWith = !m_startsWith; 03622 searchChanged(); 03623 } 03624 else 03625 handled = false; 03626 } 03627 03628 if (!handled && MythScreenType::keyPressEvent(event)) 03629 handled = true; 03630 03631 return handled; 03632 } 03633 03634 void SearchButtonListDialog::searchChanged(void) 03635 { 03636 bool found = m_parentList->Find(m_searchEdit->GetText(), m_startsWith); 03637 03638 if (m_searchState) 03639 m_searchState->DisplayState(found ? "found" : "notfound"); 03640 } 03641 03642 void SearchButtonListDialog::nextClicked(void) 03643 { 03644 bool found = m_parentList->FindNext(); 03645 03646 if (m_searchState) 03647 m_searchState->DisplayState(found ? "found" : "notfound"); 03648 } 03649 03650 void SearchButtonListDialog::prevClicked(void) 03651 { 03652 bool found = m_parentList->FindPrev(); 03653 03654 if (m_searchState) 03655 m_searchState->DisplayState(found ? "found" : "notfound"); 03656 } 03657 03658 void MythUIButtonList::SetScrollBarPosition() 03659 { 03660 if (m_clearing || !m_scrollBar || !m_showScrollBar) 03661 return; 03662 03663 int maximum = m_itemCount <= static_cast<int>(m_itemsVisible) ? 03664 0 : m_itemCount; 03665 m_scrollBar->SetMaximum(maximum); 03666 m_scrollBar->SetPageStep(m_itemsVisible); 03667 m_scrollBar->SetSliderPosition(m_selPosition); 03668 m_scrollBar->MoveToTop(); 03669 }
1.7.6.1