MythTV  0.26-pre
mythuibuttonlist.cpp
Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends