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
33  connect(this, SIGNAL(Enabling()), this, SLOT(ToggleEnabled()));
34  connect(this, SIGNAL(Disabling()), this, SLOT(ToggleEnabled()));
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 
50  connect(this, SIGNAL(Enabling()), this, SLOT(ToggleEnabled()));
51  connect(this, SIGNAL(Disabling()), this, SLOT(ToggleEnabled()));
52 
53  Const();
54 }
55 
57 {
58 
59  SetCanTakeFocus(true);
60 
61  connect(this, SIGNAL(TakingFocus()), this, SLOT(Select()));
62  connect(this, SIGNAL(LosingFocus()), this, SLOT(Deselect()));
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->gesture())
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:
2675  if ((m_layout == LayoutVertical) || (m_layout == LayoutGrid))
2676  handled = MoveUp(MovePage);
2677  break;
2678 
2681  if ((m_layout == LayoutVertical) || (m_layout == LayoutGrid))
2682  handled = MoveUp(MoveRow);
2683  break;
2684 
2686  if ((m_layout == LayoutVertical) || (m_layout == LayoutGrid))
2687  handled = MoveDown(MovePage);
2688  break;
2689 
2692  if ((m_layout == LayoutVertical) || (m_layout == LayoutGrid))
2693  handled = MoveDown(MoveRow);
2694  break;
2695 
2697  if (m_layout == LayoutHorizontal)
2698  handled = MoveDown(MoveItem);
2699  else if (m_layout == LayoutGrid)
2700  {
2701  if (m_scrollStyle == ScrollFree)
2702  handled = MoveDown(MoveColumn);
2703  else
2704  handled = MoveDown(MoveItem);
2705  }
2706  break;
2707 
2709  if (m_layout == LayoutHorizontal)
2710  handled = MoveUp(MoveItem);
2711  else if (m_layout == LayoutGrid)
2712  {
2713  if (m_scrollStyle == ScrollFree)
2714  handled = MoveUp(MoveColumn);
2715  else
2716  handled = MoveUp(MoveItem);
2717  }
2718  break;
2719 
2720  default:
2721  break;
2722  }
2723 
2724  return handled;
2725 }
2726 
2727 class NextButtonListPageEvent : public QEvent
2728 {
2729  public:
2730  NextButtonListPageEvent(int start, int pageSize) :
2731  QEvent(kEventType), m_start(start), m_pageSize(pageSize) {}
2732  const int m_start;
2733  const int m_pageSize;
2734  static Type kEventType;
2735 };
2736 
2738  (QEvent::Type) QEvent::registerEventType();
2739 
2741 {
2742  if (event->type() == NextButtonListPageEvent::kEventType)
2743  {
2744  auto *npe = dynamic_cast<NextButtonListPageEvent*>(event);
2745  int cur = npe->m_start;
2746  for (; cur < npe->m_start + npe->m_pageSize && cur < GetCount(); ++cur)
2747  {
2748  const int loginterval = (cur < 1000 ? 100 : 500);
2749  if (cur > 200 && cur % loginterval == 0)
2750  LOG(VB_GUI, LOG_INFO,
2751  QString("Build background buttonlist item %1").arg(cur));
2752  emit itemLoaded(GetItemAt(cur));
2753  }
2754  m_nextItemLoaded = cur;
2755  if (cur < GetCount())
2756  LoadInBackground(cur, npe->m_pageSize);
2757  }
2758 }
2759 
2760 void MythUIButtonList::LoadInBackground(int start, int pageSize)
2761 {
2762  m_nextItemLoaded = start;
2763  QCoreApplication::
2764  postEvent(this, new NextButtonListPageEvent(start, pageSize));
2765 }
2766 
2768 {
2769  QCoreApplication::
2770  removePostedEvents(this, NextButtonListPageEvent::kEventType);
2771  return m_nextItemLoaded;
2772 }
2773 
2774 QPoint MythUIButtonList::GetButtonPosition(int column, int row) const
2775 {
2776  int x = m_contentsRect.x() +
2777  ((column - 1) * (m_itemWidth + m_itemHorizSpacing));
2778  int y = m_contentsRect.y() +
2779  ((row - 1) * (m_itemHeight + m_itemVertSpacing));
2780 
2781  return {x, y};
2782 }
2783 
2785 {
2786  m_itemsVisible = 0;
2787  m_rows = 0;
2788  m_columns = 0;
2789 
2790  if ((m_layout == LayoutHorizontal) || (m_layout == LayoutGrid))
2791  {
2792  int x = 0;
2793 
2794  while (x <= m_contentsRect.width() - m_itemWidth)
2795  {
2797  ++m_columns;
2798  }
2799  }
2800 
2801  if ((m_layout == LayoutVertical) || (m_layout == LayoutGrid))
2802  {
2803  int y = 0;
2804 
2805  while (y <= m_contentsRect.height() - m_itemHeight)
2806  {
2808  ++m_rows;
2809  }
2810  }
2811 
2812  if (m_rows <= 0)
2813  m_rows = 1;
2814 
2815  if (m_columns <= 0)
2816  m_columns = 1;
2817 
2819 }
2820 
2822 {
2823  if (rect == m_contentsRect)
2824  return;
2825 
2826  m_contentsRect = rect;
2827 
2828  if (m_area.isValid())
2830  else if (m_parent)
2832  else
2833  m_contentsRect.CalculateArea(GetMythMainWindow()->GetUIScreenRect());
2834 }
2835 
2840  const QString &filename, QDomElement &element, bool showWarnings)
2841 {
2842  if (element.tagName() == "buttonarea")
2843  SetButtonArea(parseRect(element));
2844  else if (element.tagName() == "layout")
2845  {
2846  QString layout = getFirstText(element).toLower();
2847 
2848  if (layout == "grid")
2849  m_layout = LayoutGrid;
2850  else if (layout == "horizontal")
2852  else
2854  }
2855  else if (element.tagName() == "arrange")
2856  {
2857  QString arrange = getFirstText(element).toLower();
2858 
2859  if (arrange == "fill")
2861  else if (arrange == "spread")
2863  else if (arrange == "stack")
2865  else
2867 
2868  }
2869  else if (element.tagName() == "align")
2870  {
2871  QString align = getFirstText(element).toLower();
2872  m_alignment = parseAlignment(align);
2873  }
2874  else if (element.tagName() == "scrollstyle")
2875  {
2876  QString layout = getFirstText(element).toLower();
2877 
2878  if (layout == "center")
2880  else if (layout == "groupcenter")
2882  else if (layout == "free")
2884  }
2885  else if (element.tagName() == "wrapstyle")
2886  {
2887  QString wrapstyle = getFirstText(element).toLower();
2888 
2889  if (wrapstyle == "captive")
2891  else if (wrapstyle == "none")
2893  else if (wrapstyle == "selection")
2895  else if (wrapstyle == "flowing")
2897  else if (wrapstyle == "items")
2899  }
2900  else if (element.tagName() == "showarrow")
2901  m_showArrow = parseBool(element);
2902  else if (element.tagName() == "showscrollbar")
2903  m_showScrollBar = parseBool(element);
2904  else if (element.tagName() == "spacing")
2905  {
2906  m_itemHorizSpacing = NormX(getFirstText(element).toInt());
2907  m_itemVertSpacing = NormY(getFirstText(element).toInt());
2908  }
2909  else if (element.tagName() == "drawfrombottom")
2910  {
2911  m_drawFromBottom = parseBool(element);
2912 
2913  if (m_drawFromBottom)
2914  m_alignment |= Qt::AlignBottom;
2915  }
2916  else if (element.tagName() == "searchposition")
2917  {
2918  m_searchPosition = parsePoint(element);
2919  }
2920  else if (element.tagName() == "triggerevent")
2921  {
2922  QString trigger = getFirstText(element);
2923  if (!trigger.isEmpty())
2924  {
2925  QString action = element.attribute("action", "");
2926  if (action.isEmpty())
2927  {
2928  m_actionRemap[trigger] = "";
2929  }
2930  else
2931  {
2932  QString context = element.attribute("context", "");
2933  QString keylist = MythMainWindow::GetKey(context, action);
2934 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
2935  QStringList keys = keylist.split(',', QString::SkipEmptyParts);
2936 #else
2937  QStringList keys = keylist.split(',', Qt::SkipEmptyParts);
2938 #endif
2939  if (!keys.empty())
2940  m_actionRemap[trigger] = keys[0];
2941  }
2942  }
2943  }
2944  else
2945  {
2946  return MythUIType::ParseElement(filename, element, showWarnings);
2947  }
2948 
2949  return true;
2950 }
2951 
2955 void MythUIButtonList::DrawSelf(MythPainter * /*p*/, int /*xoffset*/, int /*yoffset*/,
2956  int /*alphaMod*/, QRect /*clipRect*/)
2957 {
2958  if (m_needsUpdate)
2959  {
2962  }
2963 }
2964 
2969 {
2970  auto *lb = new MythUIButtonList(parent, objectName());
2971  lb->CopyFrom(this);
2972 }
2973 
2978 {
2979  auto *lb = dynamic_cast<MythUIButtonList *>(base);
2980  if (!lb)
2981  return;
2982 
2983  m_layout = lb->m_layout;
2984  m_arrange = lb->m_arrange;
2985  m_alignment = lb->m_alignment;
2986 
2987  m_contentsRect = lb->m_contentsRect;
2988 
2989  m_itemHeight = lb->m_itemHeight;
2990  m_itemWidth = lb->m_itemWidth;
2991  m_itemHorizSpacing = lb->m_itemHorizSpacing;
2992  m_itemVertSpacing = lb->m_itemVertSpacing;
2993  m_itemsVisible = lb->m_itemsVisible;
2994  m_maxVisible = lb->m_maxVisible;
2995 
2996  m_active = lb->m_active;
2997  m_showArrow = lb->m_showArrow;
2998  m_showScrollBar = lb->m_showScrollBar;
2999 
3000  m_drawFromBottom = lb->m_drawFromBottom;
3001 
3002  m_scrollStyle = lb->m_scrollStyle;
3003  m_wrapStyle = lb->m_wrapStyle;
3004 
3005  m_clearing = false;
3007 
3008  m_searchPosition = lb->m_searchPosition;
3009  m_searchFields = lb->m_searchFields;
3010 
3011  MythUIType::CopyFrom(base);
3012 
3013  m_upArrow = dynamic_cast<MythUIStateType *>(GetChild("upscrollarrow"));
3014  m_downArrow = dynamic_cast<MythUIStateType *>(GetChild("downscrollarrow"));
3015  m_scrollBar = dynamic_cast<MythUIScrollBar *>(GetChild("scrollbar"));
3016 
3017  for (int i = 0; i < m_itemsVisible; ++i)
3018  {
3019  QString name = QString("buttonlist button %1").arg(i);
3020  DeleteChild(name);
3021  }
3022 
3023  m_buttonList.clear();
3024 
3025  m_actionRemap = lb->m_actionRemap;
3026 
3027  m_initialized = false;
3028 }
3029 
3034 {
3036 }
3037 
3038 void MythUIButtonList::SetLCDTitles(const QString &title, const QString &columnList)
3039 {
3040  m_lcdTitle = title;
3041  m_lcdColumns = columnList.split('|');
3042 }
3043 
3045 {
3046  if (!m_hasFocus)
3047  return;
3048 
3049  LCD *lcddev = LCD::Get();
3050 
3051  if (lcddev == nullptr)
3052  return;
3053 
3054  // Build a list of the menu items
3055  QList<LCDMenuItem> menuItems;
3056 
3057  int start = std::max(0, (int)(m_selPosition - lcddev->getLCDHeight()));
3058  int end = std::min(m_itemCount, (int)(start + (lcddev->getLCDHeight() * 2)));
3059 
3060  for (int r = start; r < end; ++r)
3061  {
3062  bool selected = r == GetCurrentPos();
3063 
3064  MythUIButtonListItem *item = GetItemAt(r);
3065  CHECKED_STATE state = NOTCHECKABLE;
3066 
3067  if (item->checkable())
3068  state = (item->state() == MythUIButtonListItem::NotChecked) ? UNCHECKED : CHECKED;
3069 
3070  QString text;
3071 
3072  for (int x = 0; x < m_lcdColumns.count(); ++x)
3073  {
3074  if (!m_lcdColumns[x].isEmpty() && item->m_strings.contains(m_lcdColumns[x]))
3075  {
3076  // named text column
3077  TextProperties props = item->m_strings[m_lcdColumns[x]];
3078 
3079  if (text.isEmpty())
3080  text = props.text;
3081  else
3082  text += " ~ " + props.text;
3083  }
3084  else
3085  {
3086  // default text column
3087  if (text.isEmpty())
3088  text = item->GetText();
3089  else
3090  text += " ~ " + item->GetText();
3091  }
3092  }
3093 
3094  if (!text.isEmpty())
3095  menuItems.append(LCDMenuItem(selected, state, text));
3096  else
3097  menuItems.append(LCDMenuItem(selected, state, item->GetText()));
3098  }
3099 
3100  if (!menuItems.isEmpty())
3101  lcddev->switchToMenu(menuItems, m_lcdTitle);
3102 }
3103 
3105 {
3106  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
3107 
3108  auto *dlg = new SearchButtonListDialog(popupStack, "MythSearchListDialog", this, "");
3109 
3110  if (dlg->Create())
3111  {
3112  if (m_searchPosition.x() != -2 || m_searchPosition.y() != -2)
3113  {
3114  int x = m_searchPosition.x();
3115  int y = m_searchPosition.y();
3116  QRect screenArea = GetMythMainWindow()->GetUIScreenRect();
3117  QRect dialogArea = dlg->GetArea();
3118 
3119  if (x == -1)
3120  x = (screenArea.width() - dialogArea.width()) / 2;
3121 
3122  if (y == -1)
3123  y = (screenArea.height() - dialogArea.height()) / 2;
3124 
3125  dlg->SetPosition(x, y);
3126  }
3127 
3128  popupStack->AddScreen(dlg);
3129  }
3130  else
3131  delete dlg;
3132 }
3133 
3134 bool MythUIButtonList::Find(const QString &searchStr, bool startsWith)
3135 {
3136  m_searchStr = searchStr;
3137  m_searchStartsWith = startsWith;
3138  return DoFind(false, true);
3139 }
3140 
3142 {
3143  return DoFind(true, true);
3144 }
3145 
3147 {
3148  return DoFind(true, false);
3149 }
3150 
3151 bool MythUIButtonList::DoFind(bool doMove, bool searchForward)
3152 {
3153  if (m_searchStr.isEmpty())
3154  return true;
3155 
3156  if (GetCount() == 0)
3157  return false;
3158 
3159  int startPos = GetCurrentPos();
3160  int currPos = startPos;
3161  bool found = false;
3162 
3163  if (doMove)
3164  {
3165  if (searchForward)
3166  {
3167  ++currPos;
3168 
3169  if (currPos >= GetCount())
3170  currPos = 0;
3171  }
3172  else
3173  {
3174  --currPos;
3175 
3176  if (currPos < 0)
3177  currPos = GetCount() - 1;
3178  }
3179  }
3180 
3181  while (true)
3182  {
3184 
3185  if (found)
3186  {
3187  SetItemCurrent(currPos);
3188  return true;
3189  }
3190 
3191  if (searchForward)
3192  {
3193  ++currPos;
3194 
3195  if (currPos >= GetCount())
3196  currPos = 0;
3197  }
3198  else
3199  {
3200  --currPos;
3201 
3202  if (currPos < 0)
3203  currPos = GetCount() - 1;
3204  }
3205 
3206  if (startPos == currPos)
3207  break;
3208  }
3209 
3210  return false;
3211 }
3212 
3214 
3216  QString text, QString image,
3217  bool checkable, CheckState state,
3218  bool showArrow, int listPosition)
3219  : m_parent(lbtype), m_text(std::move(text)), m_imageFilename(std::move(image)),
3220  m_checkable(checkable), m_state(state), m_showArrow(showArrow)
3221 {
3222  if (!lbtype)
3223  LOG(VB_GENERAL, LOG_ERR, "Cannot add a button to a non-existent list!");
3224 
3225  if (state >= NotChecked)
3226  m_checkable = true;
3227 
3228  if (m_parent)
3229  m_parent->InsertItem(this, listPosition);
3230 }
3231 
3233  const QString &text,
3234  QVariant data, int listPosition)
3235 {
3236  if (!lbtype)
3237  LOG(VB_GENERAL, LOG_ERR, "Cannot add a button to a non-existent list!");
3238 
3239  m_parent = lbtype;
3240  m_text = text;
3241  m_data = std::move(data);
3242 
3243  m_image = nullptr;
3244 
3245  m_checkable = false;
3246  m_state = CantCheck;
3247  m_showArrow = false;
3248  m_isVisible = false;
3249  m_enabled = true;
3250 
3251  if (m_parent)
3252  m_parent->InsertItem(this, listPosition);
3253 }
3254 
3256 {
3257  if (m_parent)
3258  m_parent->RemoveItem(this);
3259 
3260  if (m_image)
3261  m_image->DecrRef();
3262 
3263  QMap<QString, MythImage*>::iterator it;
3264  for (it = m_images.begin(); it != m_images.end(); ++it)
3265  {
3266  if (*it)
3267  (*it)->DecrRef();
3268  }
3269  m_images.clear();
3270 }
3271 
3272 void MythUIButtonListItem::SetText(const QString &text, const QString &name,
3273  const QString &state)
3274 {
3275  if (!name.isEmpty())
3276  {
3277  TextProperties textprop;
3278  textprop.text = text;
3279  textprop.state = state;
3280  m_strings.insert(name, textprop);
3281  }
3282  else
3283  m_text = text;
3284 
3285  if (m_parent && m_isVisible)
3286  m_parent->Update();
3287 }
3288 
3290  const QString &state)
3291 {
3292  InfoMap::const_iterator map_it = infoMap.begin();
3293 
3294  while (map_it != infoMap.end())
3295  {
3296  TextProperties textprop;
3297  textprop.text = (*map_it);
3298  textprop.state = state;
3299  m_strings[map_it.key()] = textprop;
3300  ++map_it;
3301  }
3302 
3303  if (m_parent && m_isVisible)
3304  m_parent->Update();
3305 }
3306 
3307 void MythUIButtonListItem::SetTextFromMap(const QMap<QString, TextProperties> &stringMap)
3308 {
3309  m_strings.clear();
3310  m_strings = stringMap;
3311 }
3312 
3313 QString MythUIButtonListItem::GetText(const QString &name) const
3314 {
3315  if (name.isEmpty())
3316  return m_text;
3317  if (m_strings.contains(name))
3318  return m_strings[name].text;
3319  return QString();
3320 }
3321 
3322 bool MythUIButtonListItem::FindText(const QString &searchStr, const QString &fieldList,
3323  bool startsWith) const
3324 {
3325  if (fieldList.isEmpty())
3326  {
3327  if (startsWith)
3328  return m_text.startsWith(searchStr, Qt::CaseInsensitive);
3329  return m_text.contains(searchStr, Qt::CaseInsensitive);
3330  }
3331  if (fieldList == "**ALL**")
3332  {
3333  if (startsWith)
3334  {
3335  if (m_text.startsWith(searchStr, Qt::CaseInsensitive))
3336  return true;
3337  }
3338  else
3339  {
3340  if (m_text.contains(searchStr, Qt::CaseInsensitive))
3341  return true;
3342  }
3343 
3344  QMap<QString, TextProperties>::const_iterator i = m_strings.constBegin();
3345 
3346  while (i != m_strings.constEnd())
3347  {
3348  if (startsWith)
3349  {
3350  if (i.value().text.startsWith(searchStr, Qt::CaseInsensitive))
3351  return true;
3352  }
3353  else
3354  {
3355  if (i.value().text.contains(searchStr, Qt::CaseInsensitive))
3356  return true;
3357  }
3358 
3359  ++i;
3360  }
3361  }
3362  else
3363  {
3364 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
3365  QStringList fields = fieldList.split(',', QString::SkipEmptyParts);
3366 #else
3367  QStringList fields = fieldList.split(',', Qt::SkipEmptyParts);
3368 #endif
3369 
3370  for (int x = 0; x < fields.count(); ++x)
3371  {
3372  if (m_strings.contains(fields.at(x).trimmed()))
3373  {
3374  if (startsWith)
3375  {
3376  if (m_strings[fields.at(x)].text.startsWith(searchStr, Qt::CaseInsensitive))
3377  return true;
3378  }
3379  else
3380  {
3381  if (m_strings[fields.at(x)].text.contains(searchStr, Qt::CaseInsensitive))
3382  return true;
3383  }
3384  }
3385  }
3386  }
3387 
3388  return false;
3389 }
3390 
3391 void MythUIButtonListItem::SetFontState(const QString &state,
3392  const QString &name)
3393 {
3394  if (!name.isEmpty())
3395  {
3396  if (m_strings.contains(name))
3397  m_strings[name].state = state;
3398  }
3399  else
3400  m_fontState = state;
3401 
3402  if (m_parent && m_isVisible)
3403  m_parent->Update();
3404 }
3405 
3406 void MythUIButtonListItem::SetImage(MythImage *image, const QString &name)
3407 {
3408  if (image)
3409  image->IncrRef();
3410 
3411  if (!name.isEmpty())
3412  {
3413  QMap<QString, MythImage*>::iterator it = m_images.find(name);
3414  if (it != m_images.end())
3415  {
3416  (*it)->DecrRef();
3417  if (image)
3418  *it = image;
3419  else
3420  m_images.erase(it);
3421  }
3422  else if (image)
3423  {
3424  m_images[name] = image;
3425  }
3426  }
3427  else
3428  {
3429  if (m_image)
3430  m_image->DecrRef();
3431  m_image = image;
3432  }
3433 
3434  if (m_parent && m_isVisible)
3435  m_parent->Update();
3436 }
3437 
3439 {
3440  m_imageFilenames.clear();
3441  m_imageFilenames = imageMap;
3442 }
3443 
3445 {
3446  if (!name.isEmpty())
3447  {
3448  QMap<QString, MythImage*>::iterator it = m_images.find(name);
3449  if (it != m_images.end())
3450  {
3451  (*it)->IncrRef();
3452  return (*it);
3453  }
3454  }
3455  else if (m_image)
3456  {
3457  m_image->IncrRef();
3458  return m_image;
3459  }
3460 
3461  return nullptr;
3462 }
3463 
3465  const QString &filename, const QString &name, bool force_reload)
3466 {
3467  bool do_update = force_reload;
3468 
3469  if (!name.isEmpty())
3470  {
3471  InfoMap::iterator it = m_imageFilenames.find(name);
3472 
3473  if (it == m_imageFilenames.end())
3474  {
3475  m_imageFilenames.insert(name, filename);
3476  do_update = true;
3477  }
3478  else if (*it != filename)
3479  {
3480  *it = filename;
3481  do_update = true;
3482  }
3483  }
3484  else if (m_imageFilename != filename)
3485  {
3487  do_update = true;
3488  }
3489 
3490  if (m_parent && do_update && m_isVisible)
3491  m_parent->Update();
3492 }
3493 
3494 QString MythUIButtonListItem::GetImageFilename(const QString &name) const
3495 {
3496  if (name.isEmpty())
3497  return m_imageFilename;
3498 
3499  InfoMap::const_iterator it = m_imageFilenames.find(name);
3500 
3501  if (it != m_imageFilenames.end())
3502  return *it;
3503 
3504  return QString();
3505 }
3506 
3507 void MythUIButtonListItem::DisplayState(const QString &state,
3508  const QString &name)
3509 {
3510  if (name.isEmpty())
3511  return;
3512 
3513  bool do_update = false;
3514  InfoMap::iterator it = m_states.find(name);
3515 
3516  if (it == m_states.end())
3517  {
3518  m_states.insert(name, state);
3519  do_update = true;
3520  }
3521  else if (*it != state)
3522  {
3523  *it = state;
3524  do_update = true;
3525  }
3526 
3527  if (m_parent && do_update && m_isVisible)
3528  m_parent->Update();
3529 }
3530 
3532 {
3533  m_states.clear();
3534  m_states = stateMap;
3535 }
3536 
3538 {
3539  return m_checkable;
3540 }
3541 
3543 {
3544  return m_state;
3545 }
3546 
3548 {
3549  return m_parent;
3550 }
3551 
3553 {
3554  if (!m_checkable || m_state == state)
3555  return;
3556 
3557  m_state = state;
3558 
3559  if (m_parent && m_isVisible)
3560  m_parent->Update();
3561 }
3562 
3564 {
3565  m_checkable = flag;
3566 }
3567 
3569 {
3570  m_showArrow = flag;
3571 }
3572 
3574 {
3575  return m_enabled;
3576 }
3577 
3579 {
3580  m_enabled = flag;
3581 }
3582 
3584 {
3585  m_data = std::move(data);
3586 }
3587 
3589 {
3590  return m_data;
3591 }
3592 
3594 {
3595  if (m_parent)
3596  return m_parent->MoveItemUpDown(this, flag);
3597  return false;
3598 }
3599 
3601 {
3602  if (!m_parent)
3603  return;
3604 
3605  m_parent->ItemVisible(this);
3606  m_isVisible = true;
3607 
3608  QString state;
3609 
3610  if (!m_parent->IsEnabled())
3611  state = "disabled";
3612  else if (!m_enabled)
3613  {
3614  state = m_parent->m_active ? "disabledactive" : "disabledinactive";
3615  }
3616  else if (selected)
3617  {
3618  button->MoveToTop();
3619  state = m_parent->m_active ? "selectedactive" : "selectedinactive";
3620  }
3621  else
3622  state = m_parent->m_active ? "active" : "inactive";
3623 
3624  // Begin compatibility code
3625  // Attempt to fallback if the theme is missing certain states
3626  if (state == "disabled" && !button->GetState(state))
3627  {
3628  LOG(VB_GUI, LOG_WARNING, "Theme Error: Missing buttonlist state: disabled");
3629  state = "inactive";
3630  }
3631 
3632  if (state == "inactive" && !button->GetState(state))
3633  {
3634  LOG(VB_GUI, LOG_WARNING, "Theme Error: Missing buttonlist state: inactive");
3635  state = "active";
3636  }
3637  // End compatibility code
3638 
3639  auto *buttonstate = dynamic_cast<MythUIGroup *>(button->GetState(state));
3640  if (!buttonstate)
3641  {
3642  LOG(VB_GENERAL, LOG_CRIT, QString("Theme Error: Missing buttonlist state: %1")
3643  .arg(state));
3644  return;
3645  }
3646 
3647  buttonstate->Reset();
3648 
3649  MythUIText *buttontext = dynamic_cast<MythUIText *>
3650  (buttonstate->GetChild("buttontext"));
3651 
3652  if (buttontext)
3653  {
3654  buttontext->SetText(m_text);
3655  buttontext->SetFontState(m_fontState);
3656  }
3657 
3658  MythUIImage *buttonimage = dynamic_cast<MythUIImage *>
3659  (buttonstate->GetChild("buttonimage"));
3660 
3661  if (buttonimage)
3662  {
3663  if (!m_imageFilename.isEmpty())
3664  {
3665  buttonimage->SetFilename(m_imageFilename);
3666  buttonimage->Load();
3667  }
3668  else if (m_image)
3669  buttonimage->SetImage(m_image);
3670  }
3671 
3672  MythUIImage *buttonarrow = dynamic_cast<MythUIImage *>
3673  (buttonstate->GetChild("buttonarrow"));
3674 
3675  if (buttonarrow)
3676  buttonarrow->SetVisible(m_showArrow);
3677 
3678  MythUIStateType *buttoncheck = dynamic_cast<MythUIStateType *>
3679  (buttonstate->GetChild("buttoncheck"));
3680 
3681  if (buttoncheck)
3682  {
3683  buttoncheck->SetVisible(m_checkable);
3684 
3685  if (m_checkable)
3686  {
3687  if (m_state == NotChecked)
3688  buttoncheck->DisplayState(MythUIStateType::Off);
3689  else if (m_state == HalfChecked)
3690  buttoncheck->DisplayState(MythUIStateType::Half);
3691  else
3692  buttoncheck->DisplayState(MythUIStateType::Full);
3693  }
3694  }
3695 
3696  QMap<QString, TextProperties>::iterator string_it = m_strings.begin();
3697 
3698  while (string_it != m_strings.end())
3699  {
3700  auto *text = dynamic_cast<MythUIText *>
3701  (buttonstate->GetChild(string_it.key()));
3702 
3703  if (text)
3704  {
3705  TextProperties textprop = string_it.value();
3706 
3707  QString newText = text->GetTemplateText();
3708 
3709  QRegularExpression re {R"(%(([^\|%]+)?\||\|(.))?([\w#]+)(\|(.+?))?%)",
3710  QRegularExpression::DotMatchesEverythingOption};
3711 
3712  if (!newText.isEmpty() && newText.contains(re))
3713  {
3714  QString tempString = newText;
3715 
3716  QRegularExpressionMatchIterator i = re.globalMatch(newText);
3717  while (i.hasNext()) {
3718  QRegularExpressionMatch match = i.next();
3719  QString key = match.captured(4).toLower().trimmed();
3720  QString replacement;
3721  QString value = m_strings.value(key).text;
3722 
3723  if (!value.isEmpty())
3724  {
3725  replacement = QString("%1%2%3%4")
3726  .arg(match.captured(2))
3727  .arg(match.captured(3))
3728  .arg(m_strings.value(key).text)
3729  .arg(match.captured(6));
3730  }
3731 
3732  tempString.replace(match.captured(0), replacement);
3733  }
3734 
3735  newText = tempString;
3736  }
3737  else
3738  newText = textprop.text;
3739 
3740  if (newText.isEmpty())
3741  text->Reset();
3742  else
3743  text->SetText(newText);
3744 
3745  text->SetFontState(textprop.state.isEmpty() ? m_fontState : textprop.state);
3746  }
3747 
3748  ++string_it;
3749  }
3750 
3751  InfoMap::iterator imagefile_it = m_imageFilenames.begin();
3752 
3753  while (imagefile_it != m_imageFilenames.end())
3754  {
3755  auto *image = dynamic_cast<MythUIImage *>
3756  (buttonstate->GetChild(imagefile_it.key()));
3757  if (image)
3758  {
3759  if (!imagefile_it.value().isEmpty())
3760  {
3761  image->SetFilename(imagefile_it.value());
3762  image->Load();
3763  }
3764  else
3765  image->Reset();
3766  }
3767 
3768  ++imagefile_it;
3769  }
3770 
3771  QMap<QString, MythImage *>::iterator image_it = m_images.begin();
3772 
3773  while (image_it != m_images.end())
3774  {
3775  auto *image = dynamic_cast<MythUIImage *>
3776  (buttonstate->GetChild(image_it.key()));
3777  if (image)
3778  {
3779  if (image_it.value())
3780  image->SetImage(image_it.value());
3781  else
3782  image->Reset();
3783  }
3784 
3785  ++image_it;
3786  }
3787 
3788  InfoMap::iterator state_it = m_states.begin();
3789 
3790  while (state_it != m_states.end())
3791  {
3792  auto *statetype = dynamic_cast<MythUIStateType *>
3793  (buttonstate->GetChild(state_it.key()));
3794  if (statetype)
3795  {
3796  if (!statetype->DisplayState(state_it.value()))
3797  statetype->Reset();
3798  }
3799 
3800  ++state_it;
3801  }
3802 
3803  // There is no need to check the return value here, since we already
3804  // checked that the state exists with GetState() earlier
3805  button->DisplayState(state);
3806 }
3807 
3808 //---------------------------------------------------------
3809 // SearchButtonListDialog
3810 //---------------------------------------------------------
3812 {
3813  if (!CopyWindowFromBase("MythSearchListDialog", this))
3814  return false;
3815 
3816  bool err = false;
3817  UIUtilE::Assign(this, m_searchEdit, "searchedit", &err);
3818  UIUtilE::Assign(this, m_prevButton, "prevbutton", &err);
3819  UIUtilE::Assign(this, m_nextButton, "nextbutton", &err);
3820  UIUtilW::Assign(this, m_searchState, "searchstate");
3821 
3822  if (err)
3823  {
3824  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'MythSearchListDialog'");
3825  return false;
3826  }
3827 
3829 
3830  connect(m_searchEdit, SIGNAL(valueChanged()), SLOT(searchChanged()));
3831  connect(m_prevButton, SIGNAL(Clicked()), SLOT(prevClicked()));
3832  connect(m_nextButton, SIGNAL(Clicked()), SLOT(nextClicked()));
3833 
3834  BuildFocusList();
3835 
3836  return true;
3837 }
3838 
3840 {
3841  if (GetFocusWidget() && GetFocusWidget()->keyPressEvent(event))
3842  return true;
3843 
3844  QStringList actions;
3845  bool handled = GetMythMainWindow()->TranslateKeyPress("Global", event, actions, false);
3846 
3847  for (int i = 0; i < actions.size() && !handled; ++i)
3848  {
3849  QString action = actions[i];
3850  handled = true;
3851 
3852  if (action == "0")
3853  {
3855  searchChanged();
3856  }
3857  else
3858  handled = false;
3859  }
3860 
3861  if (!handled && MythScreenType::keyPressEvent(event))
3862  handled = true;
3863 
3864  return handled;
3865 }
3866 
3868 {
3869  bool found = m_parentList->Find(m_searchEdit->GetText(), m_startsWith);
3870 
3871  if (m_searchState)
3872  m_searchState->DisplayState(found ? "found" : "notfound");
3873 }
3874 
3876 {
3877  bool found = m_parentList->FindNext();
3878 
3879  if (m_searchState)
3880  m_searchState->DisplayState(found ? "found" : "notfound");
3881 }
3882 
3884 {
3885  bool found = m_parentList->FindPrev();
3886 
3887  if (m_searchState)
3888  m_searchState->DisplayState(found ? "found" : "notfound");
3889 }
3890 
3892 {
3894  return;
3895 
3896  int maximum = (m_itemCount <= m_itemsVisible) ? 0 : m_itemCount;
3897  m_scrollBar->SetMaximum(maximum);
3901 }
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:251
MythUIButtonList::StopLoad
int StopLoad(void)
Definition: mythuibuttonlist.cpp:2767
MythUIButtonList::ScrollCenter
@ ScrollCenter
Definition: mythuibuttonlist.h:227
MythUIButtonList::m_upArrow
MythUIStateType * m_upArrow
Definition: mythuibuttonlist.h:321
MythUIButtonList::SetDrawFromBottom
void SetDrawFromBottom(bool draw)
Definition: mythuibuttonlist.cpp:95
MythUIButtonList::m_topPosition
int m_topPosition
Definition: mythuibuttonlist.h:335
MythUIButtonList::GetItemCurrent
MythUIButtonListItem * GetItemCurrent() const
Definition: mythuibuttonlist.cpp:1590
MythUIButtonList::m_itemList
QList< MythUIButtonListItem * > m_itemList
Definition: mythuibuttonlist.h:339
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:2734
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:3875
MythRect::CalculateArea
void CalculateArea(const QRect &parentArea)
Definition: mythrect.cpp:64
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:228
MythGestureEvent::UpRight
@ UpRight
Definition: mythgesture.h:56
MythUIButtonList::GetItemNext
MythUIButtonListItem * GetItemNext(MythUIButtonListItem *item) const
Definition: mythuibuttonlist.cpp:1644
MythUIButtonList::m_scrollStyle
ScrollStyle m_scrollStyle
Definition: mythuibuttonlist.h:292
MythUIButtonList::DrawSelf
void DrawSelf(MythPainter *p, int xoffset, int yoffset, int alphaMod, QRect clipRect) override
Definition: mythuibuttonlist.cpp:2955
MythUIButtonList::GetButtonPosition
virtual QPoint GetButtonPosition(int column, int row) const
Definition: mythuibuttonlist.cpp:2774
MythUIButtonList::m_itemHeight
int m_itemHeight
Definition: mythuibuttonlist.h:304
MythUIButtonList::Find
bool Find(const QString &searchStr, bool startsWith=false)
Definition: mythuibuttonlist.cpp:3134
MythUIButtonListItem::DisplayState
void DisplayState(const QString &state, const QString &name)
Definition: mythuibuttonlist.cpp:3507
MythUIButtonListItem::m_strings
QMap< QString, TextProperties > m_strings
Definition: mythuibuttonlist.h:125
NextButtonListPageEvent::NextButtonListPageEvent
NextButtonListPageEvent(int start, int pageSize)
Definition: mythuibuttonlist.cpp:2730
MythGestureEvent::Up
@ Up
Definition: mythgesture.h:49
NextButtonListPageEvent::m_pageSize
const int m_pageSize
Definition: mythuibuttonlist.cpp:2733
MythUIType::GetFullArea
virtual MythRect GetFullArea(void) const
Definition: mythuitype.cpp:878
MythUIButtonListItem::SetImageFromMap
void SetImageFromMap(const InfoMap &imageMap)
Definition: mythuibuttonlist.cpp:3438
MythUIType::Enabling
void Enabling()
MythUIStateType::GetState
MythUIType * GetState(const QString &name)
Definition: mythuistatetype.cpp:147
MythUIButtonList::m_itemCount
int m_itemCount
Definition: mythuibuttonlist.h:336
MythUIButtonList::MoveDown
virtual bool MoveDown(MovementUnit unit=MoveItem, uint amount=0)
Definition: mythuibuttonlist.cpp:2163
MythUIButtonList::m_needsUpdate
bool m_needsUpdate
Definition: mythuibuttonlist.h:331
title
QString title
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:639
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:1106
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:317
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:290
MythUIButtonList::m_buttontemplate
MythUIStateType * m_buttontemplate
Definition: mythuibuttonlist.h:324
MythUIButtonList::itemSelected
void itemSelected(MythUIButtonListItem *item)
MythUIButtonList::MoveRow
@ MoveRow
Definition: mythuibuttonlist.h:157
MythUIButtonList::Finalize
void Finalize(void) override
Perform any post-xml parsing initialisation tasks.
Definition: mythuibuttonlist.cpp:3033
MythUIButtonListItem::m_imageFilenames
InfoMap m_imageFilenames
Definition: mythuibuttonlist.h:127
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:228
MythUIButtonListItem::SetStatesFromMap
void SetStatesFromMap(const InfoMap &stateMap)
Definition: mythuibuttonlist.cpp:3531
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:115
mythuiscrollbar.h
MythUIButtonListItem::parent
MythUIButtonList * parent() const
Definition: mythuibuttonlist.cpp:3547
MythUIButtonListItem::FindText
bool FindText(const QString &searchStr, const QString &fieldList="**ALL**", bool startsWith=false) const
Definition: mythuibuttonlist.cpp:3322
MythUIButtonList::minButtonWidth
int minButtonWidth(const MythRect &area)
Definition: mythuibuttonlist.cpp:152
MythUIButtonList::SetButtonArea
void SetButtonArea(const MythRect &rect)
Definition: mythuibuttonlist.cpp:2821
SearchButtonListDialog::m_searchText
QString m_searchText
Definition: mythuibuttonlist.h:373
MythUIGroup
Create a group of widgets.
Definition: mythuigroup.h:12
MythUIButtonList::WrapNone
@ WrapNone
Definition: mythuibuttonlist.h:229
NextButtonListPageEvent::m_start
const int m_start
Definition: mythuibuttonlist.cpp:2732
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
mythuistatetype.h
MythUIButtonList::m_showScrollBar
bool m_showScrollBar
Definition: mythuibuttonlist.h:318
MythUIButtonList::m_alignment
int m_alignment
Definition: mythuibuttonlist.h:294
MythUIButtonListItem::m_imageFilename
QString m_imageFilename
Definition: mythuibuttonlist.h:117
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:230
UNCHECKED
@ UNCHECKED
Definition: lcddevice.h:17
MythUIButtonList::DoFind
bool DoFind(bool doMove, bool searchForward)
Definition: mythuibuttonlist.cpp:3151
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:2839
SearchButtonListDialog
Definition: mythuibuttonlist.h:352
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:340
SearchButtonListDialog::m_startsWith
bool m_startsWith
Definition: mythuibuttonlist.h:370
MythUIButtonListItem::~MythUIButtonListItem
virtual ~MythUIButtonListItem()
Definition: mythuibuttonlist.cpp:3255
MythMainWindow::GetStack
MythScreenStack * GetStack(const QString &stackname)
Definition: mythmainwindow.cpp:309
mythuibuttonlist.h
MythUIButtonList::SanitizePosition
void SanitizePosition(void)
Definition: mythuibuttonlist.cpp:1385
LCD::Get
static LCD * Get(void)
Definition: lcddevice.cpp:64
mythuiimage.h
MythUIButtonList::GetCount
int GetCount() const
Definition: mythuibuttonlist.cpp:1655
MythUIButtonList::m_buttonToItem
QMap< int, MythUIButtonListItem * > m_buttonToItem
Definition: mythuibuttonlist.h:327
MythUIButtonList::m_bottomRows
int m_bottomRows
Definition: mythuibuttonlist.h:314
MythUIButtonList::m_drawFromBottom
bool m_drawFromBottom
Definition: mythuibuttonlist.h:342
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:2740
SearchButtonListDialog::m_parentList
MythUIButtonList * m_parentList
Definition: mythuibuttonlist.h:372
InfoMap
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
MythUIButtonList::ItemHeight
uint ItemHeight(void)
Definition: mythuibuttonlist.cpp:2479
MythUIButtonList::MovePage
@ MovePage
Definition: mythuibuttonlist.h:157
MythUIButtonListItem::SetText
void SetText(const QString &text, const QString &name="", const QString &state="")
Definition: mythuibuttonlist.cpp:3272
MythUIButtonList::m_scrollBar
MythUIScrollBar * m_scrollBar
Definition: mythuibuttonlist.h:320
MythUIButtonList::GetItemPos
int GetItemPos(MythUIButtonListItem *item) const
Definition: mythuibuttonlist.cpp:1698
MythUIButtonList::m_itemVertSpacing
int m_itemVertSpacing
Definition: mythuibuttonlist.h:306
MythUIButtonList::ItemVisible
void ItemVisible(MythUIButtonListItem *item)
Definition: mythuibuttonlist.cpp:1453
MythUIButtonListItem
Definition: mythuibuttonlist.h:28
MythUIButtonList::ScrollGroupCenter
@ ScrollGroupCenter
Definition: mythuibuttonlist.h:227
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:296
MythUIButtonListItem::CantCheck
@ CantCheck
Definition: mythuibuttonlist.h:31
MythUIType::m_hasFocus
bool m_hasFocus
Definition: mythuitype.h:239
MythUIButtonList::itemLoaded
void itemLoaded(MythUIButtonListItem *item)
mythlogging.h
MythUIButtonList::m_lcdColumns
QStringList m_lcdColumns
Definition: mythuibuttonlist.h:345
MythUIButtonList::m_rows
int m_rows
Definition: mythuibuttonlist.h:309
MythUIButtonListItem::setCheckable
void setCheckable(bool flag)
Definition: mythuibuttonlist.cpp:3563
MythUIButtonList::itemClicked
void itemClicked(MythUIButtonListItem *item)
MythUIButtonList::MoveMax
@ MoveMax
Definition: mythuibuttonlist.h:157
MythUIButtonList::Deselect
void Deselect()
Definition: mythuibuttonlist.cpp:84
MythUIType::GetXMLLocation
QString GetXMLLocation(void) const
Definition: mythuitype.h:158
MythUIButtonListItem::isEnabled
bool isEnabled() const
Definition: mythuibuttonlist.cpp:3573
MythUIButtonList::GetCurrentPos
int GetCurrentPos() const
Definition: mythuibuttonlist.h:190
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:128
MythUIType::IsEnabled
bool IsEnabled(void) const
Definition: mythuitype.h:96
MythUIType::Disabling
void Disabling()
MythUIButtonList::m_itemsVisible
int m_itemsVisible
Definition: mythuibuttonlist.h:307
MythUIButtonList::WrapSelect
@ WrapSelect
Definition: mythuibuttonlist.h:229
MythUIButtonList::m_searchPosition
MythPoint m_searchPosition
Definition: mythuibuttonlist.h:298
MythUIButtonList::keyPressEvent
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: mythuibuttonlist.cpp:2490
MythGestureEvent::gesture
Gesture gesture(void) const
Get the gesture type.
Definition: mythgesture.h:109
MythUIButtonList::LoadInBackground
void LoadInBackground(int start=0, int pageSize=20)
Definition: mythuibuttonlist.cpp:2760
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:3444
MythUIButtonList::MoveByAmount
@ MoveByAmount
Definition: mythuibuttonlist.h:158
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:305
XMLParseBase::getFirstText
static QString getFirstText(QDomElement &element)
Definition: xmlparsebase.cpp:53
MythUIButtonList::m_topRows
int m_topRows
Definition: mythuibuttonlist.h:313
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:271
TextProperties
Definition: mythuibuttonlist.h:22
MythUIType::NormY
static int NormY(int height)
Definition: mythuitype.cpp:1143
MythGestureEvent::Right
@ Right
Definition: mythgesture.h:52
MythUIButtonList::ArrangeFill
@ ArrangeFill
Definition: mythuibuttonlist.h:228
MythUIButtonList::MythUIButtonList
MythUIButtonList(MythUIType *parent, const QString &name)
Definition: mythuibuttonlist.cpp:29
filename
QString filename
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:640
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:332
SearchButtonListDialog::searchChanged
void searchChanged(void)
Definition: mythuibuttonlist.cpp:3867
MythGestureEvent::UpLeft
@ UpLeft
Definition: mythgesture.h:55
SearchButtonListDialog::m_prevButton
MythUIButton * m_prevButton
Definition: mythuibuttonlist.h:376
MythUIButtonListItem::checkable
bool checkable() const
Definition: mythuibuttonlist.cpp:3537
MythUIButtonList::m_initialized
bool m_initialized
Definition: mythuibuttonlist.h:330
MythGestureEvent::Click
@ Click
Definition: mythgesture.h:75
MythUIButtonList::WrapItems
@ WrapItems
Definition: mythuibuttonlist.h:229
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:1151
MythUIButtonList::ScrollFree
@ ScrollFree
Definition: mythuibuttonlist.h:227
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:3038
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:300
SearchButtonListDialog::Create
bool Create(void) override
Definition: mythuibuttonlist.cpp:3811
MythUIStateType::Full
@ Full
Definition: mythuistatetype.h:25
MythUIButtonList::SetScrollBarPosition
void SetScrollBarPosition(void)
Definition: mythuibuttonlist.cpp:3891
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:228
MythUIButtonList::Init
virtual void Init()
Definition: mythuibuttonlist.cpp:2367
MythUIButtonListItem::GetData
QVariant GetData()
Definition: mythuibuttonlist.cpp:3588
uint
unsigned int uint
Definition: compat.h:140
MythUIScrollBar
Scroll bar widget.
Definition: mythuiscrollbar.h:16
MythUIButtonList::FindNext
bool FindNext(void)
Definition: mythuibuttonlist.cpp:3141
MythUIButtonList::GetVisibleCount
int GetVisibleCount()
Definition: mythuibuttonlist.cpp:1660
MythUIButtonList::m_maxVisible
int m_maxVisible
Definition: mythuibuttonlist.h:308
MythUIButtonList::m_searchStartsWith
bool m_searchStartsWith
Definition: mythuibuttonlist.h:301
MythUIType::MoveToTop
bool MoveToTop(void)
Definition: mythuitype.cpp:1354
MythUIButtonList::MovementUnit
MovementUnit
Definition: mythuibuttonlist.h:157
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:3600
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:157
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:66
NextButtonListPageEvent
Definition: mythuibuttonlist.cpp:2728
MythUIButtonListItem::SetTextFromMap
void SetTextFromMap(const InfoMap &infoMap, const QString &state="")
Definition: mythuibuttonlist.cpp:3289
LCDMenuItem
Definition: lcddevice.h:22
MythUIButtonListItem::m_images
QMap< QString, MythImage * > m_images
Definition: mythuibuttonlist.h:126
MythUIButtonListItem::GetText
QString GetText(const QString &name="") const
Definition: mythuibuttonlist.cpp:3313
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:375
MythUIStateType::Off
@ Off
Definition: mythuistatetype.h:25
MythUIButtonListItem::SetFontState
void SetFontState(const QString &state, const QString &name="")
Definition: mythuibuttonlist.cpp:3391
MythUIButtonList::PageUp
int PageUp(void)
Definition: mythuibuttonlist.cpp:1734
MythUIButtonList::m_actionRemap
QHash< QString, QString > m_actionRemap
Definition: mythuibuttonlist.h:328
MythUIType::m_initiator
bool m_initiator
Definition: mythuitype.h:243
MythUIButtonListItem::m_state
CheckState m_state
Definition: mythuibuttonlist.h:119
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:114
MythUIButtonList::m_lcdTitle
QString m_lcdTitle
Definition: mythuibuttonlist.h:344
MythPoint::isValid
bool isValid(void) const
Definition: mythrect.h:102
MythUIButtonList::LayoutVertical
@ LayoutVertical
Definition: mythuibuttonlist.h:159
MythRect::topLeft
MythPoint topLeft(void) const
Definition: mythrect.cpp:288
MythUIButtonListItem::m_parent
MythUIButtonList * m_parent
Definition: mythuibuttonlist.h:113
MythRect::setWidth
void setWidth(const QString &sWidth)
Definition: mythrect.cpp:266
MythUIButtonListItem::m_checkable
bool m_checkable
Definition: mythuibuttonlist.h:118
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:3406
MythUIButtonList::CalculateVisibleItems
virtual void CalculateVisibleItems(void)
Definition: mythuibuttonlist.cpp:2784
MythPainter
Definition: mythpainter.h:33
MythImage
Definition: mythimage.h:37
MythUIButtonList::m_columns
int m_columns
Definition: mythuibuttonlist.h:310
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:334
MythUIButtonListItem::setEnabled
void setEnabled(bool flag)
Definition: mythuibuttonlist.cpp:3578
MythUIButtonListItem::m_data
QVariant m_data
Definition: mythuibuttonlist.h:120
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:253
MythUIType::GetChildAt
MythUIType * GetChildAt(const QPoint &p, bool recursive=true, bool focusable=true) const
Return the first MythUIType at the given coordinates.
Definition: mythuitype.cpp:222
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:3542
MythUIType::SetVisible
virtual void SetVisible(bool visible)
Definition: mythuitype.cpp:1086
SearchButtonListDialog::keyPressEvent
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: mythuibuttonlist.cpp:3839
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:107
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:291
MythUIButtonList::m_leftColumns
int m_leftColumns
Definition: mythuibuttonlist.h:311
MythUIButtonList::MoveMid
@ MoveMid
Definition: mythuibuttonlist.h:158
MythUIButtonListItem::setDrawArrow
void setDrawArrow(bool flag)
Definition: mythuibuttonlist.cpp:3568
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:312
mythuibutton.h
MythUIButtonList::WrapCaptive
@ WrapCaptive
Definition: mythuibuttonlist.h:229
MythUIButtonList::m_active
bool m_active
Definition: mythuibuttonlist.h:316
SearchButtonListDialog::prevClicked
void prevClicked(void)
Definition: mythuibuttonlist.cpp:3883
MythUIButtonList::PrepareButton
MythUIGroup * PrepareButton(int buttonIdx, int itemIdx, int &selectedIdx, int &button_shift)
Definition: mythuibuttonlist.cpp:205
MythUIButtonList::m_searchFields
QString m_searchFields
Definition: mythuibuttonlist.h:299
MythGestureEvent::DownLeft
@ DownLeft
Definition: mythgesture.h:57
MythUIButtonListItem::m_isVisible
bool m_isVisible
Definition: mythuibuttonlist.h:122
MythUIButtonList::m_itemWidth
int m_itemWidth
Definition: mythuibuttonlist.h:303
SearchButtonListDialog::m_searchState
MythUIStateType * m_searchState
Definition: mythuibuttonlist.h:378
MythUIButtonList::FindPrev
bool FindPrev(void)
Definition: mythuibuttonlist.cpp:3146
MythGestureEvent
A custom event that represents a mouse gesture.
Definition: mythgesture.h:40
MythUIButtonListItem::GetImageFilename
QString GetImageFilename(const QString &name="") const
Definition: mythuibuttonlist.cpp:3494
MythUIButtonList::LayoutGrid
@ LayoutGrid
Definition: mythuibuttonlist.h:159
MythUIButtonListItem::m_image
MythImage * m_image
Definition: mythuibuttonlist.h:116
CHECKED
@ CHECKED
Definition: lcddevice.h:17
MythUIButtonList::m_buttonList
QVector< MythUIStateType * > m_buttonList
Definition: mythuibuttonlist.h:326
MythUIButtonList::MoveItem
@ MoveItem
Definition: mythuibuttonlist.h:157
MythUIButtonList::ItemWidth
uint ItemWidth(void)
Definition: mythuibuttonlist.cpp:2471
MythUIType::NormX
static int NormX(int width)
Definition: mythuitype.cpp:1138
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:121
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:2968
MythUIButtonList::m_keepSelAtBottom
bool m_keepSelAtBottom
Definition: mythuibuttonlist.h:337
MythUIButtonListItem::MythUIButtonListItem
MythUIButtonListItem(MythUIButtonList *lbtype, QString text, QString image="", bool checkable=false, CheckState state=CantCheck, bool showArrow=false, int listPosition=-1)
Definition: mythuibuttonlist.cpp:3215
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:3104
MythUIButtonList::LayoutHorizontal
@ LayoutHorizontal
Definition: mythuibuttonlist.h:159
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:1217
MythUIButtonList::m_downArrow
MythUIStateType * m_downArrow
Definition: mythuibuttonlist.h:322
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:144
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:377
MythUIButtonListItem::m_enabled
bool m_enabled
Definition: mythuibuttonlist.h:123
MythUIButtonListItem::setChecked
void setChecked(CheckState state)
Definition: mythuibuttonlist.cpp:3552
MythUIButtonListItem::SetData
void SetData(QVariant data)
Definition: mythuibuttonlist.cpp:3583
MythScreenStack::AddScreen
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
Definition: mythscreenstack.cpp:49
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:3593
MythMainWindow::GetUIScreenRect
QRect GetUIScreenRect()
Definition: mythmainwindow.cpp:2252
MythUIButtonList::updateLCD
void updateLCD(void)
Definition: mythuibuttonlist.cpp:3044
MythUIType::m_enableInitiator
bool m_enableInitiator
Definition: mythuitype.h:242
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:585
MythUIType::Finalize
virtual void Finalize(void)
Perform any post-xml parsing initialisation tasks.
Definition: mythuitype.cpp:1293
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:1303
MythUIButtonList::m_wrapStyle
WrapStyle m_wrapStyle
Definition: mythuibuttonlist.h:293
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:2977