MythTV  master
mythuibuttonlist.cpp
Go to the documentation of this file.
1 #include "mythuibuttonlist.h"
2 
3 #include <cmath>
4 #include <utility>
5 
6 // QT headers
7 #include <QCoreApplication>
8 #include <QDomDocument>
9 #include <QKeyEvent>
10 #include <QRegularExpression>
11 
12 // libmyth headers
13 #include "mythlogging.h"
14 
15 // mythui headers
16 #include "mythmainwindow.h"
17 #include "mythuiscrollbar.h"
18 #include "mythuistatetype.h"
19 #include "lcddevice.h"
20 #include "mythuibutton.h"
21 #include "mythuitext.h"
22 #include "mythuitextedit.h"
23 #include "mythuigroup.h"
24 #include "mythuiimage.h"
25 #include "mythgesture.h"
26 
27 #define LOC QString("MythUIButtonList(%1): ").arg(objectName())
28 
29 MythUIButtonList::MythUIButtonList(MythUIType *parent, const QString &name)
30  : MythUIType(parent, name)
31 {
32  // Parent members
35 
36  Const();
37 }
38 
39 MythUIButtonList::MythUIButtonList(MythUIType *parent, const QString &name,
40  const QRect area, bool showArrow,
41  bool showScrollBar)
42  : MythUIType(parent, name),
43  m_showArrow(showArrow), m_showScrollBar(showScrollBar)
44 {
45  // Parent members
46  m_area = area;
47  m_initiator = true;
48  m_enableInitiator = true;
49 
52 
53  Const();
54 }
55 
57 {
58 
59  SetCanTakeFocus(true);
60 
61  connect(this, &MythUIType::TakingFocus, this, &MythUIButtonList::Select);
63 }
64 
66 {
67  m_buttonToItem.clear();
68  m_clearing = true;
69 
70  while (!m_itemList.isEmpty())
71  delete m_itemList.takeFirst();
72 }
73 
75 {
77 
78  if (item)
79  emit itemSelected(item);
80 
81  SetActive(true);
82 }
83 
85 {
86  SetActive(false);
87 }
88 
90 {
91  if (m_initialized)
92  Update();
93 }
94 
96 {
97  m_drawFromBottom = draw;
98 }
99 
101 {
102  if (m_active == active)
103  return;
104 
105  m_active = active;
106 
107  if (m_initialized)
108  Update();
109 }
110 
115 {
116  m_buttonToItem.clear();
117 
118  if (m_itemList.isEmpty())
119  return;
120 
121  m_clearing = true;
122 
123  while (!m_itemList.isEmpty())
124  delete m_itemList.takeFirst();
125 
126  m_clearing = false;
127 
128  m_selPosition = 0;
129  m_topPosition = 0;
130  m_itemCount = 0;
131 
132  StopLoad();
133  Update();
135 
136  emit DependChanged(true);
137 }
138 
140 {
141  m_needsUpdate = true;
142  SetRedraw();
143 }
144 
145 /*
146  * The "width" of a button determines it relative position when using
147  * Dynamic-Layout.
148  *
149  * If a button has a negative offset, that offset needs accounted for
150  * to position the button in proper releation to the surrounding buttons.
151  */
153 {
154  int width = area.width();
155 
156  if (area.x() < 0)
157  {
158  /*
159  * Assume if an overlap is allowed on the left, the same overlap
160  * is on the right
161  */
162  width += (area.x() * 2 - 1); // x is negative
163 
164  while (width < 0)
165  width -= area.x(); // Oops
166  }
167  else if (m_layout == LayoutHorizontal)
168  width -= area.x(); // Get rid of any "space" betwen the buttons
169 
170  return width;
171 }
172 
173 /*
174  * The "height" of a button determines it relative position when using
175  * Dynamic-Layout.
176  *
177  * If a button has a negative offset, that offset needs accounted for
178  * to position the button in proper releation to the surrounding buttons.
179  */
181 {
182  int height = area.height();
183 
184  if (area.y() < 0)
185  {
186  /*
187  * Assume if an overlap is allowed on the top, the same overlap
188  * is on the bottom
189  */
190  height += (area.y() * 2 - 1);
191 
192  while (height < 0)
193  height -= area.y(); // Oops
194  }
195  else if (m_layout == LayoutVertical)
196  height -= area.y(); // Get rid of any "space" betwen the buttons
197 
198  return height;
199 }
200 
201 /*
202  * For Dynamic-Layout, buttons are allocated as needed. If the list is
203  * being redrawn, re-use any previously allocated buttons.
204  */
205 MythUIGroup *MythUIButtonList::PrepareButton(int buttonIdx, int itemIdx,
206  int &selectedIdx,
207  int &button_shift)
208 {
209  MythUIButtonListItem *buttonItem = m_itemList[itemIdx];
210 
211  buttonIdx += button_shift;
212 
213  if (buttonIdx < 0 || buttonIdx + 1 > m_maxVisible)
214  {
215  QString name = QString("buttonlist button %1").arg(m_maxVisible);
216  auto *button = new MythUIStateType(this, name);
217  button->CopyFrom(m_buttontemplate);
218  button->ConnectDependants(true);
219 
220  if (buttonIdx < 0)
221  {
222  /*
223  * If a new button is needed in the front of the list, previously
224  * existing buttons need shifted to the right.
225  */
226  m_buttonList.prepend(button);
227  buttonIdx = 0;
228  ++button_shift;
229 
230  if (selectedIdx >= 0)
231  ++selectedIdx;
232  }
233  else
234  m_buttonList.append(button);
235 
236  ++m_maxVisible;
237  }
238 
239  MythUIStateType *realButton = m_buttonList[buttonIdx];
240  m_buttonToItem[buttonIdx] = buttonItem;
241  buttonItem->SetToRealButton(realButton, itemIdx == m_selPosition);
242  auto *buttonstate =
243  dynamic_cast<MythUIGroup *>(realButton->GetCurrentState());
244 
245  if (itemIdx == m_selPosition)
246  selectedIdx = buttonIdx;
247 
248  return buttonstate;
249 }
250 
251 /*
252  * Dynamically layout the buttons on a row.
253  */
254 bool MythUIButtonList::DistributeRow(int &first_button, int &last_button,
255  int &first_item, int &last_item,
256  int &selected_column, int &skip_cols,
257  bool grow_left, bool grow_right,
258  int **col_widths, int &row_height,
259  int total_height, int split_height,
260  int &col_cnt, bool &wrapped)
261 {
262  MythUIGroup *buttonstate = nullptr;
263  int left_cnt = 0;
264  int left_width = 0;
265  int right_width = 0;
266  int begin = 0;
267  int end = 0;
268  bool underflow = false;
269 
270  int selectedIdx = -1;
271  int button_shift = 0;
272  col_cnt = 1;
273  skip_cols = 0;
274 
275  if (last_item + 1 > m_itemCount || last_item < 0 || first_item < 0)
276  return false;
277 
278  /*
279  * Allocate a button on the row. With a vertical layout, there is
280  * only one button per row, and this would be it.
281  */
282  if (grow_right)
283  {
284  buttonstate = PrepareButton(last_button, last_item,
285  selectedIdx, button_shift);
286  }
287  else
288  {
289  buttonstate = PrepareButton(first_button, first_item,
290  selectedIdx, button_shift);
291  }
292 
293  if (buttonstate == nullptr)
294  {
295  LOG(VB_GENERAL, LOG_ERR, QString("Failed to query buttonlist state: %1")
296  .arg(last_button));
297  return false;
298  }
299 
300  // Note size of initial button.
301  int max_width = m_contentsRect.width();
302  int max_height = m_contentsRect.height();
303  row_height = minButtonHeight(buttonstate->GetArea());
304  int width = minButtonWidth(buttonstate->GetArea());
305 
306  /*
307  * If the selected button should be centered, don't allow new buttons
308  * to take up more than half the allowed area.
309  */
310  bool vsplit = (m_scrollStyle == ScrollCenter);
311  bool hsplit = vsplit && grow_left && grow_right;
312 
313  if (hsplit)
314  {
315  max_width /= 2;
316  left_width = right_width = (width / 2);
317  }
318  else
319  {
320  if (grow_right)
321  {
322  left_width = 0;
323  right_width = width;
324  }
325  else
326  {
327  left_width = width;
328  right_width = 0;
329  }
330  }
331 
332  if (vsplit)
333  max_height /= 2;
334 
335  /*
336  * If total_height == 0, then this is the first row, so allow any height.
337  * Otherwise, If adding a button to a column would exceed the
338  * parent height, abort
339  */
340  if (total_height > 0 &&
341  ((vsplit ? split_height : total_height) +
342  m_itemVertSpacing + row_height > max_height))
343  {
344  LOG(VB_GUI, LOG_DEBUG,
345  QString("%1 Height exceeded %2 + (%3) + %4 = %5 which is > %6")
346  .arg(vsplit ? "Centering" : "Total")
347  .arg(split_height).arg(m_itemVertSpacing).arg(row_height)
348  .arg(split_height + m_itemVertSpacing + row_height)
349  .arg(max_height));
350  first_button += button_shift;
351  last_button += button_shift;
352  return false;
353  }
354 
355  LOG(VB_GUI, LOG_DEBUG, QString("Added button item %1 width %2 height %3")
356  .arg(grow_right ? last_item : first_item)
357  .arg(width).arg(row_height));
358 
359  int initial_first_button = first_button;
360  int initial_last_button = last_button;
361  int initial_first_item = first_item;
362  int initial_last_item = last_item;
363 
364  /*
365  * if col_widths is not nullptr, then grow_left & grow_right
366  * are mutually exclusive. So, col_idx can be anchored from
367  * the left or right.
368  */
369  int col_idx = 0;
370  if (!grow_right)
371  col_idx = m_columns - 1;
372 
373  // Add butons until no more fit.
374  bool added = (m_layout != LayoutVertical);
375 
376  while (added)
377  {
378  added = false;
379 
380  // If a grid, maintain same number of columns on each row.
381  if (grow_right && col_cnt < m_columns)
382  {
383  if (wrapped)
384  end = first_item;
385  else
386  {
387  // Are we allowed to wrap when we run out of items?
388  if (m_wrapStyle == WrapItems &&
389  (hsplit || m_scrollStyle != ScrollFree) &&
390  last_item + 1 == m_itemCount)
391  {
392  last_item = -1;
393  wrapped = true;
394  end = first_item;
395  }
396  else
397  end = m_itemCount;
398  }
399 
400  if (last_item + 1 < end)
401  {
402  // Allocate next button to the right.
403  buttonstate = PrepareButton(last_button + 1, last_item + 1,
404  selectedIdx, button_shift);
405 
406  if (buttonstate == nullptr)
407  continue;
408 
409  width = minButtonWidth(buttonstate->GetArea());
410 
411  // For grids, use the widest button in a column
412  if (*col_widths && width < (*col_widths)[col_idx])
413  width = (*col_widths)[col_idx];
414 
415  // Does the button fit?
416  if ((hsplit ? right_width : left_width + right_width) +
417  m_itemHorizSpacing + width > max_width)
418  {
419  int total = hsplit ? right_width : left_width + right_width;
420  LOG(VB_GUI, LOG_DEBUG,
421  QString("button on right would exceed width: "
422  "%1+(%2)+%3 == %4 which is > %5")
423  .arg(total).arg(m_itemHorizSpacing).arg(width)
424  .arg(total + m_itemHorizSpacing + width)
425  .arg(max_width));
426  }
427  else
428  {
429  added = true;
430  ++col_cnt;
431  ++last_button;
432  ++last_item;
433  ++col_idx;
434  right_width += m_itemHorizSpacing + width;
435  int height = minButtonHeight(buttonstate->GetArea());
436 
437  if (row_height < height)
438  row_height = height;
439 
440  LOG(VB_GUI, LOG_DEBUG,
441  QString("Added button item %1 "
442  "R.width %2 height %3 total width %4+%5"
443  " (max %6)")
444  .arg(last_item).arg(width).arg(height)
445  .arg(left_width).arg(right_width).arg(max_width));
446  }
447  }
448  else
449  underflow = true;
450  }
451 
452  // If a grid, maintain same number of columns on each row.
453  if (grow_left && col_cnt < m_columns)
454  {
455  if (wrapped)
456  end = last_item + 1;
457  else
458  {
459  // Are we allowed to wrap when we run out of items?
460  if (m_wrapStyle == WrapItems &&
461  (hsplit || m_scrollStyle != ScrollFree) &&
462  first_item == 0)
463  {
464  first_item = m_itemCount;
465  wrapped = true;
466  end = last_item + 1;
467  }
468  else
469  end = 0;
470  }
471 
472  if (first_item > end)
473  {
474  buttonstate = PrepareButton(first_button - 1, first_item - 1,
475  selectedIdx, button_shift);
476 
477  if (buttonstate == nullptr)
478  continue;
479 
480  width = minButtonWidth(buttonstate->GetArea());
481 
482  // For grids, use the widest button in a column
483  if (*col_widths && width < (*col_widths)[col_idx])
484  width = (*col_widths)[col_idx];
485 
486  // Does the button fit?
487  if ((hsplit ? left_width : left_width + right_width) +
488  m_itemHorizSpacing + width > max_width)
489  {
490  int total = hsplit ? left_width : left_width + right_width;
491  LOG(VB_GUI, LOG_DEBUG,
492  QString("button on left would exceed width: "
493  "%1+(%2)+%3 == %4 which is > %5")
494  .arg(total).arg(m_itemHorizSpacing).arg(width)
495  .arg(total + m_itemHorizSpacing + width)
496  .arg(max_width));
497  }
498  else
499  {
500  added = true;
501  --first_button;
502  --first_item;
503  --col_idx;
504  ++col_cnt;
505  ++left_cnt;
506  left_width += m_itemHorizSpacing + width;
507  int height = minButtonHeight(buttonstate->GetArea());
508 
509  if (row_height < height)
510  row_height = height;
511 
512  LOG(VB_GUI, LOG_DEBUG,
513  QString("Added button item %1 "
514  "L.width %2 height %3 total width %4+%5"
515  " (max %6)")
516  .arg(first_item).arg(width).arg(height)
517  .arg(left_width).arg(right_width).arg(max_width));
518  }
519  }
520  else
521  {
522  underflow = true;
523  if (m_layout == LayoutGrid)
524  skip_cols = m_columns - col_cnt;
525  }
526  }
527  }
528 
529  /*
530  * If total_height == 0, then this is the first row, so allow any height.
531  * Otherwise, If adding a button to a column would exceed the
532  * parent height, abort
533  */
534  if (total_height > 0 &&
535  ((vsplit ? split_height : total_height) +
536  m_itemVertSpacing + row_height > max_height))
537  {
538  LOG(VB_GUI, LOG_DEBUG,
539  QString("%1 Height exceeded %2 + (%3) + %4 = %5 which is > %6")
540  .arg(vsplit ? "Centering" : "Total")
541  .arg(split_height).arg(m_itemVertSpacing).arg(row_height)
542  .arg(split_height + m_itemVertSpacing + row_height)
543  .arg(max_height));
544  first_button = initial_first_button + button_shift;
545  last_button = initial_last_button + button_shift;
546  first_item = initial_first_item;
547  last_item = initial_last_item;
548  return false;
549  }
550 
551  if (*col_widths == nullptr)
552  {
553  /*
554  * Allocate array to hold columns widths, now that we know
555  * how many columns there are.
556  */
557  *col_widths = new int[col_cnt];
558 
559  for (col_idx = 0; col_idx < col_cnt; ++col_idx)
560  (*col_widths)[col_idx] = 0;
561 
562  }
563 
564  // Adjust for insertions on the front.
565  first_button += button_shift;
566  last_button += button_shift;
567 
568  // It fits, so so note max column widths
569  MythUIStateType *realButton = nullptr;
570  int buttonIdx = 0;
571 
572  if (grow_left)
573  {
574  begin = first_button;
575  end = first_button + col_cnt;
576  }
577  else
578  {
579  end = last_button + 1;
580  begin = end - col_cnt;
581  }
582 
583  for (buttonIdx = begin, col_idx = 0;
584  buttonIdx < end; ++buttonIdx, ++col_idx)
585  {
586  realButton = m_buttonList[buttonIdx];
587  buttonstate = dynamic_cast<MythUIGroup *>
588  (realButton->GetCurrentState());
589  if (!buttonstate)
590  break;
591  width = minButtonWidth(buttonstate->GetArea());
592 
593  if ((*col_widths)[col_idx] < width)
594  (*col_widths)[col_idx] = width;
595 
596  // Make note of which column has the selected button
597  if (selectedIdx == buttonIdx)
598  selected_column = col_idx;
599  }
600 
601  /*
602  An underflow indicates we ran out of items, not that the
603  buttons did not fit on the row.
604  */
605  if (total_height && underflow && col_cnt < m_columns)
606  col_cnt = m_columns;
607 
608  return true;
609 }
610 
611 /*
612  * Dynamically layout columns
613  */
614 bool MythUIButtonList::DistributeCols(int &first_button, int &last_button,
615  int &first_item, int &last_item,
616  int &selected_column, int &selected_row,
617  int &skip_cols, int **col_widths,
618  QList<int> & row_heights,
619  int &top_height, int &bottom_height,
620  bool &wrapped)
621 {
622  int col_cnt = 0;
623  int height = 0;
624  int row_cnt = 1;
625  int end = 0;
626  bool added = false;
627 
628  do
629  {
630  added = false;
631 
632  if (wrapped)
633  end = first_item;
634  else
635  {
636  // Are we allowed to wrap when we run out of items?
637  if (m_wrapStyle == WrapItems &&
640  last_item + 1 == m_itemCount)
641  {
642  last_item = -1;
643  wrapped = true;
644  end = first_item;
645  }
646  else
647  end = m_itemCount;
648  }
649 
650  if (last_item + 1 < end)
651  {
652  // Does another row fit?
653  if (DistributeRow(first_button, ++last_button,
654  first_item, ++last_item, selected_column,
655  skip_cols, false, true, col_widths, height,
656  top_height + bottom_height, bottom_height,
657  col_cnt, wrapped))
658  {
659  if (col_cnt < m_columns)
660  return false; // Need to try again with fewer cols
661 
662  if (selected_row == -1 && selected_column != -1)
663  selected_row = row_heights.size();
664 
665  ++row_cnt;
666  row_heights.push_back(height);
667  bottom_height += (height + m_itemVertSpacing);
668  added = true;
669  }
670  else
671  {
672  --last_button;
673  --last_item;
674  }
675  }
676 
677  if (wrapped)
678  end = last_item + 1;
679  else
680  {
681  // Are we allowed to wrap when we run out of items?
682  if (m_wrapStyle == WrapItems &&
685  first_item == 0)
686  {
687  first_item = m_itemCount;
688  wrapped = true;
689  end = last_item + 1;
690  }
691  else
692  end = 0;
693  }
694 
695  if (first_item > end)
696  {
697  // Can we insert another row?
698  if (DistributeRow(--first_button, last_button,
699  --first_item, last_item, selected_column,
700  skip_cols, true, false, col_widths, height,
701  top_height + bottom_height, top_height,
702  col_cnt, wrapped))
703  {
704  if (col_cnt < m_columns)
705  return false; // Need to try again with fewer cols
706 
707  if (selected_row == -1 && selected_column != -1)
708  selected_row = row_heights.size();
709  else if (selected_row != -1)
710  ++selected_row;
711 
712  ++row_cnt;
713  row_heights.push_front(height);
714  top_height += (height + m_itemVertSpacing);
715  added = true;
716  }
717  else
718  {
719  ++first_button;
720  ++first_item;
721  }
722  }
723  }
724  while (added);
725 
726  return true;
727 }
728 
729 /*
730  * Dynamically layout as many buttons as will fit in the area.
731  */
733 {
734  int first_button = 0;
735  int last_button = 0;
736  int start_button = 0;
737  int start_item = m_selPosition;
738  int first_item = 0;
739  int last_item = 0;
740  int skip_cols = 0;
741  int *col_widths = nullptr;
742  int col_cnt = 0;
743  int selected_column = -1;
744  int selected_row = -1;
745  bool wrapped = false;
746  bool grow_left = true;
747  int height = 0;
748  int top_height = 0;
749  int bottom_height = 0;
750 
751  QList<int> row_heights;
752 
753  LOG(VB_GUI, LOG_DEBUG, QString("DistributeButtons: "
754  "selected item %1 total items %2")
755  .arg(start_item).arg(m_itemCount));
756 
757  // if there are no items to show make sure all the buttons are made invisible
758  if (m_itemCount == 0)
759  {
760  for (int i = 0; i < m_buttonList.count(); ++i)
761  {
762  if (m_buttonList[i])
763  m_buttonList[i]->SetVisible(false);
764  }
765 
766  return false;
767  }
768 
769  /*
770  * Try fewer and fewer columns until each row can fit the same
771  * number of columns.
772  */
773  for (m_columns = m_itemCount; m_columns > 0;)
774  {
775  first_item = last_item = start_item;
776 
777  /*
778  * Drawing starts at start_button, and radiates from there.
779  * Attempt to pick a start_button which will minimize the need for new
780  * button allocations.
781  */
782  switch (m_scrollStyle)
783  {
784  case ScrollCenter:
785  case ScrollGroupCenter:
786  start_button = qMax((m_maxVisible / 2) - 1, 0);
787  break;
788  case ScrollFree:
789 
790  if (m_layout == LayoutGrid)
791  {
792  start_button = 0;
793  first_item = last_item = 0;
794  grow_left = false;
795  }
796  else if (!m_buttonList.empty())
797  {
798  if (m_itemCount - m_selPosition - 1 <
799  (m_buttonList.size() / 2))
800  {
801  start_button = m_buttonList.size() -
802  (m_itemCount - m_selPosition) + 1;
803  }
804  else if (m_selPosition >
805  (m_buttonList.size() / 2))
806  {
807  start_button = (m_buttonList.size() / 2);
808  }
809  else
810  {
811  start_button = m_selPosition;
812  }
813  }
814  else
815  start_button = 0;
816 
817  break;
818  }
819 
820  first_button = last_button = start_button;
821  row_heights.clear();
822 
823  // Process row with selected button, and set starting val for m_columns.
824  if (!DistributeRow(first_button, last_button,
825  first_item, last_item, selected_column,
826  skip_cols, grow_left, true, &col_widths,
827  height, 0, 0, col_cnt, wrapped))
828  {
829  delete[] col_widths;
830  return false;
831  }
832 
833  m_columns = col_cnt;
834 
836  {
837  /*
838  * Now that we know how many columns there are, we can start
839  * the grid layout for real.
840  */
841  start_item = (m_selPosition / m_columns) * m_columns;
842  first_item = last_item = start_item;
843 
844  /*
845  * Attempt to pick a start_button which will minimize the need
846  * for new button allocations.
847  */
848  start_button = qMax(m_buttonList.size() / 2, 0);
849  start_button = (start_button / qMax(m_columns, 1)) * m_columns;
850 
851  if (start_button < m_itemCount / 2 &&
852  m_itemCount - m_selPosition - 1 < m_buttonList.size() / 2)
853  start_button += m_columns;
854 
855  first_button = last_button = start_button;
856 
857  // Now do initial row layout again with our new knowledge
858  selected_column = selected_row = -1;
859 
860  if (!DistributeRow(first_button, last_button,
861  first_item, last_item, selected_column,
862  skip_cols, grow_left, true, &col_widths,
863  height, 0, 0, col_cnt, wrapped))
864  {
865  delete[] col_widths;
866  return false;
867  }
868  }
869 
870  if (selected_column != -1)
871  selected_row = 0;
872 
873  row_heights.push_back(height);
874 
876  top_height = bottom_height = (height / 2);
877  else
878  bottom_height = height;
879 
880  if (m_layout == LayoutHorizontal)
881  break;
882 
883  // As as many columns as will fit.
884  if (DistributeCols(first_button, last_button,
885  first_item, last_item,
886  selected_column, selected_row,
887  skip_cols, &col_widths, row_heights,
888  top_height, bottom_height, wrapped))
889  break; // Buttons fit on each row, so done
890 
891  delete[] col_widths;
892  col_widths = nullptr;
893 
894  --m_columns;
895  start_item = m_selPosition;
896  }
897 
898  m_rows = row_heights.size();
899 
900  LOG(VB_GUI, LOG_DEBUG,
901  QString("%1 rows, %2 columns fit inside parent area %3x%4")
903  .arg(m_contentsRect.height()));
904 
905  if (col_widths == nullptr)
906  return false;
907 
908  int total = 0;
909  int left_spacing = 0;
910  int right_spacing = 0;
911  int top_spacing = 0;
912  int bottom_spacing = 0;
913  MythRect min_rect;
914  QString status_msg;
915 
916  /*
917  * Calculate heights of buttons on each side of selected button
918  */
919  top_height = bottom_height = m_topRows = m_bottomRows = 0;
920 
921  status_msg = "Row heights: ";
922 
923  for (int row = 0; row < m_rows; ++row)
924  {
925  if (row != 0)
926  status_msg += ", ";
927 
928  if (row == selected_row)
929  {
930  status_msg += '[';
931  top_height += (row_heights[row] / 2);
932  bottom_height += ((row_heights[row] / 2) + (row_heights[row] % 2));
933  }
934  else
935  {
936  if (bottom_height)
937  {
938  bottom_height += m_itemVertSpacing + row_heights[row];
939  ++m_bottomRows;
940  }
941  else
942  {
943  top_height += row_heights[row] + m_itemVertSpacing;
944  ++m_topRows;
945  }
946  }
947 
948  status_msg += QString("%1").arg(row_heights[row]);
949 
950  if (row == selected_row)
951  status_msg += ']';
952  }
953 
954  /*
955  * How much extra space should there be between buttons?
956  */
958  {
959  // None
960  top_spacing = bottom_spacing = 0;
961  }
962  else
963  {
964  if (m_rows < 2)
965  {
966  // Equal space on both sides of single row
967  top_spacing = bottom_spacing =
968  (m_contentsRect.height() - top_height) / 2;
969  }
970  else
971  {
973  {
974  // Selected button needs to end up in the middle of area
975  top_spacing = m_topRows ? (m_contentsRect.height() / 2 -
976  top_height) / m_topRows : 0;
977  bottom_spacing = m_bottomRows ? (m_contentsRect.height() / 2 -
978  bottom_height) / m_bottomRows : 0;
979 
980  if (m_arrange == ArrangeSpread)
981  {
982  // Use same spacing on both sides of selected button
983  if (!m_topRows || top_spacing > bottom_spacing)
984  top_spacing = bottom_spacing;
985  else
986  bottom_spacing = top_spacing;
987  }
988  }
989  else
990  {
991  // Buttons will be evenly spread out to fill entire area
992  top_spacing = bottom_spacing = (m_contentsRect.height() -
993  (top_height + bottom_height)) /
995  }
996  }
997 
998  // Add in intra-button space size
999  top_height += (top_spacing * m_topRows);
1000  bottom_height += (bottom_spacing * m_bottomRows);
1001  }
1002 
1003  /*
1004  * Calculate top margin
1005  */
1006  int y = m_contentsRect.y();
1007 
1008  if ((m_alignment & Qt::AlignVCenter) && m_arrange != ArrangeFill)
1009  {
1010  if (m_scrollStyle == ScrollCenter)
1011  {
1012  // Adjust to compensate for top height less than bottom height
1013  y += qMax(bottom_height - top_height, 0);
1014  total = qMax(top_height, bottom_height) * 2;
1015  }
1016  else
1017  total = top_height + bottom_height;
1018 
1019  // Adjust top margin so selected button ends up in the middle
1020  y += (qMax(m_contentsRect.height() - total, 2) / 2);
1021  }
1022  else if ((m_alignment & Qt::AlignBottom) && m_arrange == ArrangeStack)
1023  {
1024  // Adjust top margin so buttons are bottom justified
1025  y += qMax(m_contentsRect.height() -
1026  (top_height + bottom_height), 0);
1027  }
1028  min_rect.setY(y);
1029 
1030  status_msg += QString(" spacing top %1 bottom %2 fixed %3 offset %4")
1031  .arg(top_spacing).arg(bottom_spacing)
1032  .arg(m_itemVertSpacing).arg(y);
1033 
1034  LOG(VB_GUI, LOG_DEBUG, status_msg);
1035 
1036  /*
1037  * Calculate width of buttons on each side of selected button
1038  */
1039  int left_width = 0;
1040  int right_width = 0;
1042 
1043  status_msg = "Col widths: ";
1044 
1045  for (int col = 0; col < m_columns; ++col)
1046  {
1047  if (col != 0)
1048  status_msg += ", ";
1049 
1050  if (col == selected_column)
1051  {
1052  status_msg += '[';
1053  left_width += (col_widths[col] / 2);
1054  right_width += ((col_widths[col] / 2) + (col_widths[col] % 2));
1055  }
1056  else
1057  {
1058  if (right_width)
1059  {
1060  right_width += m_itemHorizSpacing + col_widths[col];
1061  ++m_rightColumns;
1062  }
1063  else
1064  {
1065  left_width += col_widths[col] + m_itemHorizSpacing;
1066  ++m_leftColumns;
1067  }
1068  }
1069 
1070  status_msg += QString("%1").arg(col_widths[col]);
1071 
1072  if (col == selected_column)
1073  status_msg += ']';
1074  }
1075 
1076  /*
1077  * How much extra space should there be between buttons?
1078  */
1080  {
1081  // None
1082  left_spacing = right_spacing = 0;
1083  }
1084  else
1085  {
1086  if (m_columns < 2)
1087  {
1088  // Equal space on both sides of single column
1089  left_spacing = right_spacing =
1090  (m_contentsRect.width() - left_width) / 2;
1091  }
1092  else
1093  {
1094  if (m_scrollStyle == ScrollCenter)
1095  {
1096  // Selected button needs to end up in the middle
1097  left_spacing = m_leftColumns ? (m_contentsRect.width() / 2 -
1098  left_width) / m_leftColumns : 0;
1099  right_spacing = m_rightColumns ? (m_contentsRect.width() / 2 -
1100  right_width) / m_rightColumns : 0;
1101 
1102  if (m_arrange == ArrangeSpread)
1103  {
1104  // Use same spacing on both sides of selected button
1105  if (!m_leftColumns || left_spacing > right_spacing)
1106  left_spacing = right_spacing;
1107  else
1108  right_spacing = left_spacing;
1109  }
1110  }
1111  else
1112  {
1113  // Buttons will be evenly spread out to fill entire area
1114  left_spacing = right_spacing = (m_contentsRect.width() -
1115  (left_width + right_width)) /
1117  }
1118  }
1119 
1120  // Add in intra-button space size
1121  left_width += (left_spacing * m_leftColumns);
1122  right_width += (right_spacing * m_rightColumns);
1123  }
1124 
1125  /*
1126  * Calculate left margin
1127  */
1128  int x_init = m_contentsRect.x();
1129 
1130  if ((m_alignment & Qt::AlignHCenter) && m_arrange != ArrangeFill)
1131  {
1132  if (m_scrollStyle == ScrollCenter)
1133  {
1134  // Compensate for left being smaller than right
1135  x_init += qMax(right_width - left_width, 0);
1136  total = qMax(left_width, right_width) * 2;
1137  }
1138  else
1139  total = left_width + right_width;
1140 
1141  // Adjust left margin so selected button ends up in the middle
1142  x_init += (qMax(m_contentsRect.width() - total, 2) / 2);
1143  }
1144  else if ((m_alignment & Qt::AlignRight) && m_arrange == ArrangeStack)
1145  {
1146  // Adjust left margin, so buttons are right justified
1147  x_init += qMax(m_contentsRect.width() -
1148  (left_width + right_width), 0);
1149  }
1150  min_rect.setX(x_init);
1151 
1152  status_msg += QString(" spacing left %1 right %2 fixed %3 offset %4")
1153  .arg(left_spacing).arg(right_spacing)
1154  .arg(m_itemHorizSpacing).arg(x_init);
1155  LOG(VB_GUI, LOG_DEBUG, status_msg);
1156 
1157  top_spacing += m_itemVertSpacing;
1158  bottom_spacing += m_itemVertSpacing;
1159  left_spacing += m_itemHorizSpacing;
1160  right_spacing += m_itemHorizSpacing;
1161 
1162  // Calculate position of each button
1163  int buttonIdx = first_button - skip_cols;
1164  int x = 0;
1165  int x_adj = 0;
1166  int y_adj = 0;
1167 
1168  int vertical_spacing = top_spacing;
1169 
1170  for (int row = 0; row < m_rows; ++row)
1171  {
1172  x = x_init;
1173  int horizontal_spacing = left_spacing;
1174 
1175  for (int col = 0; col < m_columns && buttonIdx <= last_button; ++col)
1176  {
1177  if (buttonIdx >= first_button)
1178  {
1179  MythUIStateType *realButton = m_buttonList[buttonIdx];
1180  auto *buttonstate = dynamic_cast<MythUIGroup *>
1181  (realButton->GetCurrentState());
1182  if (!buttonstate)
1183  break; // Not continue
1184 
1185  MythRect area = buttonstate->GetArea();
1186 
1187  // Center button within width of column
1188  if (m_alignment & Qt::AlignHCenter)
1189  x_adj = (col_widths[col] - minButtonWidth(area)) / 2;
1190  else if (m_alignment & Qt::AlignRight)
1191  x_adj = (col_widths[col] - minButtonWidth(area));
1192  else
1193  x_adj = 0;
1194  if (m_layout == LayoutHorizontal)
1195  x_adj -= area.x(); // Negate button's own offset
1196 
1197  // Center button within height of row.
1198  if (m_alignment & Qt::AlignVCenter)
1199  y_adj = (row_heights[row] - minButtonHeight(area)) / 2;
1200  else if (m_alignment & Qt::AlignBottom)
1201  y_adj = (row_heights[row] - minButtonHeight(area));
1202  else
1203  y_adj = 0;
1204  if (m_layout == LayoutVertical)
1205  y_adj -= area.y(); // Negate button's own offset
1206 
1207  // Set position of button
1208  realButton->SetPosition(x + x_adj, y + y_adj);
1209  realButton->SetVisible(true);
1210 
1211  if (col == selected_column)
1212  {
1213  horizontal_spacing = right_spacing;
1214  if (row == selected_row)
1215  realButton->MoveToTop();
1216  }
1217  }
1218  x += col_widths[col] + horizontal_spacing;
1219  ++buttonIdx;
1220  }
1221 
1222  if (row == selected_row)
1223  vertical_spacing = bottom_spacing;
1224 
1225  y += row_heights[row] + vertical_spacing;
1226  }
1227  min_rect.setWidth(x - min_rect.x());
1228  min_rect.setHeight(y - min_rect.y());
1229 
1231 
1232  // Hide buttons before first active button
1233  for (buttonIdx = 0; buttonIdx < first_button; ++buttonIdx)
1234  m_buttonList[buttonIdx]->SetVisible(false);
1235 
1236  // Hide buttons after last active buttons.
1237  for (buttonIdx = m_maxVisible - 1; buttonIdx > last_button; --buttonIdx)
1238  m_buttonList[buttonIdx]->SetVisible(false);
1239 
1240  // Set m_topPosition so arrows are displayed correctly.
1242  m_topPosition = static_cast<int>(m_itemsVisible < m_itemCount);
1243  else
1244  m_topPosition = first_item;
1245 
1247  if (m_minSize.isValid())
1248  {
1249  // Record the minimal area needed for the button list
1250  SetMinArea(min_rect);
1251  }
1252 
1253  delete[] col_widths;
1254  return true;
1255 }
1256 
1258 {
1259  if (m_buttonList.empty())
1260  return;
1261 
1262  int button = 0;
1263 
1264  switch (m_scrollStyle)
1265  {
1266  case ScrollCenter:
1267  case ScrollGroupCenter:
1268  m_topPosition = qMax(m_selPosition -
1269  (int)((float)m_itemsVisible / 2), 0);
1270  break;
1271  case ScrollFree:
1272  {
1273  int adjust = 0;
1274 
1275  if (m_topPosition == -1 || m_keepSelAtBottom)
1276  {
1277  if (m_topPosition == -1)
1278  m_topPosition = 0;
1279 
1280  if (m_layout == LayoutHorizontal)
1281  adjust = 1 - m_itemsVisible;
1282  else
1283  adjust = m_columns - m_itemsVisible;
1284 
1285  m_keepSelAtBottom = false;
1286  }
1287 
1288  if (m_selPosition < m_topPosition ||
1290  {
1291  if (m_layout == LayoutHorizontal)
1292  m_topPosition = m_selPosition + adjust;
1293  else
1294  m_topPosition = (m_selPosition + adjust) /
1295  m_columns * m_columns;
1296  }
1297 
1298  // Adjusted if last item is deleted
1299  if (((m_itemList.count() - m_topPosition) < m_itemsVisible) &&
1301  m_columns == 1)
1303 
1304  m_topPosition = qMax(m_topPosition, 0);
1305  break;
1306  }
1307  }
1308 
1309  QList<MythUIButtonListItem *>::iterator it = m_itemList.begin() +
1310  m_topPosition;
1311 
1313  {
1314  if (m_selPosition <= m_itemsVisible / 2)
1315  {
1316  button = (m_itemsVisible / 2) - m_selPosition;
1317 
1318  if (m_wrapStyle == WrapItems && button > 0 &&
1320  {
1321  it = m_itemList.end() - button;
1322  button = 0;
1323  }
1324  }
1325  else if ((m_itemCount - m_selPosition) < (m_itemsVisible / 2))
1326  {
1327  it = m_itemList.begin() + m_selPosition - (m_itemsVisible / 2);
1328  }
1329  }
1331  button = m_itemsVisible - m_itemCount;
1332 
1333  for (int i = 0; i < button; ++i)
1334  m_buttonList[i]->SetVisible(false);
1335 
1336  bool seenSelected = false;
1337 
1338  MythUIStateType *realButton = nullptr;
1339  MythUIButtonListItem *buttonItem = nullptr;
1340 
1341  if (it < m_itemList.begin())
1342  it = m_itemList.begin();
1343 
1344  int curItem = it < m_itemList.end() ? GetItemPos(*it) : 0;
1345 
1346  while (it < m_itemList.end() && button < m_itemsVisible)
1347  {
1348  realButton = m_buttonList[button];
1349  buttonItem = *it;
1350 
1351  if (!realButton || !buttonItem)
1352  break;
1353 
1354  bool selected = false;
1355 
1356  if (!seenSelected && (curItem == m_selPosition))
1357  {
1358  seenSelected = true;
1359  selected = true;
1360  }
1361 
1362  m_buttonToItem[button] = buttonItem;
1363  buttonItem->SetToRealButton(realButton, selected);
1364  realButton->SetVisible(true);
1365 
1366  if (m_wrapStyle == WrapItems && it == (m_itemList.end() - 1) &&
1368  {
1369  it = m_itemList.begin();
1370  curItem = 0;
1371  }
1372  else
1373  {
1374  ++it;
1375  ++curItem;
1376  }
1377 
1378  ++button;
1379  }
1380 
1381  for (; button < m_itemsVisible; ++button)
1382  m_buttonList[button]->SetVisible(false);
1383 }
1384 
1386 {
1387  if (m_selPosition < 0)
1388  m_selPosition = (m_wrapStyle > WrapNone) ? m_itemList.size() - 1 : 0;
1389  else if (m_selPosition >= m_itemList.size())
1390  m_selPosition = (m_wrapStyle > WrapNone) ? 0 : m_itemList.size() - 1;
1391 }
1392 
1394 {
1395  if (!m_initialized)
1396  Init();
1397 
1398  if (!m_initialized)
1399  return;
1400 
1401  if (m_clearing)
1402  return;
1403 
1404  m_needsUpdate = false;
1405 
1406  // mark the visible buttons as invisible
1407  QMap<int, MythUIButtonListItem*>::const_iterator i = m_buttonToItem.constBegin();
1408  while (i != m_buttonToItem.constEnd())
1409  {
1410  if (i.value())
1411  i.value()->setVisible(false);
1412  ++i;
1413  }
1414 
1415  // set topitem, top position
1416  SanitizePosition();
1417  m_buttonToItem.clear();
1418 
1419  if (m_arrange == ArrangeFixed)
1421  else
1423 
1424  updateLCD();
1425 
1426  m_needsUpdate = false;
1427 
1428  if (!m_downArrow || !m_upArrow)
1429  return;
1430 
1431  if (m_itemCount == 0)
1432  {
1435  }
1436  else
1437  {
1438  if (m_topPosition != 0)
1440  else
1442 
1445  else
1447 
1448  m_upArrow->MoveToTop();
1450  }
1451 }
1452 
1454 {
1455  if (item)
1456  emit itemVisible(item);
1457 }
1458 
1460 {
1461  bool wasEmpty = m_itemList.isEmpty();
1462 
1463  if (listPosition >= 0 && listPosition <= m_itemList.count())
1464  {
1465  m_itemList.insert(listPosition, item);
1466 
1467  if (listPosition <= m_selPosition)
1468  ++m_selPosition;
1469 
1470  if (listPosition <= m_topPosition)
1471  ++m_topPosition;
1472  }
1473  else
1474  m_itemList.append(item);
1475 
1476  ++m_itemCount;
1477 
1478  if (wasEmpty)
1479  {
1481  emit itemSelected(item);
1482  emit DependChanged(false);
1483  }
1484 
1485  Update();
1486 }
1487 
1489 {
1490  if (m_clearing)
1491  return;
1492 
1493  int curIndex = m_itemList.indexOf(item);
1494 
1495  if (curIndex == -1)
1496  return;
1497 
1498  QMap<int, MythUIButtonListItem*>::iterator it = m_buttonToItem.begin();
1499  while (it != m_buttonToItem.end())
1500  {
1501  if (it.value() == item)
1502  {
1503  m_buttonToItem.erase(it);
1504  break;
1505  }
1506  ++it;
1507  }
1508 
1509  if (curIndex < m_topPosition &&
1510  m_topPosition > 0)
1511  {
1512  // The removed item is before the visible part, move
1513  // everything up 1. The visible part shouldn't appear to
1514  // change.
1515  --m_topPosition;
1516  --m_selPosition;
1517  }
1518  else if (curIndex < m_selPosition ||
1519  (m_selPosition == m_itemCount - 1 &&
1520  m_selPosition > 0))
1521  {
1522  // The removed item is visible and before the selected item or
1523  // the selected item is the last item but not the only item,
1524  // move the selected item up 1.
1525  --m_selPosition;
1526  }
1527 
1528  m_itemList.removeAt(curIndex);
1529  --m_itemCount;
1530 
1531  Update();
1532 
1533  if (m_selPosition < m_itemCount)
1535  else
1536  emit itemSelected(nullptr);
1537 
1538  if (IsEmpty())
1539  emit DependChanged(true);
1540 }
1541 
1542 void MythUIButtonList::SetValueByData(const QVariant& data)
1543 {
1544  if (!m_initialized)
1545  Init();
1546 
1547  for (auto *item : qAsConst(m_itemList))
1548  {
1549  if (item->GetData() == data)
1550  {
1551  SetItemCurrent(item);
1552  return;
1553  }
1554  }
1555 }
1556 
1558 {
1559  int newIndex = m_itemList.indexOf(item);
1560  SetItemCurrent(newIndex);
1561 }
1562 
1563 void MythUIButtonList::SetItemCurrent(int current, int topPosition)
1564 {
1565  if (!m_initialized)
1566  Init();
1567 
1568  if (current == -1 || current >= m_itemList.size())
1569  return;
1570 
1571  if (!m_itemList.at(current)->isEnabled())
1572  return;
1573 
1574  if (current == m_selPosition &&
1575  (topPosition == -1 || topPosition == m_topPosition))
1576  return;
1577 
1578  m_topPosition = topPosition;
1579 
1580  if (topPosition > 0 && m_layout == LayoutGrid)
1581  m_topPosition -= (topPosition % m_columns);
1582 
1584 
1585  Update();
1586 
1587  emit itemSelected(GetItemCurrent());
1588 }
1589 
1591 {
1592  if (m_itemList.isEmpty() || m_selPosition >= m_itemList.size() ||
1593  m_selPosition < 0)
1594  return nullptr;
1595 
1596  return m_itemList.at(m_selPosition);
1597 }
1598 
1600 {
1602 
1603  if (item)
1604  return item->GetText().toInt();
1605 
1606  return 0;
1607 }
1608 
1610 {
1612 
1613  if (item)
1614  return item->GetText();
1615 
1616  return QString();
1617 }
1618 
1620 {
1622 
1623  if (item)
1624  return item->GetData();
1625 
1626  return QVariant();
1627 }
1628 
1630 {
1631  if (m_contentsRect.isValid())
1632  return m_contentsRect;
1633  return m_area;
1634 }
1635 
1637 {
1638  if (!m_itemList.empty())
1639  return m_itemList[0];
1640 
1641  return nullptr;
1642 }
1643 
1645 const
1646 {
1647  QListIterator<MythUIButtonListItem *> it(m_itemList);
1648 
1649  if (!it.findNext(item))
1650  return nullptr;
1651 
1652  return it.previous();
1653 }
1654 
1656 {
1657  return m_itemCount;
1658 }
1659 
1661 {
1662  if (m_needsUpdate)
1663  {
1666  }
1667 
1668  return m_itemsVisible;
1669 }
1670 
1672 {
1673  return m_itemCount <= 0;
1674 }
1675 
1677 {
1678  if (pos < 0 || pos >= m_itemList.size())
1679  return nullptr;
1680 
1681  return m_itemList.at(pos);
1682 }
1683 
1685 {
1686  if (!m_initialized)
1687  Init();
1688 
1689  for (auto *item : qAsConst(m_itemList))
1690  {
1691  if (item->GetData() == data)
1692  return item;
1693  }
1694 
1695  return nullptr;
1696 }
1697 
1699 {
1700  if (!item)
1701  return -1;
1702 
1703  return m_itemList.indexOf(item);
1704 }
1705 
1706 void MythUIButtonList::InitButton(int itemIdx, MythUIStateType* & realButton,
1707  MythUIButtonListItem* & buttonItem)
1708 {
1709  buttonItem = m_itemList[itemIdx];
1710 
1711  if (m_maxVisible == 0)
1712  {
1713  QString name("buttonlist button 0");
1714  auto *button = new MythUIStateType(this, name);
1715  button->CopyFrom(m_buttontemplate);
1716  button->ConnectDependants(true);
1717  m_buttonList.append(button);
1718  ++m_maxVisible;
1719  }
1720 
1721  realButton = m_buttonList[0];
1722  m_buttonToItem[0] = buttonItem;
1723 }
1724 
1725 /*
1726  * PageUp and PageDown are helpers when Dynamic layout is being used.
1727  *
1728  * When buttons are layed out dynamically, the number of buttons on the next
1729  * page, may not equal the number of buttons on the current page. Dynamic
1730  * layout is always center-weighted, so attempt to figure out which button
1731  * is near the middle on the next page.
1732  */
1733 
1735 {
1736  int pos = m_selPosition;
1737  int total = 0;
1738 
1739  /*
1740  * /On the new page/
1741  * If the number of buttons before the selected button does not equal
1742  * the number of buttons after the selected button, this logic can
1743  * undershoot the new selected button. That is better than overshooting
1744  * though.
1745  *
1746  * To fix this would require laying out the new page and then figuring
1747  * out which button should be selected, but this is already complex enough.
1748  */
1749 
1750  if (m_layout == LayoutHorizontal)
1751  {
1752  pos -= (m_leftColumns + 1);
1753 
1754  int max_width = m_contentsRect.width() / 2;
1755 
1756  for (; pos >= 0; --pos)
1757  {
1758  MythUIStateType *realButton = nullptr;
1759  MythUIButtonListItem *buttonItem = nullptr;
1760  InitButton(pos, realButton, buttonItem);
1761  buttonItem->SetToRealButton(realButton, true);
1762  auto *buttonstate = dynamic_cast<MythUIGroup *>
1763  (realButton->GetCurrentState());
1764 
1765  if (buttonstate == nullptr)
1766  {
1767  LOG(VB_GENERAL, LOG_ERR,
1768  "PageUp: Failed to query buttonlist state");
1769  return pos;
1770  }
1771 
1772  if (total + m_itemHorizSpacing +
1773  buttonstate->GetArea().width() / 2 >= max_width)
1774  return pos + 1;
1775 
1776  buttonItem->SetToRealButton(realButton, false);
1777  buttonstate = dynamic_cast<MythUIGroup *>
1778  (realButton->GetCurrentState());
1779  if (buttonstate)
1780  total += m_itemHorizSpacing + buttonstate->GetArea().width();
1781  }
1782 
1783  return 0;
1784  }
1785 
1786  // Grid or Vertical
1787  int dec = 1;
1788 
1789  if (m_layout == LayoutGrid)
1790  {
1791  /*
1792  * Adjusting using bottomRow:TopRow only works if new page
1793  * has the same ratio as the previous page, but that is common
1794  * with the grid layout, so go for it. If themers start doing
1795  * grids where this is not true, then this will need to be modified.
1796  */
1797  pos -= (m_columns * (m_topRows + 2 +
1798  qMax(m_bottomRows - m_topRows, 0)));
1799  dec = m_columns;
1800  }
1801  else
1802  {
1803  pos -= (m_topRows + 1);
1804  dec = 1;
1805  }
1806 
1807  int max_height = m_contentsRect.height() / 2;
1808 
1809  for (; pos >= 0; pos -= dec)
1810  {
1811  MythUIStateType *realButton = nullptr;
1812  MythUIButtonListItem *buttonItem = nullptr;
1813  InitButton(pos, realButton, buttonItem);
1814  buttonItem->SetToRealButton(realButton, true);
1815  auto *buttonstate = dynamic_cast<MythUIGroup *>
1816  (realButton->GetCurrentState());
1817 
1818  if (buttonstate == nullptr)
1819  {
1820  LOG(VB_GENERAL, LOG_ERR,
1821  "PageUp: Failed to query buttonlist state");
1822  return pos;
1823  }
1824 
1825  if (total + m_itemHorizSpacing +
1826  buttonstate->GetArea().height() / 2 >= max_height)
1827  return pos + dec;
1828 
1829  buttonItem->SetToRealButton(realButton, false);
1830  buttonstate = dynamic_cast<MythUIGroup *>
1831  (realButton->GetCurrentState());
1832  if (buttonstate)
1833  total += m_itemHorizSpacing + buttonstate->GetArea().height();
1834  }
1835 
1836  return 0;
1837 }
1838 
1840 {
1841  int pos = m_selPosition;
1842  int num_items = m_itemList.size();
1843  int total = 0;
1844 
1845  /*
1846  * /On the new page/
1847  * If the number of buttons before the selected button does not equal
1848  * the number of buttons after the selected button, this logic can
1849  * undershoot the new selected button. That is better than overshooting
1850  * though.
1851  *
1852  * To fix this would require laying out the new page and then figuring
1853  * out which button should be selected, but this is already complex enough.
1854  */
1855 
1856  if (m_layout == LayoutHorizontal)
1857  {
1858  pos += (m_rightColumns + 1);
1859 
1860  int max_width = m_contentsRect.width() / 2;
1861 
1862  for (; pos < num_items; ++pos)
1863  {
1864  MythUIStateType *realButton = nullptr;
1865  MythUIButtonListItem *buttonItem = nullptr;
1866  InitButton(pos, realButton, buttonItem);
1867  buttonItem->SetToRealButton(realButton, true);
1868  auto *buttonstate = dynamic_cast<MythUIGroup *>
1869  (realButton->GetCurrentState());
1870 
1871  if (buttonstate == nullptr)
1872  {
1873  LOG(VB_GENERAL, LOG_ERR,
1874  "PageDown: Failed to query buttonlist state");
1875  return pos;
1876  }
1877 
1878  if (total + m_itemHorizSpacing +
1879  buttonstate->GetArea().width() / 2 >= max_width)
1880  return pos - 1;
1881 
1882  buttonItem->SetToRealButton(realButton, false);
1883  buttonstate = dynamic_cast<MythUIGroup *>
1884  (realButton->GetCurrentState());
1885  if (buttonstate)
1886  total += m_itemHorizSpacing + buttonstate->GetArea().width();
1887  }
1888 
1889  return num_items - 1;
1890  }
1891 
1892  // Grid or Vertical
1893  int inc = 1;
1894 
1895  if (m_layout == LayoutGrid)
1896  {
1897  /*
1898  * Adjusting using bottomRow:TopRow only works if new page
1899  * has the same ratio as the previous page, but that is common
1900  * with the grid layout, so go for it. If themers start doing
1901  * grids where this is not true, then this will need to be modified.
1902  */
1903  pos += (m_columns * (m_bottomRows + 2 +
1904  qMax(m_topRows - m_bottomRows, 0)));
1905  inc = m_columns;
1906  }
1907  else
1908  {
1909  pos += (m_bottomRows + 1);
1910  inc = 1;
1911  }
1912 
1913  int max_height = m_contentsRect.height() / 2;
1914 
1915  for (; pos < num_items; pos += inc)
1916  {
1917  MythUIStateType *realButton = nullptr;
1918  MythUIButtonListItem *buttonItem = nullptr;
1919  InitButton(pos, realButton, buttonItem);
1920  buttonItem->SetToRealButton(realButton, true);
1921  auto *buttonstate = dynamic_cast<MythUIGroup *>
1922  (realButton->GetCurrentState());
1923 
1924  if (!buttonstate)
1925  {
1926  LOG(VB_GENERAL, LOG_ERR,
1927  "PageDown: Failed to query buttonlist state");
1928  return pos;
1929  }
1930 
1931  if (total + m_itemHorizSpacing +
1932  buttonstate->GetArea().height() / 2 >= max_height)
1933  return pos - inc;
1934 
1935  buttonItem->SetToRealButton(realButton, false);
1936  buttonstate = dynamic_cast<MythUIGroup *>
1937  (realButton->GetCurrentState());
1938  if (buttonstate)
1939  total += m_itemHorizSpacing + buttonstate->GetArea().height();
1940  }
1941 
1942  return num_items - 1;
1943 }
1944 
1946 {
1947  int pos = m_selPosition;
1948 
1949  if (pos == -1 || m_itemList.isEmpty() || !m_initialized)
1950  return false;
1951 
1952  switch (unit)
1953  {
1954  case MoveItem:
1955  if (m_selPosition > 0)
1956  --m_selPosition;
1957  else if (m_wrapStyle > WrapNone)
1958  m_selPosition = m_itemList.size() - 1;
1959  else if (m_wrapStyle == WrapCaptive)
1960  return true;
1961 
1962  FindEnabledUp(unit);
1963 
1964  break;
1965 
1966  case MoveColumn:
1967  if (pos % m_columns > 0)
1968  {
1969  --m_selPosition;
1970  }
1971  else if (m_wrapStyle == WrapFlowing)
1972  {
1973  if (m_selPosition == 0)
1974  --m_selPosition = m_itemList.size() - 1;
1975  else
1976  --m_selPosition;
1977  }
1978  else if (m_wrapStyle > WrapNone)
1979  {
1980  m_selPosition = pos + (m_columns - 1);
1981  }
1982  else if (m_wrapStyle == WrapCaptive)
1983  {
1984  return true;
1985  }
1986 
1987  FindEnabledUp(unit);
1988 
1989  break;
1990 
1991  case MoveRow:
1992  if (m_scrollStyle != ScrollFree)
1993  {
1995  if (m_selPosition < 0)
1996  m_selPosition += m_itemList.size();
1997  else
1998  m_selPosition %= m_itemList.size();
1999  }
2000  else if ((pos - m_columns) >= 0)
2002  else if (m_wrapStyle > WrapNone)
2003  {
2004  m_selPosition = ((m_itemList.size() - 1) / m_columns) *
2005  m_columns + pos;
2006 
2007  if ((m_selPosition / m_columns)
2008  < ((m_itemList.size() - 1) / m_columns))
2009  m_selPosition = m_itemList.size() - 1;
2010 
2011  if (m_layout == LayoutVertical)
2012  m_topPosition = qMax(0, m_selPosition - m_itemsVisible + 1);
2013  }
2014  else if (m_wrapStyle == WrapCaptive)
2015  return true;
2016 
2017  FindEnabledUp(unit);
2018 
2019  break;
2020 
2021  case MovePage:
2022  if (m_arrange == ArrangeFixed)
2024  else
2025  m_selPosition = PageUp();
2026 
2027  FindEnabledUp(unit);
2028 
2029  break;
2030 
2031  case MoveMid:
2032  m_selPosition = m_itemList.size() / 2;
2033  FindEnabledUp(unit);
2034  break;
2035 
2036  case MoveMax:
2037  m_selPosition = 0;
2038  FindEnabledUp(unit);
2039  break;
2040 
2041  case MoveByAmount:
2042  for (uint i = 0; i < amount; ++i)
2043  {
2044  if (m_selPosition > 0)
2045  --m_selPosition;
2046  else if (m_wrapStyle > WrapNone)
2047  m_selPosition = m_itemList.size() - 1;
2048  }
2049 
2050  FindEnabledUp(unit);
2051 
2052  break;
2053  }
2054 
2055  SanitizePosition();
2056 
2057  if (pos != m_selPosition)
2058  {
2059  Update();
2060  emit itemSelected(GetItemCurrent());
2061  }
2062  else
2063  return false;
2064 
2065  return true;
2066 }
2067 
2068 
2073 {
2074  if (m_selPosition < 0 || m_selPosition >= m_itemList.size() ||
2075  m_itemList.at(m_selPosition)->isEnabled())
2076  return;
2077 
2078  int step = (unit == MoveRow) ? m_columns : 1;
2079  if (unit == MoveRow && m_wrapStyle == WrapFlowing)
2080  unit = MoveItem;
2081  if (unit == MoveColumn)
2082  {
2083  while (m_selPosition < m_itemList.size() &&
2084  (m_selPosition + 1) % m_columns > 0 &&
2085  !m_itemList.at(m_selPosition)->isEnabled())
2086  ++m_selPosition;
2087 
2088  if (m_itemList.at(m_selPosition)->isEnabled())
2089  return;
2090 
2091  if (m_wrapStyle > WrapNone)
2092  {
2094  while ((m_selPosition + 1) % m_columns > 0 &&
2095  !m_itemList.at(m_selPosition)->isEnabled())
2096  ++m_selPosition;
2097  }
2098  }
2099  else
2100  {
2101  while (!m_itemList.at(m_selPosition)->isEnabled() &&
2102  (m_selPosition < m_itemList.size() - step))
2103  m_selPosition += step;
2104 
2105  if (!m_itemList.at(m_selPosition)->isEnabled() &&
2107  {
2108  m_selPosition = (m_selPosition + step) % m_itemList.size();
2109 
2110  while (!m_itemList.at(m_selPosition)->isEnabled() &&
2111  (m_selPosition < m_itemList.size() - step))
2112  m_selPosition += step;
2113  }
2114  }
2115 }
2116 
2118 {
2119  if (m_selPosition < 0 || m_selPosition >= m_itemList.size() ||
2120  m_itemList.at(m_selPosition)->isEnabled())
2121  return;
2122 
2123  int step = (unit == MoveRow) ? m_columns : 1;
2124  if (unit == MoveRow && m_wrapStyle == WrapFlowing)
2125  unit = MoveItem;
2126  if (unit == MoveColumn)
2127  {
2128  while (m_selPosition > 0 && (m_selPosition - 1) % m_columns > 0 &&
2129  !m_itemList.at(m_selPosition)->isEnabled())
2130  --m_selPosition;
2131 
2132  if (m_itemList.at(m_selPosition)->isEnabled())
2133  return;
2134 
2135  if (m_wrapStyle > WrapNone)
2136  {
2138  while ((m_selPosition - 1) % m_columns > 0 &&
2139  !m_itemList.at(m_selPosition)->isEnabled())
2140  --m_selPosition;
2141  }
2142  }
2143  else
2144  {
2145  while (!m_itemList.at(m_selPosition)->isEnabled() &&
2146  (m_selPosition - step >= 0))
2147  m_selPosition -= step;
2148 
2149  if (!m_itemList.at(m_selPosition)->isEnabled() &&
2151  {
2152  m_selPosition = m_itemList.size() - 1;
2153 
2154  while (m_selPosition > 0 &&
2155  !m_itemList.at(m_selPosition)->isEnabled() &&
2156  (m_selPosition - step >= 0))
2157  m_selPosition -= step;
2158  }
2159  }
2160 }
2161 
2162 
2164 {
2165  int pos = m_selPosition;
2166 
2167  if (pos == -1 || m_itemList.isEmpty() || !m_initialized)
2168  return false;
2169 
2170  switch (unit)
2171  {
2172  case MoveItem:
2173  if (m_selPosition < m_itemList.size() - 1)
2174  ++m_selPosition;
2175  else if (m_wrapStyle > WrapNone)
2176  m_selPosition = 0;
2177  else if (m_wrapStyle == WrapCaptive)
2178  return true;
2179 
2180  FindEnabledDown(unit);
2181 
2182  break;
2183 
2184  case MoveColumn:
2185  if ((pos + 1) % m_columns > 0)
2186  {
2187  ++m_selPosition;
2188  }
2189  else if (m_wrapStyle == WrapFlowing)
2190  {
2191  if (m_selPosition < m_itemList.size() - 1)
2192  ++m_selPosition;
2193  else
2194  m_selPosition = 0;
2195  }
2196  else if (m_wrapStyle > WrapNone)
2197  {
2198  m_selPosition = pos - (m_columns - 1);
2199  }
2200  else if (m_wrapStyle == WrapCaptive)
2201  {
2202  return true;
2203  }
2204 
2205  FindEnabledDown(unit);
2206 
2207  break;
2208 
2209  case MoveRow:
2210  if (m_itemList.empty() || m_columns < 1)
2211  return true;
2212  if (m_scrollStyle != ScrollFree)
2213  {
2215  m_selPosition %= m_itemList.size();
2216  }
2217  else if (((m_itemList.size() - 1) / qMax(m_columns, 0))
2218  > (pos / m_columns))
2219  {
2221  if (m_selPosition >= m_itemList.size())
2222  m_selPosition = m_itemList.size() - 1;
2223  }
2224  else if (m_wrapStyle > WrapNone)
2225  m_selPosition = (pos % m_columns);
2226  else if (m_wrapStyle == WrapCaptive)
2227  return true;
2228 
2229  FindEnabledDown(unit);
2230 
2231  break;
2232 
2233  case MovePage:
2234  if (m_arrange == ArrangeFixed)
2235  {
2236  m_selPosition = qMin(m_itemCount - 1,
2238  }
2239  else
2240  {
2241  m_selPosition = PageDown();
2242  }
2243 
2244  FindEnabledDown(unit);
2245 
2246  break;
2247 
2248  case MoveMax:
2249  m_selPosition = m_itemCount - 1;
2250  FindEnabledDown(unit);
2251  break;
2252 
2253  case MoveByAmount:
2254  for (uint i = 0; i < amount; ++i)
2255  {
2256  if (m_selPosition < m_itemList.size() - 1)
2257  ++m_selPosition;
2258  else if (m_wrapStyle > WrapNone)
2259  m_selPosition = 0;
2260  }
2261  FindEnabledDown(unit);
2262  break;
2263 
2264  case MoveMid:
2265  break;
2266  }
2267 
2268  SanitizePosition();
2269 
2270  if (pos != m_selPosition)
2271  {
2272  m_keepSelAtBottom = true;
2273  Update();
2274  emit itemSelected(GetItemCurrent());
2275  }
2276  else
2277  return false;
2278 
2279  return true;
2280 }
2281 
2282 bool MythUIButtonList::MoveToNamedPosition(const QString &position_name)
2283 {
2284  if (!m_initialized)
2285  Init();
2286 
2287  if (m_selPosition < 0 || m_itemList.isEmpty() || !m_initialized)
2288  return false;
2289 
2290  bool found_it = false;
2291  int selectedPosition = 0;
2292  QList<MythUIButtonListItem *>::iterator it = m_itemList.begin();
2293 
2294  while (it != m_itemList.end())
2295  {
2296  if ((*it)->GetText() == position_name)
2297  {
2298  found_it = true;
2299  break;
2300  }
2301 
2302  ++it;
2303  ++selectedPosition;
2304  }
2305 
2306  if (!found_it || m_selPosition == selectedPosition)
2307  return false;
2308 
2309  SetItemCurrent(selectedPosition);
2310  return true;
2311 }
2312 
2314 {
2315  if (GetItemCurrent() != item)
2316  return false;
2317 
2318  if (item == m_itemList.first() && up)
2319  return false;
2320 
2321  if (item == m_itemList.last() && !up)
2322  return false;
2323 
2324  int oldpos = m_selPosition;
2325  int insertat = 0;
2326  bool dolast = false;
2327 
2328  if (up)
2329  {
2330  insertat = m_selPosition - 1;
2331 
2332  if (item == m_itemList.last())
2333  dolast = true;
2334  else
2335  ++m_selPosition;
2336 
2337  if (item == m_itemList.at(m_topPosition))
2338  ++m_topPosition;
2339  }
2340  else
2341  insertat = m_selPosition + 1;
2342 
2343  m_itemList.removeAt(oldpos);
2344  m_itemList.insert(insertat, item);
2345 
2346  if (up)
2347  {
2348  MoveUp();
2349 
2350  if (!dolast)
2351  MoveUp();
2352  }
2353  else
2354  MoveDown();
2355 
2356  return true;
2357 }
2358 
2360 {
2361  QMutableListIterator<MythUIButtonListItem *> it(m_itemList);
2362 
2363  while (it.hasNext())
2364  it.next()->setChecked(state);
2365 }
2366 
2368 {
2369  if (m_initialized)
2370  return;
2371 
2372  m_upArrow = dynamic_cast<MythUIStateType *>(GetChild("upscrollarrow"));
2373  m_downArrow = dynamic_cast<MythUIStateType *>(GetChild("downscrollarrow"));
2374  m_scrollBar = dynamic_cast<MythUIScrollBar *>(GetChild("scrollbar"));
2375 
2376  if (m_upArrow)
2377  m_upArrow->SetVisible(true);
2378 
2379  if (m_downArrow)
2380  m_downArrow->SetVisible(true);
2381 
2382  if (m_scrollBar)
2384 
2386 
2387  m_buttontemplate = dynamic_cast<MythUIStateType *>(GetChild("buttonitem"));
2388 
2389  if (!m_buttontemplate)
2390  {
2391  LOG(VB_GENERAL, LOG_ERR, QString("(%1) Statetype buttonitem is "
2392  "required in mythuibuttonlist: %2")
2393  .arg(GetXMLLocation()).arg(objectName()));
2394  return;
2395  }
2396 
2397  m_buttontemplate->SetVisible(false);
2398 
2399  MythRect buttonItemArea;
2400 
2401  MythUIGroup *buttonActiveState = dynamic_cast<MythUIGroup *>
2402  (m_buttontemplate->GetState("active"));
2403 
2404  if (buttonActiveState)
2405  buttonItemArea = buttonActiveState->GetArea();
2406  else
2407  buttonItemArea = m_buttontemplate->GetArea();
2408 
2409  buttonItemArea.CalculateArea(m_contentsRect);
2410 
2411  m_itemHeight = buttonItemArea.height();
2412  m_itemWidth = buttonItemArea.width();
2413 
2414  /*
2415  * If fixed spacing is defined, then use the "active" state size
2416  * to predictively determine the position of each button.
2417  */
2418  if (m_arrange == ArrangeFixed)
2419  {
2420 
2422 
2423  int col = 1;
2424  int row = 1;
2425 
2426  for (int i = 0; i < m_itemsVisible; ++i)
2427  {
2428  QString name = QString("buttonlist button %1").arg(i);
2429  auto *button = new MythUIStateType(this, name);
2430  button->CopyFrom(m_buttontemplate);
2431  button->ConnectDependants(true);
2432 
2433  if (col > m_columns)
2434  {
2435  col = 1;
2436  ++row;
2437  }
2438 
2439  button->SetPosition(GetButtonPosition(col, row));
2440  ++col;
2441 
2442  m_buttonList.push_back(button);
2443  }
2444  }
2445 
2446  // The following is pretty much a hack for the benefit of MythGallery
2447  // it scales images based on the button size and we need to give it the
2448  // largest button state so that the images are not too small
2449  // This can be removed once the disk based image caching is added to
2450  // mythui, since the mythgallery thumbnail generator can be ditched.
2451  MythUIGroup *buttonSelectedState = dynamic_cast<MythUIGroup *>
2452  (m_buttontemplate->GetState("selected"));
2453 
2454  if (buttonSelectedState)
2455  {
2456  MythRect itemArea = buttonSelectedState->GetArea();
2457  itemArea.CalculateArea(m_contentsRect);
2458 
2459  if (m_itemHeight < itemArea.height())
2460  m_itemHeight = itemArea.height();
2461 
2462  if (m_itemWidth < itemArea.width())
2463  m_itemWidth = itemArea.width();
2464  }
2465 
2466  // End Hack
2467 
2468  m_initialized = true;
2469 }
2470 
2472 {
2473  if (!m_initialized)
2474  Init();
2475 
2476  return m_itemWidth;
2477 }
2478 
2480 {
2481  if (!m_initialized)
2482  Init();
2483 
2484  return m_itemHeight;
2485 }
2486 
2490 bool MythUIButtonList::keyPressEvent(QKeyEvent *event)
2491 {
2492  QStringList actions;
2493  bool handled = false;
2494  handled = GetMythMainWindow()->TranslateKeyPress("Global", event, actions);
2495 
2496  // Handle action remappings
2497  for (int i = 0; i < actions.size(); ++i)
2498  {
2499  if (!m_actionRemap.contains(actions[i]))
2500  continue;
2501 
2502  QString key = m_actionRemap[actions[i]];
2503  if (key.isEmpty())
2504  return true;
2505 
2506  QKeySequence a(key);
2507  if (a.isEmpty())
2508  continue;
2509 
2510  int keyCode = a[0];
2511  Qt::KeyboardModifiers modifiers = Qt::NoModifier;
2512  QStringList parts = key.split('+');
2513  for (int j = 0; j < parts.count(); ++j)
2514  {
2515  if (parts[j].toUpper() == "CTRL")
2516  modifiers |= Qt::ControlModifier;
2517  if (parts[j].toUpper() == "SHIFT")
2518  modifiers |= Qt::ShiftModifier;
2519  if (parts[j].toUpper() == "ALT")
2520  modifiers |= Qt::AltModifier;
2521  if (parts[j].toUpper() == "META")
2522  modifiers |= Qt::MetaModifier;
2523  }
2524 
2525  QCoreApplication::postEvent(
2527  new QKeyEvent(QEvent::KeyPress, keyCode, modifiers, key));
2528  QCoreApplication::postEvent(
2530  new QKeyEvent(QEvent::KeyRelease, keyCode, modifiers, key));
2531 
2532  return true;
2533  }
2534 
2535  // handle actions for this container
2536  for (int i = 0; i < actions.size() && !handled; ++i)
2537  {
2538  QString action = actions[i];
2539  handled = true;
2540 
2541  if (action == "UP")
2542  {
2543  if ((m_layout == LayoutVertical) || (m_layout == LayoutGrid))
2544  handled = MoveUp(MoveRow);
2545  else
2546  handled = false;
2547  }
2548  else if (action == "DOWN")
2549  {
2550  if ((m_layout == LayoutVertical) || (m_layout == LayoutGrid))
2551  handled = MoveDown(MoveRow);
2552  else
2553  handled = false;
2554  }
2555  else if (action == "RIGHT")
2556  {
2557  if (m_layout == LayoutHorizontal)
2558  handled = MoveDown(MoveItem);
2559  else if (m_layout == LayoutGrid)
2560  {
2561  if (m_scrollStyle == ScrollFree)
2562  handled = MoveDown(MoveColumn);
2563  else
2564  handled = MoveDown(MoveItem);
2565  }
2566  else
2567  handled = false;
2568  }
2569  else if (action == "LEFT")
2570  {
2571  if (m_layout == LayoutHorizontal)
2572  handled = MoveUp(MoveItem);
2573  else if (m_layout == LayoutGrid)
2574  {
2575  if (m_scrollStyle == ScrollFree)
2576  handled = MoveUp(MoveColumn);
2577  else
2578  handled = MoveUp(MoveItem);
2579  }
2580  else
2581  handled = false;
2582  }
2583  else if (action == "PAGEUP")
2584  {
2585  MoveUp(MovePage);
2586  }
2587  else if (action == "PAGEDOWN")
2588  {
2589  MoveDown(MovePage);
2590  }
2591  else if (action == "PAGETOP")
2592  {
2593  MoveUp(MoveMax);
2594  }
2595  else if (action == "PAGEMIDDLE")
2596  {
2597  MoveUp(MoveMid);
2598  }
2599  else if (action == "PAGEBOTTOM")
2600  {
2601  MoveDown(MoveMax);
2602  }
2603  else if (action == "SELECT")
2604  {
2606 
2607  if (item && item->isEnabled())
2608  emit itemClicked(item);
2609  }
2610  else if (action == "SEARCH")
2611  {
2612  ShowSearchDialog();
2613  }
2614  else
2615  handled = false;
2616  }
2617 
2618  return handled;
2619 }
2620 
2625 {
2626  bool handled = false;
2627 
2628  switch (event->GetGesture())
2629  {
2631  {
2632  // We want the relative position of the click
2633  QPoint position = event->GetPosition() -
2634  m_parent->GetArea().topLeft();
2635 
2636  MythUIType *type = GetChildAt(position, false, false);
2637 
2638  if (!type)
2639  return false;
2640 
2641  auto *object = dynamic_cast<MythUIStateType *>(type);
2642  if (object)
2643  {
2644  handled = true;
2645  QString name = object->objectName();
2646 
2647  if (name == "upscrollarrow")
2648  {
2649  MoveUp(MovePage);
2650  }
2651  else if (name == "downscrollarrow")
2652  {
2653  MoveDown(MovePage);
2654  }
2655  else if (name.startsWith("buttonlist button"))
2656  {
2657  int pos = name.section(' ', 2, 2).toInt();
2658  MythUIButtonListItem *item = m_buttonToItem.value(pos);
2659 
2660  if (item)
2661  {
2662  if (item == GetItemCurrent())
2663  emit itemClicked(item);
2664  else
2665  SetItemCurrent(item);
2666  }
2667  }
2668  else
2669  handled = false;
2670  }
2671  }
2672  break;
2673 
2674  case MythGestureEvent::Up:
2677  if ((m_layout == LayoutVertical) || (m_layout == LayoutGrid))
2678  handled = MoveUp(MoveRow);
2679  break;
2680 
2684  if ((m_layout == LayoutVertical) || (m_layout == LayoutGrid))
2685  handled = MoveDown(MoveRow);
2686  break;
2687 
2689  if (m_layout == LayoutHorizontal)
2690  handled = MoveDown(MoveItem);
2691  else if (m_layout == LayoutGrid)
2692  {
2693  if (m_scrollStyle == ScrollFree)
2694  handled = MoveDown(MoveColumn);
2695  else
2696  handled = MoveDown(MoveItem);
2697  }
2698  break;
2699 
2701  if (m_layout == LayoutHorizontal)
2702  handled = MoveUp(MoveItem);
2703  else if (m_layout == LayoutGrid)
2704  {
2705  if (m_scrollStyle == ScrollFree)
2706  handled = MoveUp(MoveColumn);
2707  else
2708  handled = MoveUp(MoveItem);
2709  }
2710  break;
2711 
2712  default:
2713  break;
2714  }
2715 
2716  return handled;
2717 }
2718 
2719 class NextButtonListPageEvent : public QEvent
2720 {
2721  public:
2722  NextButtonListPageEvent(int start, int pageSize) :
2723  QEvent(kEventType), m_start(start), m_pageSize(pageSize) {}
2724  const int m_start;
2725  const int m_pageSize;
2726  static Type kEventType;
2727 };
2728 
2730  (QEvent::Type) QEvent::registerEventType();
2731 
2733 {
2734  if (event->type() == NextButtonListPageEvent::kEventType)
2735  {
2736  auto *npe = dynamic_cast<NextButtonListPageEvent*>(event);
2737  int cur = npe->m_start;
2738  for (; cur < npe->m_start + npe->m_pageSize && cur < GetCount(); ++cur)
2739  {
2740  const int loginterval = (cur < 1000 ? 100 : 500);
2741  if (cur > 200 && cur % loginterval == 0)
2742  LOG(VB_GUI, LOG_INFO,
2743  QString("Build background buttonlist item %1").arg(cur));
2744  emit itemLoaded(GetItemAt(cur));
2745  }
2746  m_nextItemLoaded = cur;
2747  if (cur < GetCount())
2748  LoadInBackground(cur, npe->m_pageSize);
2749  }
2750 }
2751 
2752 void MythUIButtonList::LoadInBackground(int start, int pageSize)
2753 {
2754  m_nextItemLoaded = start;
2755  QCoreApplication::
2756  postEvent(this, new NextButtonListPageEvent(start, pageSize));
2757 }
2758 
2760 {
2761  QCoreApplication::
2762  removePostedEvents(this, NextButtonListPageEvent::kEventType);
2763  return m_nextItemLoaded;
2764 }
2765 
2766 QPoint MythUIButtonList::GetButtonPosition(int column, int row) const
2767 {
2768  int x = m_contentsRect.x() +
2769  ((column - 1) * (m_itemWidth + m_itemHorizSpacing));
2770  int y = m_contentsRect.y() +
2771  ((row - 1) * (m_itemHeight + m_itemVertSpacing));
2772 
2773  return {x, y};
2774 }
2775 
2777 {
2778  m_itemsVisible = 0;
2779  m_rows = 0;
2780  m_columns = 0;
2781 
2782  if ((m_layout == LayoutHorizontal) || (m_layout == LayoutGrid))
2783  {
2784  int x = 0;
2785 
2786  while (x <= m_contentsRect.width() - m_itemWidth)
2787  {
2789  ++m_columns;
2790  }
2791  }
2792 
2793  if ((m_layout == LayoutVertical) || (m_layout == LayoutGrid))
2794  {
2795  int y = 0;
2796 
2797  while (y <= m_contentsRect.height() - m_itemHeight)
2798  {
2800  ++m_rows;
2801  }
2802  }
2803 
2804  if (m_rows <= 0)
2805  m_rows = 1;
2806 
2807  if (m_columns <= 0)
2808  m_columns = 1;
2809 
2811 }
2812 
2814 {
2815  if (rect == m_contentsRect)
2816  return;
2817 
2818  m_contentsRect = rect;
2819 
2820  if (m_area.isValid())
2822  else if (m_parent)
2824  else
2825  m_contentsRect.CalculateArea(GetMythMainWindow()->GetUIScreenRect());
2826 }
2827 
2832  const QString &filename, QDomElement &element, bool showWarnings)
2833 {
2834  if (element.tagName() == "buttonarea")
2835  SetButtonArea(parseRect(element));
2836  else if (element.tagName() == "layout")
2837  {
2838  QString layout = getFirstText(element).toLower();
2839 
2840  if (layout == "grid")
2841  m_layout = LayoutGrid;
2842  else if (layout == "horizontal")
2844  else
2846  }
2847  else if (element.tagName() == "arrange")
2848  {
2849  QString arrange = getFirstText(element).toLower();
2850 
2851  if (arrange == "fill")
2853  else if (arrange == "spread")
2855  else if (arrange == "stack")
2857  else
2859 
2860  }
2861  else if (element.tagName() == "align")
2862  {
2863  QString align = getFirstText(element).toLower();
2864  m_alignment = parseAlignment(align);
2865  }
2866  else if (element.tagName() == "scrollstyle")
2867  {
2868  QString layout = getFirstText(element).toLower();
2869 
2870  if (layout == "center")
2872  else if (layout == "groupcenter")
2874  else if (layout == "free")
2876  }
2877  else if (element.tagName() == "wrapstyle")
2878  {
2879  QString wrapstyle = getFirstText(element).toLower();
2880 
2881  if (wrapstyle == "captive")
2883  else if (wrapstyle == "none")
2885  else if (wrapstyle == "selection")
2887  else if (wrapstyle == "flowing")
2889  else if (wrapstyle == "items")
2891  }
2892  else if (element.tagName() == "showarrow")
2893  m_showArrow = parseBool(element);
2894  else if (element.tagName() == "showscrollbar")
2895  m_showScrollBar = parseBool(element);
2896  else if (element.tagName() == "spacing")
2897  {
2898  m_itemHorizSpacing = NormX(getFirstText(element).toInt());
2899  m_itemVertSpacing = NormY(getFirstText(element).toInt());
2900  }
2901  else if (element.tagName() == "drawfrombottom")
2902  {
2903  m_drawFromBottom = parseBool(element);
2904 
2905  if (m_drawFromBottom)
2906  m_alignment |= Qt::AlignBottom;
2907  }
2908  else if (element.tagName() == "searchposition")
2909  {
2910  m_searchPosition = parsePoint(element);
2911  }
2912  else if (element.tagName() == "triggerevent")
2913  {
2914  QString trigger = getFirstText(element);
2915  if (!trigger.isEmpty())
2916  {
2917  QString action = element.attribute("action", "");
2918  if (action.isEmpty())
2919  {
2920  m_actionRemap[trigger] = "";
2921  }
2922  else
2923  {
2924  QString context = element.attribute("context", "");
2925  QString keylist = MythMainWindow::GetKey(context, action);
2926 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
2927  QStringList keys = keylist.split(',', QString::SkipEmptyParts);
2928 #else
2929  QStringList keys = keylist.split(',', Qt::SkipEmptyParts);
2930 #endif
2931  if (!keys.empty())
2932  m_actionRemap[trigger] = keys[0];
2933  }
2934  }
2935  }
2936  else
2937  {
2938  return MythUIType::ParseElement(filename, element, showWarnings);
2939  }
2940 
2941  return true;
2942 }
2943 
2947 void MythUIButtonList::DrawSelf(MythPainter * /*p*/, int /*xoffset*/, int /*yoffset*/,
2948  int /*alphaMod*/, QRect /*clipRect*/)
2949 {
2950  if (m_needsUpdate)
2951  {
2954  }
2955 }
2956 
2961 {
2962  auto *lb = new MythUIButtonList(parent, objectName());
2963  lb->CopyFrom(this);
2964 }
2965 
2970 {
2971  auto *lb = dynamic_cast<MythUIButtonList *>(base);
2972  if (!lb)
2973  return;
2974 
2975  m_layout = lb->m_layout;
2976  m_arrange = lb->m_arrange;
2977  m_alignment = lb->m_alignment;
2978 
2979  m_contentsRect = lb->m_contentsRect;
2980 
2981  m_itemHeight = lb->m_itemHeight;
2982  m_itemWidth = lb->m_itemWidth;
2983  m_itemHorizSpacing = lb->m_itemHorizSpacing;
2984  m_itemVertSpacing = lb->m_itemVertSpacing;
2985  m_itemsVisible = lb->m_itemsVisible;
2986  m_maxVisible = lb->m_maxVisible;
2987 
2988  m_active = lb->m_active;
2989  m_showArrow = lb->m_showArrow;
2990  m_showScrollBar = lb->m_showScrollBar;
2991 
2992  m_drawFromBottom = lb->m_drawFromBottom;
2993 
2994  m_scrollStyle = lb->m_scrollStyle;
2995  m_wrapStyle = lb->m_wrapStyle;
2996 
2997  m_clearing = false;
2999 
3000  m_searchPosition = lb->m_searchPosition;
3001  m_searchFields = lb->m_searchFields;
3002 
3003  MythUIType::CopyFrom(base);
3004 
3005  m_upArrow = dynamic_cast<MythUIStateType *>(GetChild("upscrollarrow"));
3006  m_downArrow = dynamic_cast<MythUIStateType *>(GetChild("downscrollarrow"));
3007  m_scrollBar = dynamic_cast<MythUIScrollBar *>(GetChild("scrollbar"));
3008 
3009  for (int i = 0; i < m_itemsVisible; ++i)
3010  {
3011  QString name = QString("buttonlist button %1").arg(i);
3012  DeleteChild(name);
3013  }
3014 
3015  m_buttonList.clear();
3016 
3017  m_actionRemap = lb->m_actionRemap;
3018 
3019  m_initialized = false;
3020 }
3021 
3026 {
3028 }
3029 
3030 void MythUIButtonList::SetLCDTitles(const QString &title, const QString &columnList)
3031 {
3032  m_lcdTitle = title;
3033  m_lcdColumns = columnList.split('|');
3034 }
3035 
3037 {
3038  if (!m_hasFocus)
3039  return;
3040 
3041  LCD *lcddev = LCD::Get();
3042 
3043  if (lcddev == nullptr)
3044  return;
3045 
3046  // Build a list of the menu items
3047  QList<LCDMenuItem> menuItems;
3048 
3049  int start = std::max(0, (int)(m_selPosition - lcddev->getLCDHeight()));
3050  int end = std::min(m_itemCount, (int)(start + (lcddev->getLCDHeight() * 2)));
3051 
3052  for (int r = start; r < end; ++r)
3053  {
3054  bool selected = r == GetCurrentPos();
3055 
3056  MythUIButtonListItem *item = GetItemAt(r);
3057  CHECKED_STATE state = NOTCHECKABLE;
3058 
3059  if (item->checkable())
3060  state = (item->state() == MythUIButtonListItem::NotChecked) ? UNCHECKED : CHECKED;
3061 
3062  QString text;
3063 
3064  for (int x = 0; x < m_lcdColumns.count(); ++x)
3065  {
3066  if (!m_lcdColumns[x].isEmpty() && item->m_strings.contains(m_lcdColumns[x]))
3067  {
3068  // named text column
3069  TextProperties props = item->m_strings[m_lcdColumns[x]];
3070 
3071  if (text.isEmpty())
3072  text = props.text;
3073  else
3074  text += " ~ " + props.text;
3075  }
3076  else
3077  {
3078  // default text column
3079  if (text.isEmpty())
3080  text = item->GetText();
3081  else
3082  text += " ~ " + item->GetText();
3083  }
3084  }
3085 
3086  if (!text.isEmpty())
3087  menuItems.append(LCDMenuItem(selected, state, text));
3088  else
3089  menuItems.append(LCDMenuItem(selected, state, item->GetText()));
3090  }
3091 
3092  if (!menuItems.isEmpty())
3093  lcddev->switchToMenu(menuItems, m_lcdTitle);
3094 }
3095 
3097 {
3098  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
3099 
3100  auto *dlg = new SearchButtonListDialog(popupStack, "MythSearchListDialog", this, "");
3101 
3102  if (dlg->Create())
3103  {
3104  if (m_searchPosition.x() != -2 || m_searchPosition.y() != -2)
3105  {
3106  int x = m_searchPosition.x();
3107  int y = m_searchPosition.y();
3108  QRect screenArea = GetMythMainWindow()->GetUIScreenRect();
3109  QRect dialogArea = dlg->GetArea();
3110 
3111  if (x == -1)
3112  x = (screenArea.width() - dialogArea.width()) / 2;
3113 
3114  if (y == -1)
3115  y = (screenArea.height() - dialogArea.height()) / 2;
3116 
3117  dlg->SetPosition(x, y);
3118  }
3119 
3120  popupStack->AddScreen(dlg);
3121  }
3122  else
3123  delete dlg;
3124 }
3125 
3126 bool MythUIButtonList::Find(const QString &searchStr, bool startsWith)
3127 {
3128  m_searchStr = searchStr;
3129  m_searchStartsWith = startsWith;
3130  return DoFind(false, true);
3131 }
3132 
3134 {
3135  return DoFind(true, true);
3136 }
3137 
3139 {
3140  return DoFind(true, false);
3141 }
3142 
3143 bool MythUIButtonList::DoFind(bool doMove, bool searchForward)
3144 {
3145  if (m_searchStr.isEmpty())
3146  return true;
3147 
3148  if (GetCount() == 0)
3149  return false;
3150 
3151  int startPos = GetCurrentPos();
3152  int currPos = startPos;
3153  bool found = false;
3154 
3155  if (doMove)
3156  {
3157  if (searchForward)
3158  {
3159  ++currPos;
3160 
3161  if (currPos >= GetCount())
3162  currPos = 0;
3163  }
3164  else
3165  {
3166  --currPos;
3167 
3168  if (currPos < 0)
3169  currPos = GetCount() - 1;
3170  }
3171  }
3172 
3173  while (true)
3174  {
3176 
3177  if (found)
3178  {
3179  SetItemCurrent(currPos);
3180  return true;
3181  }
3182 
3183  if (searchForward)
3184  {
3185  ++currPos;
3186 
3187  if (currPos >= GetCount())
3188  currPos = 0;
3189  }
3190  else
3191  {
3192  --currPos;
3193 
3194  if (currPos < 0)
3195  currPos = GetCount() - 1;
3196  }
3197 
3198  if (startPos == currPos)
3199  break;
3200  }
3201 
3202  return false;
3203 }
3204 
3206 
3208  QString text, QString image,
3209  bool checkable, CheckState state,
3210  bool showArrow, int listPosition)
3211  : m_parent(lbtype), m_text(std::move(text)), m_imageFilename(std::move(image)),
3212  m_checkable(checkable), m_state(state), m_showArrow(showArrow)
3213 {
3214  if (!lbtype)
3215  LOG(VB_GENERAL, LOG_ERR, "Cannot add a button to a non-existent list!");
3216 
3217  if (state >= NotChecked)
3218  m_checkable = true;
3219 
3220  if (m_parent)
3221  m_parent->InsertItem(this, listPosition);
3222 }
3223 
3225  const QString &text,
3226  QVariant data, int listPosition)
3227 {
3228  if (!lbtype)
3229  LOG(VB_GENERAL, LOG_ERR, "Cannot add a button to a non-existent list!");
3230 
3231  m_parent = lbtype;
3232  m_text = text;
3233  m_data = std::move(data);
3234 
3235  m_image = nullptr;
3236 
3237  m_checkable = false;
3238  m_state = CantCheck;
3239  m_showArrow = false;
3240  m_isVisible = false;
3241  m_enabled = true;
3242 
3243  if (m_parent)
3244  m_parent->InsertItem(this, listPosition);
3245 }
3246 
3248 {
3249  if (m_parent)
3250  m_parent->RemoveItem(this);
3251 
3252  if (m_image)
3253  m_image->DecrRef();
3254 
3255  QMap<QString, MythImage*>::iterator it;
3256  for (it = m_images.begin(); it != m_images.end(); ++it)
3257  {
3258  if (*it)
3259  (*it)->DecrRef();
3260  }
3261  m_images.clear();
3262 }
3263 
3264 void MythUIButtonListItem::SetText(const QString &text, const QString &name,
3265  const QString &state)
3266 {
3267  if (!name.isEmpty())
3268  {
3269  TextProperties textprop;
3270  textprop.text = text;
3271  textprop.state = state;
3272  m_strings.insert(name, textprop);
3273  }
3274  else
3275  m_text = text;
3276 
3277  if (m_parent && m_isVisible)
3278  m_parent->Update();
3279 }
3280 
3282  const QString &state)
3283 {
3284  InfoMap::const_iterator map_it = infoMap.begin();
3285 
3286  while (map_it != infoMap.end())
3287  {
3288  TextProperties textprop;
3289  textprop.text = (*map_it);
3290  textprop.state = state;
3291  m_strings[map_it.key()] = textprop;
3292  ++map_it;
3293  }
3294 
3295  if (m_parent && m_isVisible)
3296  m_parent->Update();
3297 }
3298 
3299 void MythUIButtonListItem::SetTextFromMap(const QMap<QString, TextProperties> &stringMap)
3300 {
3301  m_strings.clear();
3302  m_strings = stringMap;
3303 }
3304 
3305 QString MythUIButtonListItem::GetText(const QString &name) const
3306 {
3307  if (name.isEmpty())
3308  return m_text;
3309  if (m_strings.contains(name))
3310  return m_strings[name].text;
3311  return QString();
3312 }
3313 
3314 bool MythUIButtonListItem::FindText(const QString &searchStr, const QString &fieldList,
3315  bool startsWith) const
3316 {
3317  if (fieldList.isEmpty())
3318  {
3319  if (startsWith)
3320  return m_text.startsWith(searchStr, Qt::CaseInsensitive);
3321  return m_text.contains(searchStr, Qt::CaseInsensitive);
3322  }
3323  if (fieldList == "**ALL**")
3324  {
3325  if (startsWith)
3326  {
3327  if (m_text.startsWith(searchStr, Qt::CaseInsensitive))
3328  return true;
3329  }
3330  else
3331  {
3332  if (m_text.contains(searchStr, Qt::CaseInsensitive))
3333  return true;
3334  }
3335 
3336  QMap<QString, TextProperties>::const_iterator i = m_strings.constBegin();
3337 
3338  while (i != m_strings.constEnd())
3339  {
3340  if (startsWith)
3341  {
3342  if (i.value().text.startsWith(searchStr, Qt::CaseInsensitive))
3343  return true;
3344  }
3345  else
3346  {
3347  if (i.value().text.contains(searchStr, Qt::CaseInsensitive))
3348  return true;
3349  }
3350 
3351  ++i;
3352  }
3353  }
3354  else
3355  {
3356 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
3357  QStringList fields = fieldList.split(',', QString::SkipEmptyParts);
3358 #else
3359  QStringList fields = fieldList.split(',', Qt::SkipEmptyParts);
3360 #endif
3361 
3362  for (int x = 0; x < fields.count(); ++x)
3363  {
3364  if (m_strings.contains(fields.at(x).trimmed()))
3365  {
3366  if (startsWith)
3367  {
3368  if (m_strings[fields.at(x)].text.startsWith(searchStr, Qt::CaseInsensitive))
3369  return true;
3370  }
3371  else
3372  {
3373  if (m_strings[fields.at(x)].text.contains(searchStr, Qt::CaseInsensitive))
3374  return true;
3375  }
3376  }
3377  }
3378  }
3379 
3380  return false;
3381 }
3382 
3383 void MythUIButtonListItem::SetFontState(const QString &state,
3384  const QString &name)
3385 {
3386  if (!name.isEmpty())
3387  {
3388  if (m_strings.contains(name))
3389  m_strings[name].state = state;
3390  }
3391  else
3392  m_fontState = state;
3393 
3394  if (m_parent && m_isVisible)
3395  m_parent->Update();
3396 }
3397 
3398 void MythUIButtonListItem::SetImage(MythImage *image, const QString &name)
3399 {
3400  if (image)
3401  image->IncrRef();
3402 
3403  if (!name.isEmpty())
3404  {
3405  QMap<QString, MythImage*>::iterator it = m_images.find(name);
3406  if (it != m_images.end())
3407  {
3408  (*it)->DecrRef();
3409  if (image)
3410  *it = image;
3411  else
3412  m_images.erase(it);
3413  }
3414  else if (image)
3415  {
3416  m_images[name] = image;
3417  }
3418  }
3419  else
3420  {
3421  if (m_image)
3422  m_image->DecrRef();
3423  m_image = image;
3424  }
3425 
3426  if (m_parent && m_isVisible)
3427  m_parent->Update();
3428 }
3429 
3431 {
3432  m_imageFilenames.clear();
3433  m_imageFilenames = imageMap;
3434 }
3435 
3437 {
3438  if (!name.isEmpty())
3439  {
3440  QMap<QString, MythImage*>::iterator it = m_images.find(name);
3441  if (it != m_images.end())
3442  {
3443  (*it)->IncrRef();
3444  return (*it);
3445  }
3446  }
3447  else if (m_image)
3448  {
3449  m_image->IncrRef();
3450  return m_image;
3451  }
3452 
3453  return nullptr;
3454 }
3455 
3457  const QString &filename, const QString &name, bool force_reload)
3458 {
3459  bool do_update = force_reload;
3460 
3461  if (!name.isEmpty())
3462  {
3463  InfoMap::iterator it = m_imageFilenames.find(name);
3464 
3465  if (it == m_imageFilenames.end())
3466  {
3467  m_imageFilenames.insert(name, filename);
3468  do_update = true;
3469  }
3470  else if (*it != filename)
3471  {
3472  *it = filename;
3473  do_update = true;
3474  }
3475  }
3476  else if (m_imageFilename != filename)
3477  {
3479  do_update = true;
3480  }
3481 
3482  if (m_parent && do_update && m_isVisible)
3483  m_parent->Update();
3484 }
3485 
3486 QString MythUIButtonListItem::GetImageFilename(const QString &name) const
3487 {
3488  if (name.isEmpty())
3489  return m_imageFilename;
3490 
3491  InfoMap::const_iterator it = m_imageFilenames.find(name);
3492 
3493  if (it != m_imageFilenames.end())
3494  return *it;
3495 
3496  return QString();
3497 }
3498 
3499 void MythUIButtonListItem::DisplayState(const QString &state,
3500  const QString &name)
3501 {
3502  if (name.isEmpty())
3503  return;
3504 
3505  bool do_update = false;
3506  InfoMap::iterator it = m_states.find(name);
3507 
3508  if (it == m_states.end())
3509  {
3510  m_states.insert(name, state);
3511  do_update = true;
3512  }
3513  else if (*it != state)
3514  {
3515  *it = state;
3516  do_update = true;
3517  }
3518 
3519  if (m_parent && do_update && m_isVisible)
3520  m_parent->Update();
3521 }
3522 
3524 {
3525  m_states.clear();
3526  m_states = stateMap;
3527 }
3528 
3530 {
3531  return m_checkable;
3532 }
3533 
3535 {
3536  return m_state;
3537 }
3538 
3540 {
3541  return m_parent;
3542 }
3543 
3545 {
3546  if (!m_checkable || m_state == state)
3547  return;
3548 
3549  m_state = state;
3550 
3551  if (m_parent && m_isVisible)
3552  m_parent->Update();
3553 }
3554 
3556 {
3557  m_checkable = flag;
3558 }
3559 
3561 {
3562  m_showArrow = flag;
3563 }
3564 
3566 {
3567  return m_enabled;
3568 }
3569 
3571 {
3572  m_enabled = flag;
3573 }
3574 
3576 {
3577  m_data = std::move(data);
3578 }
3579 
3581 {
3582  return m_data;
3583 }
3584 
3586 {
3587  if (m_parent)
3588  return m_parent->MoveItemUpDown(this, flag);
3589  return false;
3590 }
3591 
3593 {
3594  if (!m_parent)
3595  return;
3596 
3597  m_parent->ItemVisible(this);
3598  m_isVisible = true;
3599 
3600  QString state;
3601 
3602  if (!m_parent->IsEnabled())
3603  state = "disabled";
3604  else if (!m_enabled)
3605  {
3606  state = m_parent->m_active ? "disabledactive" : "disabledinactive";
3607  }
3608  else if (selected)
3609  {
3610  button->MoveToTop();
3611  state = m_parent->m_active ? "selectedactive" : "selectedinactive";
3612  }
3613  else
3614  state = m_parent->m_active ? "active" : "inactive";
3615 
3616  // Begin compatibility code
3617  // Attempt to fallback if the theme is missing certain states
3618  if (state == "disabled" && !button->GetState(state))
3619  {
3620  LOG(VB_GUI, LOG_WARNING, "Theme Error: Missing buttonlist state: disabled");
3621  state = "inactive";
3622  }
3623 
3624  if (state == "inactive" && !button->GetState(state))
3625  {
3626  LOG(VB_GUI, LOG_WARNING, "Theme Error: Missing buttonlist state: inactive");
3627  state = "active";
3628  }
3629  // End compatibility code
3630 
3631  auto *buttonstate = dynamic_cast<MythUIGroup *>(button->GetState(state));
3632  if (!buttonstate)
3633  {
3634  LOG(VB_GENERAL, LOG_CRIT, QString("Theme Error: Missing buttonlist state: %1")
3635  .arg(state));
3636  return;
3637  }
3638 
3639  buttonstate->Reset();
3640 
3641  MythUIText *buttontext = dynamic_cast<MythUIText *>
3642  (buttonstate->GetChild("buttontext"));
3643 
3644  if (buttontext)
3645  {
3646  buttontext->SetText(m_text);
3647  buttontext->SetFontState(m_fontState);
3648  }
3649 
3650  MythUIImage *buttonimage = dynamic_cast<MythUIImage *>
3651  (buttonstate->GetChild("buttonimage"));
3652 
3653  if (buttonimage)
3654  {
3655  if (!m_imageFilename.isEmpty())
3656  {
3657  buttonimage->SetFilename(m_imageFilename);
3658  buttonimage->Load();
3659  }
3660  else if (m_image)
3661  buttonimage->SetImage(m_image);
3662  }
3663 
3664  MythUIImage *buttonarrow = dynamic_cast<MythUIImage *>
3665  (buttonstate->GetChild("buttonarrow"));
3666 
3667  if (buttonarrow)
3668  buttonarrow->SetVisible(m_showArrow);
3669 
3670  MythUIStateType *buttoncheck = dynamic_cast<MythUIStateType *>
3671  (buttonstate->GetChild("buttoncheck"));
3672 
3673  if (buttoncheck)
3674  {
3675  buttoncheck->SetVisible(m_checkable);
3676 
3677  if (m_checkable)
3678  {
3679  if (m_state == NotChecked)
3680  buttoncheck->DisplayState(MythUIStateType::Off);
3681  else if (m_state == HalfChecked)
3682  buttoncheck->DisplayState(MythUIStateType::Half);
3683  else
3684  buttoncheck->DisplayState(MythUIStateType::Full);
3685  }
3686  }
3687 
3688  QMap<QString, TextProperties>::iterator string_it = m_strings.begin();
3689 
3690  while (string_it != m_strings.end())
3691  {
3692  auto *text = dynamic_cast<MythUIText *>
3693  (buttonstate->GetChild(string_it.key()));
3694 
3695  if (text)
3696  {
3697  TextProperties textprop = string_it.value();
3698 
3699  QString newText = text->GetTemplateText();
3700 
3701  QRegularExpression re {R"(%(([^\|%]+)?\||\|(.))?([\w#]+)(\|(.+?))?%)",
3702  QRegularExpression::DotMatchesEverythingOption};
3703 
3704  if (!newText.isEmpty() && newText.contains(re))
3705  {
3706  QString tempString = newText;
3707 
3708  QRegularExpressionMatchIterator i = re.globalMatch(newText);
3709  while (i.hasNext()) {
3710  QRegularExpressionMatch match = i.next();
3711  QString key = match.captured(4).toLower().trimmed();
3712  QString replacement;
3713  QString value = m_strings.value(key).text;
3714 
3715  if (!value.isEmpty())
3716  {
3717  replacement = QString("%1%2%3%4")
3718  .arg(match.captured(2))
3719  .arg(match.captured(3))
3720  .arg(m_strings.value(key).text)
3721  .arg(match.captured(6));
3722  }
3723 
3724  tempString.replace(match.captured(0), replacement);
3725  }
3726 
3727  newText = tempString;
3728  }
3729  else
3730  newText = textprop.text;
3731 
3732  if (newText.isEmpty())
3733  text->Reset();
3734  else
3735  text->SetText(newText);
3736 
3737  text->SetFontState(textprop.state.isEmpty() ? m_fontState : textprop.state);
3738  }
3739 
3740  ++string_it;
3741  }
3742 
3743  InfoMap::iterator imagefile_it = m_imageFilenames.begin();
3744 
3745  while (imagefile_it != m_imageFilenames.end())
3746  {
3747  auto *image = dynamic_cast<MythUIImage *>
3748  (buttonstate->GetChild(imagefile_it.key()));
3749  if (image)
3750  {
3751  if (!imagefile_it.value().isEmpty())
3752  {
3753  image->SetFilename(imagefile_it.value());
3754  image->Load();
3755  }
3756  else
3757  image->Reset();
3758  }
3759 
3760  ++imagefile_it;
3761  }
3762 
3763  QMap<QString, MythImage *>::iterator image_it = m_images.begin();
3764 
3765  while (image_it != m_images.end())
3766  {
3767  auto *image = dynamic_cast<MythUIImage *>
3768  (buttonstate->GetChild(image_it.key()));
3769  if (image)
3770  {
3771  if (image_it.value())
3772  image->SetImage(image_it.value());
3773  else
3774  image->Reset();
3775  }
3776 
3777  ++image_it;
3778  }
3779 
3780  InfoMap::iterator state_it = m_states.begin();
3781 
3782  while (state_it != m_states.end())
3783  {
3784  auto *statetype = dynamic_cast<MythUIStateType *>
3785  (buttonstate->GetChild(state_it.key()));
3786  if (statetype)
3787  {
3788  if (!statetype->DisplayState(state_it.value()))
3789  statetype->Reset();
3790  }
3791 
3792  ++state_it;
3793  }
3794 
3795  // There is no need to check the return value here, since we already
3796  // checked that the state exists with GetState() earlier
3797  button->DisplayState(state);
3798 }
3799 
3800 //---------------------------------------------------------
3801 // SearchButtonListDialog
3802 //---------------------------------------------------------
3804 {
3805  if (!CopyWindowFromBase("MythSearchListDialog", this))
3806  return false;
3807 
3808  bool err = false;
3809  UIUtilE::Assign(this, m_searchEdit, "searchedit", &err);
3810  UIUtilE::Assign(this, m_prevButton, "prevbutton", &err);
3811  UIUtilE::Assign(this, m_nextButton, "nextbutton", &err);
3812  UIUtilW::Assign(this, m_searchState, "searchstate");
3813 
3814  if (err)
3815  {
3816  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'MythSearchListDialog'");
3817  return false;
3818  }
3819 
3821 
3825 
3826  BuildFocusList();
3827 
3828  return true;
3829 }
3830 
3832 {
3833  if (GetFocusWidget() && GetFocusWidget()->keyPressEvent(event))
3834  return true;
3835 
3836  QStringList actions;
3837  bool handled = GetMythMainWindow()->TranslateKeyPress("Global", event, actions, false);
3838 
3839  for (int i = 0; i < actions.size() && !handled; ++i)
3840  {
3841  QString action = actions[i];
3842  handled = true;
3843 
3844  if (action == "0")
3845  {
3847  searchChanged();
3848  }
3849  else
3850  handled = false;
3851  }
3852 
3853  if (!handled && MythScreenType::keyPressEvent(event))
3854  handled = true;
3855 
3856  return handled;
3857 }
3858 
3860 {
3861  bool found = m_parentList->Find(m_searchEdit->GetText(), m_startsWith);
3862 
3863  if (m_searchState)
3864  m_searchState->DisplayState(found ? "found" : "notfound");
3865 }
3866 
3868 {
3869  bool found = m_parentList->FindNext();
3870 
3871  if (m_searchState)
3872  m_searchState->DisplayState(found ? "found" : "notfound");
3873 }
3874 
3876 {
3877  bool found = m_parentList->FindPrev();
3878 
3879  if (m_searchState)
3880  m_searchState->DisplayState(found ? "found" : "notfound");
3881 }
3882 
3884 {
3886  return;
3887 
3888  int maximum = (m_itemCount <= m_itemsVisible) ? 0 : m_itemCount;
3889  m_scrollBar->SetMaximum(maximum);
3893 }
MythUIButton::Clicked
void Clicked()
MythUIText::SetFontState
void SetFontState(const QString &state)
Definition: mythuitext.cpp:221
MythGestureEvent::Down
@ Down
Definition: mythgesture.h:50
MythUIButtonList::GetItemAt
MythUIButtonListItem * GetItemAt(int pos) const
Definition: mythuibuttonlist.cpp:1676
MythUIType::m_area
MythRect m_area
Definition: mythuitype.h:271
MythUIButtonList::StopLoad
int StopLoad(void)
Definition: mythuibuttonlist.cpp:2759
MythUIButtonList::ScrollCenter
@ ScrollCenter
Definition: mythuibuttonlist.h:235
MythUIButtonList::m_upArrow
MythUIStateType * m_upArrow
Definition: mythuibuttonlist.h:329
MythUIButtonList::SetDrawFromBottom
void SetDrawFromBottom(bool draw)
Definition: mythuibuttonlist.cpp:95
MythUIButtonList::m_topPosition
int m_topPosition
Definition: mythuibuttonlist.h:343
MythUIButtonList::GetItemCurrent
MythUIButtonListItem * GetItemCurrent() const
Definition: mythuibuttonlist.cpp:1590
MythUIButtonList::m_itemList
QList< MythUIButtonListItem * > m_itemList
Definition: mythuibuttonlist.h:347
MythUIButtonListItem::HalfChecked
@ HalfChecked
Definition: mythuibuttonlist.h:33
MythUIButtonList::SetValueByData
void SetValueByData(const QVariant &data)
Definition: mythuibuttonlist.cpp:1542
mythuitext.h
NextButtonListPageEvent::kEventType
static Type kEventType
Definition: mythuibuttonlist.cpp:2726
XMLParseBase::parseAlignment
static int parseAlignment(const QString &text)
Definition: xmlparsebase.cpp:162
MythUIButtonList::Const
void Const()
Definition: mythuibuttonlist.cpp:56
SearchButtonListDialog::nextClicked
void nextClicked(void)
Definition: mythuibuttonlist.cpp:3867
MythUIImage
Image widget, displays a single image or multiple images in sequence.
Definition: mythuiimage.h:98
XMLParseBase::parsePoint
static MythPoint parsePoint(const QString &text, bool normalize=true)
Definition: xmlparsebase.cpp:76
MythUIType::DeleteChild
void DeleteChild(const QString &name)
Delete a named child of this UIType.
Definition: mythuitype.cpp:146
MythUIButtonList::ArrangeSpread
@ ArrangeSpread
Definition: mythuibuttonlist.h:236
MythGestureEvent::UpRight
@ UpRight
Definition: mythgesture.h:56
MythUIButtonList::GetItemNext
MythUIButtonListItem * GetItemNext(MythUIButtonListItem *item) const
Definition: mythuibuttonlist.cpp:1644
MythUIScreenBounds::GetUIScreenRect
QRect GetUIScreenRect()
Definition: mythuiscreenbounds.cpp:192
MythUIButtonList::m_scrollStyle
ScrollStyle m_scrollStyle
Definition: mythuibuttonlist.h:300
MythUIButtonList::DrawSelf
void DrawSelf(MythPainter *p, int xoffset, int yoffset, int alphaMod, QRect clipRect) override
Definition: mythuibuttonlist.cpp:2947
MythUIButtonList::GetButtonPosition
virtual QPoint GetButtonPosition(int column, int row) const
Definition: mythuibuttonlist.cpp:2766
MythUIButtonList::m_itemHeight
int m_itemHeight
Definition: mythuibuttonlist.h:312
MythUIButtonList::Find
bool Find(const QString &searchStr, bool startsWith=false)
Definition: mythuibuttonlist.cpp:3126
MythGestureEvent::GetGesture
Gesture GetGesture() const
Definition: mythgesture.h:85
MythUIButtonListItem::DisplayState
void DisplayState(const QString &state, const QString &name)
Definition: mythuibuttonlist.cpp:3499
MythUIType::GetChildAt
MythUIType * GetChildAt(QPoint p, bool recursive=true, bool focusable=true) const
Return the first MythUIType at the given coordinates.
Definition: mythuitype.cpp:222
MythUIButtonListItem::m_strings
QMap< QString, TextProperties > m_strings
Definition: mythuibuttonlist.h:133
NextButtonListPageEvent::NextButtonListPageEvent
NextButtonListPageEvent(int start, int pageSize)
Definition: mythuibuttonlist.cpp:2722
MythGestureEvent::Up
@ Up
Definition: mythgesture.h:49
NextButtonListPageEvent::m_pageSize
const int m_pageSize
Definition: mythuibuttonlist.cpp:2725
MythUIType::GetFullArea
virtual MythRect GetFullArea(void) const
Definition: mythuitype.cpp:878
MythUIButtonListItem::SetImageFromMap
void SetImageFromMap(const InfoMap &imageMap)
Definition: mythuibuttonlist.cpp:3430
MythUIType::Enabling
void Enabling()
MythUIStateType::GetState
MythUIType * GetState(const QString &name)
Definition: mythuistatetype.cpp:147
MythUIButtonList::m_itemCount
int m_itemCount
Definition: mythuibuttonlist.h:344
MythUIButtonList::MoveDown
virtual bool MoveDown(MovementUnit unit=MoveItem, uint amount=0)
Definition: mythuibuttonlist.cpp:2163
MythUIButtonList::m_needsUpdate
bool m_needsUpdate
Definition: mythuibuttonlist.h:339
title
QString title
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:636
MythUIButtonList::gestureEvent
bool gestureEvent(MythGestureEvent *event) override
Mouse click/movement handler, receives mouse gesture events from the QCoreApplication event loop.
Definition: mythuibuttonlist.cpp:2624
MythMainWindow::TranslateKeyPress
bool TranslateKeyPress(const QString &context, QKeyEvent *e, QStringList &actions, bool allowJumps=true)
Get a list of actions for a keypress in the given context.
Definition: mythmainwindow.cpp:1142
MythUIType::GetChild
MythUIType * GetChild(const QString &name) const
Get a named child of this UIType.
Definition: mythuitype.cpp:131
MythUIButtonList::m_showArrow
bool m_showArrow
Definition: mythuibuttonlist.h:325
MythUIButtonList::RemoveItem
void RemoveItem(MythUIButtonListItem *item)
Definition: mythuibuttonlist.cpp:1488
MythUIImage::Load
bool Load(bool allowLoadInBackground=true, bool forceStat=false)
Load the image(s), wraps ImageLoader::LoadImage()
Definition: mythuiimage.cpp:968
MythUIButtonList::m_layout
LayoutType m_layout
Definition: mythuibuttonlist.h:298
MythUIButtonList::m_buttontemplate
MythUIStateType * m_buttontemplate
Definition: mythuibuttonlist.h:332
MythUIButtonList::itemSelected
void itemSelected(MythUIButtonListItem *item)
MythUIButtonList::MoveRow
@ MoveRow
Definition: mythuibuttonlist.h:165
MythUIButtonList::Finalize
void Finalize(void) override
Perform any post-xml parsing initialisation tasks.
Definition: mythuibuttonlist.cpp:3025
MythUIButtonListItem::m_imageFilenames
InfoMap m_imageFilenames
Definition: mythuibuttonlist.h:135
MythUIStateType::GetCurrentState
MythUIType * GetCurrentState()
Definition: mythuistatetype.h:41
MythScreenStack
Definition: mythscreenstack.h:16
MythUIButtonList::FindEnabledUp
void FindEnabledUp(MovementUnit unit)
Definition: mythuibuttonlist.cpp:2117
arg
arg(title).arg(filename).arg(doDelete))
MythUIButtonList::ArrangeStack
@ ArrangeStack
Definition: mythuibuttonlist.h:236
MythUIButtonListItem::SetStatesFromMap
void SetStatesFromMap(const InfoMap &stateMap)
Definition: mythuibuttonlist.cpp:3523
MythUIType::SetCanTakeFocus
void SetCanTakeFocus(bool set=true)
Set whether this widget can take focus.
Definition: mythuitype.cpp:343
MythUIButtonListItem::m_fontState
QString m_fontState
Definition: mythuibuttonlist.h:123
mythuiscrollbar.h
MythUIButtonListItem::parent
MythUIButtonList * parent() const
Definition: mythuibuttonlist.cpp:3539
MythUIButtonListItem::FindText
bool FindText(const QString &searchStr, const QString &fieldList="**ALL**", bool startsWith=false) const
Definition: mythuibuttonlist.cpp:3314
MythUIButtonList::minButtonWidth
int minButtonWidth(const MythRect &area)
Definition: mythuibuttonlist.cpp:152
MythUIButtonList::SetButtonArea
void SetButtonArea(const MythRect &rect)
Definition: mythuibuttonlist.cpp:2813
SearchButtonListDialog::m_searchText
QString m_searchText
Definition: mythuibuttonlist.h:381
MythUIGroup
Create a group of widgets.
Definition: mythuigroup.h:12
MythUIButtonList::WrapNone
@ WrapNone
Definition: mythuibuttonlist.h:237
NextButtonListPageEvent::m_start
const int m_start
Definition: mythuibuttonlist.cpp:2724
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
mythuistatetype.h
MythUIButtonList::m_showScrollBar
bool m_showScrollBar
Definition: mythuibuttonlist.h:326
MythUIButtonList::m_alignment
int m_alignment
Definition: mythuibuttonlist.h:302
MythUIButtonListItem::m_imageFilename
QString m_imageFilename
Definition: mythuibuttonlist.h:125
MythUIStateType::Reset
void Reset(void) override
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuistatetype.cpp:197
MythUITextEdit::GetText
QString GetText(void) const
Definition: mythuitextedit.h:47
MythUIButtonList::WrapFlowing
@ WrapFlowing
Definition: mythuibuttonlist.h:238
UNCHECKED
@ UNCHECKED
Definition: lcddevice.h:17
MythUIButtonList::DoFind
bool DoFind(bool doMove, bool searchForward)
Definition: mythuibuttonlist.cpp:3143
MythUIButtonList::ParseElement
bool ParseElement(const QString &filename, QDomElement &element, bool showWarnings) override
Parse the xml definition of this widget setting the state of the object accordingly.
Definition: mythuibuttonlist.cpp:2831
SearchButtonListDialog
Definition: mythuibuttonlist.h:360
MythRect
Wrapper around QRect allowing us to handle percentage and other relative values for areas in mythui.
Definition: mythrect.h:18
MythUIButtonList::m_nextItemLoaded
int m_nextItemLoaded
Definition: mythuibuttonlist.h:348
SearchButtonListDialog::m_startsWith
bool m_startsWith
Definition: mythuibuttonlist.h:378
MythUIButtonListItem::~MythUIButtonListItem
virtual ~MythUIButtonListItem()
Definition: mythuibuttonlist.cpp:3247
MythMainWindow::GetStack
MythScreenStack * GetStack(const QString &stackname)
Definition: mythmainwindow.cpp:329
mythuibuttonlist.h
MythUIButtonList::SanitizePosition
void SanitizePosition(void)
Definition: mythuibuttonlist.cpp:1385
LCD::Get
static LCD * Get(void)
Definition: lcddevice.cpp:67
mythuiimage.h
MythUIButtonList::GetCount
int GetCount() const
Definition: mythuibuttonlist.cpp:1655
MythUIButtonList::m_buttonToItem
QMap< int, MythUIButtonListItem * > m_buttonToItem
Definition: mythuibuttonlist.h:335
MythUIButtonList::m_bottomRows
int m_bottomRows
Definition: mythuibuttonlist.h:322
MythUIButtonList::m_drawFromBottom
bool m_drawFromBottom
Definition: mythuibuttonlist.h:350
MythDate::current
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
MythUIButtonList::DistributeButtons
bool DistributeButtons(void)
Definition: mythuibuttonlist.cpp:732
MythUIButtonListItem::CheckState
CheckState
Definition: mythuibuttonlist.h:30
MythUIButtonList::GetItemByData
MythUIButtonListItem * GetItemByData(const QVariant &data)
Definition: mythuibuttonlist.cpp:1684
MythScreenType::GetFocusWidget
MythUIType * GetFocusWidget(void) const
Definition: mythscreentype.cpp:112
MythUIType::TakingFocus
void TakingFocus()
MythUIType::GetArea
virtual MythRect GetArea(void) const
If the object has a minimum area defined, return it, other wise return the default area.
Definition: mythuitype.cpp:870
MythUIButtonList::customEvent
void customEvent(QEvent *event) override
Definition: mythuibuttonlist.cpp:2732
SearchButtonListDialog::m_parentList
MythUIButtonList * m_parentList
Definition: mythuibuttonlist.h:380
InfoMap
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
MythUIButtonList::ItemHeight
uint ItemHeight(void)
Definition: mythuibuttonlist.cpp:2479
MythUIButtonList::MovePage
@ MovePage
Definition: mythuibuttonlist.h:165
MythUIButtonListItem::SetText
void SetText(const QString &text, const QString &name="", const QString &state="")
Definition: mythuibuttonlist.cpp:3264
MythUIButtonList::m_scrollBar
MythUIScrollBar * m_scrollBar
Definition: mythuibuttonlist.h:328
MythUIButtonList::GetItemPos
int GetItemPos(MythUIButtonListItem *item) const
Definition: mythuibuttonlist.cpp:1698
MythUIButtonList::m_itemVertSpacing
int m_itemVertSpacing
Definition: mythuibuttonlist.h:314
MythUIButtonList::ItemVisible
void ItemVisible(MythUIButtonListItem *item)
Definition: mythuibuttonlist.cpp:1453
MythUIButtonListItem
Definition: mythuibuttonlist.h:28
MythUIButtonList::ScrollGroupCenter
@ ScrollGroupCenter
Definition: mythuibuttonlist.h:235
MythUITextEdit::SetText
void SetText(const QString &text, bool moveCursor=true)
Definition: mythuitextedit.cpp:216
MythUIButtonList::IsEmpty
bool IsEmpty() const
Definition: mythuibuttonlist.cpp:1671
MythUIButtonList::PageDown
int PageDown(void)
Definition: mythuibuttonlist.cpp:1839
MythUIButtonList::~MythUIButtonList
~MythUIButtonList() override
Definition: mythuibuttonlist.cpp:65
MythUIButtonList::MoveItemUpDown
bool MoveItemUpDown(MythUIButtonListItem *item, bool up)
Definition: mythuibuttonlist.cpp:2313
MythUIButtonList::m_contentsRect
MythRect m_contentsRect
Definition: mythuibuttonlist.h:304
MythUIButtonListItem::CantCheck
@ CantCheck
Definition: mythuibuttonlist.h:31
MythUIType::m_hasFocus
bool m_hasFocus
Definition: mythuitype.h:259
MythUIButtonList::itemLoaded
void itemLoaded(MythUIButtonListItem *item)
mythlogging.h
MythUIButtonList::m_lcdColumns
QStringList m_lcdColumns
Definition: mythuibuttonlist.h:353
MythUIButtonList::m_rows
int m_rows
Definition: mythuibuttonlist.h:317
MythUIButtonListItem::setCheckable
void setCheckable(bool flag)
Definition: mythuibuttonlist.cpp:3555
MythUIButtonList::itemClicked
void itemClicked(MythUIButtonListItem *item)
MythUIButtonList::MoveMax
@ MoveMax
Definition: mythuibuttonlist.h:165
MythUIButtonList::Deselect
void Deselect()
Definition: mythuibuttonlist.cpp:84
MythUIType::GetXMLLocation
QString GetXMLLocation(void) const
Definition: mythuitype.h:177
MythUIButtonListItem::isEnabled
bool isEnabled() const
Definition: mythuibuttonlist.cpp:3565
MythUIButtonList::GetCurrentPos
int GetCurrentPos() const
Definition: mythuibuttonlist.h:198
MythUIType::SetMinArea
virtual void SetMinArea(const MythRect &rect)
Set the minimum area based on the given size.
Definition: mythuitype.cpp:805
MythUIType::DependChanged
void DependChanged(bool isDefault)
MythUIType::SetPosition
void SetPosition(int x, int y)
Convenience method, calls SetPosition(const MythPoint&) Override that instead to change functionality...
Definition: mythuitype.cpp:518
MythUIButtonListItem::m_states
InfoMap m_states
Definition: mythuibuttonlist.h:136
MythUIType::IsEnabled
bool IsEnabled(void) const
Definition: mythuitype.h:115
MythUIType::Disabling
void Disabling()
MythUIButtonList::m_itemsVisible
int m_itemsVisible
Definition: mythuibuttonlist.h:315
MythUIButtonList::WrapSelect
@ WrapSelect
Definition: mythuibuttonlist.h:237
MythUIButtonList::m_searchPosition
MythPoint m_searchPosition
Definition: mythuibuttonlist.h:306
MythUIButtonList::keyPressEvent
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: mythuibuttonlist.cpp:2490
MythUIButtonList::LoadInBackground
void LoadInBackground(int start=0, int pageSize=20)
Definition: mythuibuttonlist.cpp:2752
MythUIButtonList::Update
void Update()
Definition: mythuibuttonlist.cpp:139
MythUIButtonListItem::GetImage
MythImage * GetImage(const QString &name="")
Gets a MythImage which has been assigned to this button item, as with SetImage() it should only be us...
Definition: mythuibuttonlist.cpp:3436
MythUIButtonList::MoveByAmount
@ MoveByAmount
Definition: mythuibuttonlist.h:166
MythUIScrollBar::SetSliderPosition
void SetSliderPosition(int value)
Definition: mythuiscrollbar.cpp:69
MythUIButtonList::minButtonHeight
int minButtonHeight(const MythRect &area)
Definition: mythuibuttonlist.cpp:180
MythScreenType::BuildFocusList
void BuildFocusList(void)
Definition: mythscreentype.cpp:222
XMLParseBase::CopyWindowFromBase
static bool CopyWindowFromBase(const QString &windowname, MythScreenType *win)
Definition: xmlparsebase.cpp:916
MythUIButtonList::m_itemHorizSpacing
int m_itemHorizSpacing
Definition: mythuibuttonlist.h:313
XMLParseBase::getFirstText
static QString getFirstText(QDomElement &element)
Definition: xmlparsebase.cpp:53
MythUIButtonList::m_topRows
int m_topRows
Definition: mythuibuttonlist.h:321
MythUIStateType::Half
@ Half
Definition: mythuistatetype.h:25
MythImage::DecrRef
int DecrRef(void) override
Decrements reference count and deletes on 0.
Definition: mythimage.cpp:54
MythRect::setHeight
void setHeight(const QString &sHeight)
Definition: mythrect.cpp:277
MythUIType::m_parent
MythUIType * m_parent
Definition: mythuitype.h:291
TextProperties
Definition: mythuibuttonlist.h:22
MythUIType::NormY
static int NormY(int height)
Definition: mythuitype.cpp:1144
MythGestureEvent::Right
@ Right
Definition: mythgesture.h:52
MythRect::CalculateArea
void CalculateArea(QRect parentArea)
Definition: mythrect.cpp:64
MythUIButtonList::ArrangeFill
@ ArrangeFill
Definition: mythuibuttonlist.h:236
MythUIButtonList::MythUIButtonList
MythUIButtonList(MythUIType *parent, const QString &name)
Definition: mythuibuttonlist.cpp:29
filename
QString filename
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:637
MythUIButtonList::InsertItem
void InsertItem(MythUIButtonListItem *item, int listPosition=-1)
Definition: mythuibuttonlist.cpp:1459
TextProperties::state
QString state
Definition: mythuibuttonlist.h:24
MythUIButtonList::InitButton
void InitButton(int itemIdx, MythUIStateType *&realButton, MythUIButtonListItem *&buttonItem)
Definition: mythuibuttonlist.cpp:1706
MythUIButtonList::GetDataValue
QVariant GetDataValue() const
Definition: mythuibuttonlist.cpp:1619
MythUIButtonList::m_clearing
bool m_clearing
Definition: mythuibuttonlist.h:340
SearchButtonListDialog::searchChanged
void searchChanged(void)
Definition: mythuibuttonlist.cpp:3859
MythGestureEvent::UpLeft
@ UpLeft
Definition: mythgesture.h:55
SearchButtonListDialog::m_prevButton
MythUIButton * m_prevButton
Definition: mythuibuttonlist.h:384
MythUIButtonListItem::checkable
bool checkable() const
Definition: mythuibuttonlist.cpp:3529
MythUIButtonList::m_initialized
bool m_initialized
Definition: mythuibuttonlist.h:338
MythGestureEvent::Click
@ Click
Definition: mythgesture.h:77
MythUIButtonList::WrapItems
@ WrapItems
Definition: mythuibuttonlist.h:237
MythUIButtonList::SetAllChecked
void SetAllChecked(MythUIButtonListItem::CheckState state)
Definition: mythuibuttonlist.cpp:2359
MythUIType::CopyFrom
virtual void CopyFrom(MythUIType *base)
Copy this widgets state from another.
Definition: mythuitype.cpp:1152
MythUIButtonList::ScrollFree
@ ScrollFree
Definition: mythuibuttonlist.h:235
MythUIButtonList::MoveUp
virtual bool MoveUp(MovementUnit unit=MoveItem, uint amount=0)
Definition: mythuibuttonlist.cpp:1945
MythUIButtonList::SetLCDTitles
void SetLCDTitles(const QString &title, const QString &columnList="")
Definition: mythuibuttonlist.cpp:3030
MythUIButtonList::CalculateButtonPositions
void CalculateButtonPositions(void)
Definition: mythuibuttonlist.cpp:1257
MythUIButtonList::GetIntValue
virtual int GetIntValue() const
Definition: mythuibuttonlist.cpp:1599
MythUIButtonList::m_searchStr
QString m_searchStr
Definition: mythuibuttonlist.h:308
SearchButtonListDialog::Create
bool Create(void) override
Definition: mythuibuttonlist.cpp:3803
MythUIStateType::Full
@ Full
Definition: mythuistatetype.h:25
MythUIButtonList::SetScrollBarPosition
void SetScrollBarPosition(void)
Definition: mythuibuttonlist.cpp:3883
XMLParseBase::parseRect
static MythRect parseRect(const QString &text, bool normalize=true)
Definition: xmlparsebase.cpp:137
MythUIButtonList::DistributeCols
bool DistributeCols(int &first_button, int &last_button, int &first_item, int &last_item, int &selected_column, int &selected_row, int &skip_cols, int **col_widths, QList< int > &row_heights, int &top_height, int &bottom_height, bool &wrapped)
Definition: mythuibuttonlist.cpp:614
MythUIButtonList::ArrangeFixed
@ ArrangeFixed
Definition: mythuibuttonlist.h:236
MythUIButtonList::Init
virtual void Init()
Definition: mythuibuttonlist.cpp:2367
MythUIButtonListItem::GetData
QVariant GetData()
Definition: mythuibuttonlist.cpp:3580
uint
unsigned int uint
Definition: compat.h:141
MythUIScrollBar
Scroll bar widget.
Definition: mythuiscrollbar.h:16
MythUIButtonList::FindNext
bool FindNext(void)
Definition: mythuibuttonlist.cpp:3133
MythUIButtonList::GetVisibleCount
int GetVisibleCount()
Definition: mythuibuttonlist.cpp:1660
MythUIButtonList::m_maxVisible
int m_maxVisible
Definition: mythuibuttonlist.h:316
MythUIButtonList::m_searchStartsWith
bool m_searchStartsWith
Definition: mythuibuttonlist.h:309
MythUIType::MoveToTop
bool MoveToTop(void)
Definition: mythuitype.cpp:1355
MythUIButtonList::MovementUnit
MovementUnit
Definition: mythuibuttonlist.h:165
MythGestureEvent::Left
@ Left
Definition: mythgesture.h:51
MythUIButtonList::itemVisible
void itemVisible(MythUIButtonListItem *item)
mythgesture.h
A C++ ripoff of the stroke library for MythTV.
MythUIButtonListItem::SetToRealButton
virtual void SetToRealButton(MythUIStateType *button, bool selected)
Definition: mythuibuttonlist.cpp:3592
MythUIType::Reset
virtual void Reset(void)
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuitype.cpp:70
MythUIButtonList::MoveColumn
@ MoveColumn
Definition: mythuibuttonlist.h:165
UIUtilDisp::Assign
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)
Definition: mythuiutils.h:27
mythuigroup.h
LCD::getLCDHeight
uint getLCDHeight(void) const
Definition: lcddevice.h:292
MythUIType
The base class on which all widgets and screens are based.
Definition: mythuitype.h:85
NextButtonListPageEvent
Definition: mythuibuttonlist.cpp:2720
MythUIButtonListItem::SetTextFromMap
void SetTextFromMap(const InfoMap &infoMap, const QString &state="")
Definition: mythuibuttonlist.cpp:3281
LCDMenuItem
Definition: lcddevice.h:22
MythUIButtonListItem::m_images
QMap< QString, MythImage * > m_images
Definition: mythuibuttonlist.h:134
MythUIButtonListItem::GetText
QString GetText(const QString &name="") const
Definition: mythuibuttonlist.cpp:3305
MythImage::IncrRef
int IncrRef(void) override
Increments reference count.
Definition: mythimage.cpp:46
MythUIScrollBar::SetPageStep
void SetPageStep(int value)
Definition: mythuiscrollbar.cpp:45
SearchButtonListDialog::m_searchEdit
MythUITextEdit * m_searchEdit
Definition: mythuibuttonlist.h:383
MythUIStateType::Off
@ Off
Definition: mythuistatetype.h:25
MythUIButtonListItem::SetFontState
void SetFontState(const QString &state, const QString &name="")
Definition: mythuibuttonlist.cpp:3383
MythUIButtonList::PageUp
int PageUp(void)
Definition: mythuibuttonlist.cpp:1734
MythUIButtonList::m_actionRemap
QHash< QString, QString > m_actionRemap
Definition: mythuibuttonlist.h:336
MythUIType::m_initiator
bool m_initiator
Definition: mythuitype.h:263
MythUIButtonListItem::m_state
CheckState m_state
Definition: mythuibuttonlist.h:127
MythUIButtonList::ToggleEnabled
void ToggleEnabled()
Definition: mythuibuttonlist.cpp:89
MythUIText
All purpose text widget, displays a text string.
Definition: mythuitext.h:29
MythScreenType::keyPressEvent
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: mythscreentype.cpp:414
CHECKED_STATE
CHECKED_STATE
Definition: lcddevice.h:17
MythUIButtonListItem::m_text
QString m_text
Definition: mythuibuttonlist.h:122
MythUIButtonList::m_lcdTitle
QString m_lcdTitle
Definition: mythuibuttonlist.h:352
MythPoint::isValid
bool isValid(void) const
Definition: mythrect.h:102
MythUIButtonList::LayoutVertical
@ LayoutVertical
Definition: mythuibuttonlist.h:167
MythRect::topLeft
MythPoint topLeft(void) const
Definition: mythrect.cpp:288
MythUIButtonListItem::m_parent
MythUIButtonList * m_parent
Definition: mythuibuttonlist.h:121
MythRect::setWidth
void setWidth(const QString &sWidth)
Definition: mythrect.cpp:266
MythUIButtonListItem::m_checkable
bool m_checkable
Definition: mythuibuttonlist.h:126
MythUIButtonList::DistributeRow
bool DistributeRow(int &first_button, int &last_button, int &first_item, int &last_item, int &selected_column, int &skip_cols, bool grow_left, bool grow_right, int **col_widths, int &row_height, int total_height, int split_height, int &col_cnt, bool &wrapped)
Definition: mythuibuttonlist.cpp:254
mythuitextedit.h
MythUIButtonListItem::SetImage
void SetImage(MythImage *image, const QString &name="")
Sets an image directly, should only be used in special circumstances since it bypasses the cache.
Definition: mythuibuttonlist.cpp:3398
MythUIButtonList::CalculateVisibleItems
virtual void CalculateVisibleItems(void)
Definition: mythuibuttonlist.cpp:2776
MythPainter
Definition: mythpainter.h:33
MythImage
Definition: mythimage.h:37
MythUIButtonList::m_columns
int m_columns
Definition: mythuibuttonlist.h:318
MythUIScrollBar::SetMaximum
void SetMaximum(int value)
Definition: mythuiscrollbar.cpp:57
NOTCHECKABLE
@ NOTCHECKABLE
Definition: lcddevice.h:17
MythUIButtonList::m_selPosition
int m_selPosition
Definition: mythuibuttonlist.h:342
MythUIButtonListItem::setEnabled
void setEnabled(bool flag)
Definition: mythuibuttonlist.cpp:3570
MythUIButtonListItem::m_data
QVariant m_data
Definition: mythuibuttonlist.h:128
MythUIButtonList::CalculateArrowStates
void CalculateArrowStates(void)
Definition: mythuibuttonlist.cpp:1393
MythRect::setY
void setY(const QString &sY)
Definition: mythrect.cpp:256
MythUIType::m_minSize
MythPoint m_minSize
Definition: mythuitype.h:273
MythUIButtonList::GetValue
virtual QString GetValue() const
Definition: mythuibuttonlist.cpp:1609
MythUIText::SetText
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:134
MythUIButtonListItem::state
CheckState state() const
Definition: mythuibuttonlist.cpp:3534
MythUIType::SetVisible
virtual void SetVisible(bool visible)
Definition: mythuitype.cpp:1086
SearchButtonListDialog::keyPressEvent
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: mythuibuttonlist.cpp:3831
MythUIButtonList::GetButtonArea
MythRect GetButtonArea(void) const
Definition: mythuibuttonlist.cpp:1629
MythUIButtonList::Reset
void Reset() override
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuibuttonlist.cpp:114
GetMythMainWindow
MythMainWindow * GetMythMainWindow(void)
Definition: mythmainwindow.cpp:108
MythUIButtonList::MoveToNamedPosition
bool MoveToNamedPosition(const QString &position_name)
Definition: mythuibuttonlist.cpp:2282
MythUIButtonList::SetItemCurrent
void SetItemCurrent(MythUIButtonListItem *item)
Definition: mythuibuttonlist.cpp:1557
build_compdb.action
action
Definition: build_compdb.py:9
MythUIButtonList::m_arrange
ArrangeType m_arrange
Definition: mythuibuttonlist.h:299
MythUIButtonList::m_leftColumns
int m_leftColumns
Definition: mythuibuttonlist.h:319
MythUIButtonList::MoveMid
@ MoveMid
Definition: mythuibuttonlist.h:166
MythUIButtonListItem::setDrawArrow
void setDrawArrow(bool flag)
Definition: mythuibuttonlist.cpp:3560
MythUIImage::SetImage
void SetImage(MythImage *img)
Should not be used unless absolutely necessary since it bypasses the image caching and threaded loade...
Definition: mythuiimage.cpp:748
MythUIButtonList::m_rightColumns
int m_rightColumns
Definition: mythuibuttonlist.h:320
mythuibutton.h
MythUIButtonList::WrapCaptive
@ WrapCaptive
Definition: mythuibuttonlist.h:237
MythUIButtonList::m_active
bool m_active
Definition: mythuibuttonlist.h:324
SearchButtonListDialog::prevClicked
void prevClicked(void)
Definition: mythuibuttonlist.cpp:3875
MythUIButtonList::PrepareButton
MythUIGroup * PrepareButton(int buttonIdx, int itemIdx, int &selectedIdx, int &button_shift)
Definition: mythuibuttonlist.cpp:205
MythUITextEdit::valueChanged
void valueChanged()
MythUIButtonList::m_searchFields
QString m_searchFields
Definition: mythuibuttonlist.h:307
MythGestureEvent::DownLeft
@ DownLeft
Definition: mythgesture.h:57
MythUIButtonListItem::m_isVisible
bool m_isVisible
Definition: mythuibuttonlist.h:130
MythUIButtonList::m_itemWidth
int m_itemWidth
Definition: mythuibuttonlist.h:311
SearchButtonListDialog::m_searchState
MythUIStateType * m_searchState
Definition: mythuibuttonlist.h:386
MythUIButtonList::FindPrev
bool FindPrev(void)
Definition: mythuibuttonlist.cpp:3138
MythGestureEvent
A custom event that represents a mouse gesture.
Definition: mythgesture.h:40
MythUIButtonListItem::GetImageFilename
QString GetImageFilename(const QString &name="") const
Definition: mythuibuttonlist.cpp:3486
MythUIButtonList::LayoutGrid
@ LayoutGrid
Definition: mythuibuttonlist.h:167
MythUIButtonListItem::m_image
MythImage * m_image
Definition: mythuibuttonlist.h:124
CHECKED
@ CHECKED
Definition: lcddevice.h:17
MythUIButtonList::m_buttonList
QVector< MythUIStateType * > m_buttonList
Definition: mythuibuttonlist.h:334
MythUIButtonList::MoveItem
@ MoveItem
Definition: mythuibuttonlist.h:165
MythUIButtonList::ItemWidth
uint ItemWidth(void)
Definition: mythuibuttonlist.cpp:2471
MythUIType::NormX
static int NormX(int width)
Definition: mythuitype.cpp:1139
MythUIButtonList::FindEnabledDown
void FindEnabledDown(MovementUnit unit)
If the current item is not enabled, find the next enabled one.
Definition: mythuibuttonlist.cpp:2072
MythUIButtonListItem::m_showArrow
bool m_showArrow
Definition: mythuibuttonlist.h:129
MythUIButtonList::CreateCopy
void CreateCopy(MythUIType *parent) override
Copy the state of this widget to the one given, it must be of the same type.
Definition: mythuibuttonlist.cpp:2960
MythUIButtonList::m_keepSelAtBottom
bool m_keepSelAtBottom
Definition: mythuibuttonlist.h:345
MythUIButtonListItem::MythUIButtonListItem
MythUIButtonListItem(MythUIButtonList *lbtype, QString text, QString image="", bool checkable=false, CheckState state=CantCheck, bool showArrow=false, int listPosition=-1)
Definition: mythuibuttonlist.cpp:3207
lcddevice.h
MythUIImage::SetFilename
void SetFilename(const QString &filename)
Must be followed by a call to Load() to load the image.
Definition: mythuiimage.cpp:676
MythUIButtonList::ShowSearchDialog
void ShowSearchDialog(void)
Definition: mythuibuttonlist.cpp:3096
MythUIButtonList::LayoutHorizontal
@ LayoutHorizontal
Definition: mythuibuttonlist.h:167
MythUIType::LosingFocus
void LosingFocus()
MythUIType::ParseElement
virtual bool ParseElement(const QString &filename, QDomElement &element, bool showWarnings)
Parse the xml definition of this widget setting the state of the object accordingly.
Definition: mythuitype.cpp:1218
MythUIButtonList::m_downArrow
MythUIStateType * m_downArrow
Definition: mythuibuttonlist.h:330
MythUIButtonList::GetItemFirst
MythUIButtonListItem * GetItemFirst() const
Definition: mythuibuttonlist.cpp:1636
MythUIButtonList::Select
void Select()
Definition: mythuibuttonlist.cpp:74
MythUIButtonList
List widget, displays list items in a variety of themeable arrangements and can trigger signals when ...
Definition: mythuibuttonlist.h:152
MythGestureEvent::DownRight
@ DownRight
Definition: mythgesture.h:58
TextProperties::text
QString text
Definition: mythuibuttonlist.h:23
mythmainwindow.h
SearchButtonListDialog::m_nextButton
MythUIButton * m_nextButton
Definition: mythuibuttonlist.h:385
MythUIButtonListItem::m_enabled
bool m_enabled
Definition: mythuibuttonlist.h:131
MythUIButtonListItem::setChecked
void setChecked(CheckState state)
Definition: mythuibuttonlist.cpp:3544
MythUIButtonListItem::SetData
void SetData(QVariant data)
Definition: mythuibuttonlist.cpp:3575
MythScreenStack::AddScreen
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
Definition: mythscreenstack.cpp:52
MythUIType::SetRedraw
void SetRedraw(void)
Definition: mythuitype.cpp:294
XMLParseBase::parseBool
static bool parseBool(const QString &text)
Definition: xmlparsebase.cpp:65
MythUIButtonListItem::MoveUpDown
bool MoveUpDown(bool flag)
Definition: mythuibuttonlist.cpp:3585
MythUIButtonList::updateLCD
void updateLCD(void)
Definition: mythuibuttonlist.cpp:3036
MythUIType::m_enableInitiator
bool m_enableInitiator
Definition: mythuitype.h:262
LCD
Definition: lcddevice.h:170
MythUIStateType
This widget is used for grouping other widgets for display when a particular named state is called.
Definition: mythuistatetype.h:23
MythRect::setX
void setX(const QString &sX)
Definition: mythrect.cpp:246
LCD::switchToMenu
void switchToMenu(QList< LCDMenuItem > &menuItems, const QString &app_name="", bool popMenu=true)
Definition: lcddevice.cpp:588
MythUIType::Finalize
virtual void Finalize(void)
Perform any post-xml parsing initialisation tasks.
Definition: mythuitype.cpp:1294
MythUIStateType::DisplayState
bool DisplayState(const QString &name)
Definition: mythuistatetype.cpp:84
MythMainWindow::GetKey
static QString GetKey(const QString &context, const QString &action)
Definition: mythmainwindow.cpp:1339
MythUIButtonList::m_wrapStyle
WrapStyle m_wrapStyle
Definition: mythuibuttonlist.h:301
MythUIButtonListItem::NotChecked
@ NotChecked
Definition: mythuibuttonlist.h:32
MythUIButtonList::SetActive
void SetActive(bool active)
Definition: mythuibuttonlist.cpp:100
MythUIButtonList::CopyFrom
void CopyFrom(MythUIType *base) override
Copy this widgets state from another.
Definition: mythuibuttonlist.cpp:2969