7 #include <QCoreApplication>
8 #include <QDomDocument>
10 #include <QRegularExpression>
27 #define LOC QString("MythUIButtonList(%1): ").arg(objectName())
40 const QRect area,
bool showArrow,
43 m_showArrow(showArrow), m_showScrollBar(showScrollBar)
154 int width = area.width();
162 width += (area.x() * 2 - 1);
182 int height = area.height();
190 height += (area.y() * 2 - 1);
211 buttonIdx += button_shift;
215 QString name = QString(
"buttonlist button %1").arg(
m_maxVisible);
218 button->ConnectDependants(
true);
230 if (selectedIdx >= 0)
246 selectedIdx = buttonIdx;
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)
268 bool underflow =
false;
270 int selectedIdx = -1;
271 int button_shift = 0;
275 if (last_item + 1 >
m_itemCount || last_item < 0 || first_item < 0)
285 selectedIdx, button_shift);
290 selectedIdx, button_shift);
293 if (buttonstate ==
nullptr)
295 LOG(VB_GENERAL, LOG_ERR, QString(
"Failed to query buttonlist state: %1")
311 bool hsplit = vsplit && grow_left && grow_right;
316 left_width = right_width = (width / 2);
340 if (total_height > 0 &&
341 ((vsplit ? split_height : total_height) +
344 LOG(VB_GUI, LOG_DEBUG,
345 QString(
"%1 Height exceeded %2 + (%3) + %4 = %5 which is > %6")
346 .
arg(vsplit ?
"Centering" :
"Total")
350 first_button += button_shift;
351 last_button += button_shift;
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));
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;
400 if (last_item + 1 < end)
404 selectedIdx, button_shift);
406 if (buttonstate ==
nullptr)
412 if (*col_widths && width < (*col_widths)[col_idx])
413 width = (*col_widths)[col_idx];
416 if ((hsplit ? right_width : left_width + right_width) +
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")
437 if (row_height < height)
440 LOG(VB_GUI, LOG_DEBUG,
441 QString(
"Added button item %1 "
442 "R.width %2 height %3 total width %4+%5"
445 .
arg(left_width).
arg(right_width).
arg(max_width));
472 if (first_item > end)
474 buttonstate =
PrepareButton(first_button - 1, first_item - 1,
475 selectedIdx, button_shift);
477 if (buttonstate ==
nullptr)
483 if (*col_widths && width < (*col_widths)[col_idx])
484 width = (*col_widths)[col_idx];
487 if ((hsplit ? left_width : left_width + right_width) +
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")
509 if (row_height < height)
512 LOG(VB_GUI, LOG_DEBUG,
513 QString(
"Added button item %1 "
514 "L.width %2 height %3 total width %4+%5"
517 .
arg(left_width).
arg(right_width).
arg(max_width));
534 if (total_height > 0 &&
535 ((vsplit ? split_height : total_height) +
538 LOG(VB_GUI, LOG_DEBUG,
539 QString(
"%1 Height exceeded %2 + (%3) + %4 = %5 which is > %6")
540 .
arg(vsplit ?
"Centering" :
"Total")
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;
551 if (*col_widths ==
nullptr)
557 *col_widths =
new int[
static_cast<size_t>(col_cnt)];
559 for (col_idx = 0; col_idx < col_cnt; ++col_idx)
560 (*col_widths)[col_idx] = 0;
565 first_button += button_shift;
566 last_button += button_shift;
574 begin = first_button;
575 end = first_button + col_cnt;
579 end = last_button + 1;
580 begin = end - col_cnt;
583 for (buttonIdx = begin, col_idx = 0;
584 buttonIdx < end; ++buttonIdx, ++col_idx)
593 if ((*col_widths)[col_idx] < width)
594 (*col_widths)[col_idx] = width;
597 if (selectedIdx == buttonIdx)
598 selected_column = col_idx;
605 if (total_height && underflow && col_cnt <
m_columns)
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,
650 if (last_item + 1 < end)
654 first_item, ++last_item, selected_column,
655 skip_cols,
false,
true, col_widths, height,
656 top_height + bottom_height, bottom_height,
662 if (selected_row == -1 && selected_column != -1)
663 selected_row = row_heights.size();
666 row_heights.push_back(height);
695 if (first_item > end)
699 --first_item, last_item, selected_column,
700 skip_cols,
true,
false, col_widths, height,
701 top_height + bottom_height, top_height,
707 if (selected_row == -1 && selected_column != -1)
708 selected_row = row_heights.size();
709 else if (selected_row != -1)
713 row_heights.push_front(height);
734 int first_button = 0;
736 int start_button = 0;
741 int *col_widths =
nullptr;
743 int selected_column = -1;
744 int selected_row = -1;
745 bool wrapped =
false;
746 bool grow_left =
true;
749 int bottom_height = 0;
751 QList<int> row_heights;
753 LOG(VB_GUI, LOG_DEBUG, QString(
"DistributeButtons: "
754 "selected item %1 total items %2")
775 first_item = last_item = start_item;
793 first_item = last_item = 0;
820 first_button = last_button = start_button;
825 first_item, last_item, selected_column,
826 skip_cols, grow_left,
true, &col_widths,
827 height, 0, 0, col_cnt, wrapped))
842 first_item = last_item = start_item;
855 first_button = last_button = start_button;
858 selected_column = selected_row = -1;
861 first_item, last_item, selected_column,
862 skip_cols, grow_left,
true, &col_widths,
863 height, 0, 0, col_cnt, wrapped))
870 if (selected_column != -1)
873 row_heights.push_back(height);
876 top_height = bottom_height = (height / 2);
878 bottom_height = height;
885 first_item, last_item,
886 selected_column, selected_row,
887 skip_cols, &col_widths, row_heights,
888 top_height, bottom_height, wrapped))
892 col_widths =
nullptr;
898 m_rows = row_heights.size();
900 LOG(VB_GUI, LOG_DEBUG,
901 QString(
"%1 rows, %2 columns fit inside parent area %3x%4")
905 if (col_widths ==
nullptr)
909 int left_spacing = 0;
910 int right_spacing = 0;
912 int bottom_spacing = 0;
921 status_msg =
"Row heights: ";
923 for (
int row = 0; row <
m_rows; ++row)
928 if (row == selected_row)
931 top_height += (row_heights[row] / 2);
932 bottom_height += ((row_heights[row] / 2) + (row_heights[row] % 2));
948 status_msg += QString(
"%1").arg(row_heights[row]);
950 if (row == selected_row)
960 top_spacing = bottom_spacing = 0;
967 top_spacing = bottom_spacing =
983 if (!
m_topRows || top_spacing > bottom_spacing)
984 top_spacing = bottom_spacing;
986 bottom_spacing = top_spacing;
993 (top_height + bottom_height)) /
1013 y += qMax(bottom_height - top_height, 0);
1014 total = qMax(top_height, bottom_height) * 2;
1017 total = top_height + bottom_height;
1026 (top_height + bottom_height), 0);
1030 status_msg += QString(
" spacing top %1 bottom %2 fixed %3 offset %4")
1031 .arg(top_spacing).arg(bottom_spacing)
1034 LOG(VB_GUI, LOG_DEBUG, status_msg);
1040 int right_width = 0;
1043 status_msg =
"Col widths: ";
1045 for (
int col = 0; col <
m_columns; ++col)
1050 if (col == selected_column)
1053 left_width += (col_widths[col] / 2);
1054 right_width += ((col_widths[col] / 2) + (col_widths[col] % 2));
1070 status_msg += QString(
"%1").arg(col_widths[col]);
1072 if (col == selected_column)
1082 left_spacing = right_spacing = 0;
1089 left_spacing = right_spacing =
1106 left_spacing = right_spacing;
1108 right_spacing = left_spacing;
1115 (left_width + right_width)) /
1135 x_init += qMax(right_width - left_width, 0);
1136 total = qMax(left_width, right_width) * 2;
1139 total = left_width + right_width;
1148 (left_width + right_width), 0);
1150 min_rect.
setX(x_init);
1152 status_msg += QString(
" spacing left %1 right %2 fixed %3 offset %4")
1153 .arg(left_spacing).arg(right_spacing)
1155 LOG(VB_GUI, LOG_DEBUG, status_msg);
1163 int buttonIdx = first_button - skip_cols;
1168 int vertical_spacing = top_spacing;
1170 for (
int row = 0; row <
m_rows; ++row)
1173 int horizontal_spacing = left_spacing;
1175 for (
int col = 0; col <
m_columns && buttonIdx <= last_button; ++col)
1177 if (buttonIdx >= first_button)
1185 MythRect area = buttonstate->GetArea();
1211 if (col == selected_column)
1213 horizontal_spacing = right_spacing;
1214 if (row == selected_row)
1218 x += col_widths[col] + horizontal_spacing;
1222 if (row == selected_row)
1223 vertical_spacing = bottom_spacing;
1225 y += row_heights[row] + vertical_spacing;
1227 min_rect.
setWidth(x - min_rect.x());
1233 for (buttonIdx = 0; buttonIdx < first_button; ++buttonIdx)
1237 for (buttonIdx =
m_maxVisible - 1; buttonIdx > last_button; --buttonIdx)
1253 delete[] col_widths;
1309 QList<MythUIButtonListItem *>::iterator it =
m_itemList.begin() +
1333 for (
int i = 0; i < button; ++i)
1336 bool seenSelected =
false;
1351 if (!realButton || !buttonItem)
1354 bool selected =
false;
1358 seenSelected =
true;
1407 QMap<int, MythUIButtonListItem*>::const_iterator i =
m_buttonToItem.constBegin();
1411 i.value()->setVisible(
false);
1463 if (listPosition >= 0 && listPosition <=
m_itemList.count())
1498 QMap<int, MythUIButtonListItem*>::iterator it =
m_buttonToItem.begin();
1501 if (it.value() == item)
1549 if (item->GetData() == data)
1604 return item->
GetText().toInt();
1647 QListIterator<MythUIButtonListItem *> it(
m_itemList);
1649 if (!it.findNext(item))
1652 return it.previous();
1691 if (item->GetData() == data)
1713 QString name(
"buttonlist button 0");
1716 button->ConnectDependants(
true);
1756 for (; pos >= 0; --pos)
1765 if (buttonstate ==
nullptr)
1767 LOG(VB_GENERAL, LOG_ERR,
1768 "PageUp: Failed to query buttonlist state");
1773 buttonstate->GetArea().width() / 2 >= max_width)
1809 for (; pos >= 0; pos -= dec)
1818 if (buttonstate ==
nullptr)
1820 LOG(VB_GENERAL, LOG_ERR,
1821 "PageUp: Failed to query buttonlist state");
1826 buttonstate->GetArea().height() / 2 >= max_height)
1862 for (; pos < num_items; ++pos)
1871 if (buttonstate ==
nullptr)
1873 LOG(VB_GENERAL, LOG_ERR,
1874 "PageDown: Failed to query buttonlist state");
1879 buttonstate->GetArea().width() / 2 >= max_width)
1889 return num_items - 1;
1915 for (; pos < num_items; pos += inc)
1926 LOG(VB_GENERAL, LOG_ERR,
1927 "PageDown: Failed to query buttonlist state");
1932 buttonstate->GetArea().height() / 2 >= max_height)
1942 return num_items - 1;
2042 for (
uint i = 0; i < amount; ++i)
2074 if (m_selPosition < 0 || m_selPosition >=
m_itemList.size() ||
2119 if (m_selPosition < 0 || m_selPosition >=
m_itemList.size() ||
2254 for (
uint i = 0; i < amount; ++i)
2290 bool found_it =
false;
2291 int selectedPosition = 0;
2292 QList<MythUIButtonListItem *>::iterator it =
m_itemList.begin();
2296 if ((*it)->GetText() == position_name)
2326 bool dolast =
false;
2361 QMutableListIterator<MythUIButtonListItem *> it(
m_itemList);
2363 while (it.hasNext())
2364 it.next()->setChecked(state);
2391 LOG(VB_GENERAL, LOG_ERR, QString(
"(%1) Statetype buttonitem is "
2392 "required in mythuibuttonlist: %2")
2404 if (buttonActiveState)
2405 buttonItemArea = buttonActiveState->
GetArea();
2428 QString name = QString(
"buttonlist button %1").arg(i);
2431 button->ConnectDependants(
true);
2454 if (buttonSelectedState)
2492 QStringList actions;
2493 bool handled =
false;
2497 for (
int i = 0; i < actions.size(); ++i)
2506 QKeySequence a(key);
2511 Qt::KeyboardModifiers modifiers = Qt::NoModifier;
2512 QStringList parts = key.split(
'+');
2513 for (
int j = 0; j < parts.count(); ++j)
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;
2525 QCoreApplication::postEvent(
2527 new QKeyEvent(QEvent::KeyPress, keyCode, modifiers, key));
2528 QCoreApplication::postEvent(
2530 new QKeyEvent(QEvent::KeyRelease, keyCode, modifiers, key));
2536 for (
int i = 0; i < actions.size() && !handled; ++i)
2538 QString
action = actions[i];
2548 else if (
action ==
"DOWN")
2555 else if (
action ==
"RIGHT")
2569 else if (
action ==
"LEFT")
2583 else if (
action ==
"PAGEUP")
2587 else if (
action ==
"PAGEDOWN")
2591 else if (
action ==
"PAGETOP")
2595 else if (
action ==
"PAGEMIDDLE")
2599 else if (
action ==
"PAGEBOTTOM")
2603 else if (
action ==
"SELECT")
2610 else if (
action ==
"SEARCH")
2626 bool handled =
false;
2633 QPoint position =
event->GetPosition() -
2645 QString name =
object->objectName();
2647 if (name ==
"upscrollarrow")
2651 else if (name ==
"downscrollarrow")
2655 else if (name.startsWith(
"buttonlist button"))
2657 int pos = name.section(
' ', 2, 2).toInt();
2730 (QEvent::Type) QEvent::registerEventType();
2738 int cur = npe->m_start;
2739 for (; cur < npe->m_start + npe->m_pageSize && cur <
GetCount(); ++cur)
2741 const int loginterval = (cur < 1000 ? 100 : 500);
2742 if (cur > 200 && cur % loginterval == 0)
2743 LOG(VB_GUI, LOG_INFO,
2744 QString(
"Build background buttonlist item %1").
arg(cur));
2834 const QString &
filename, QDomElement &element,
bool showWarnings)
2836 if (element.tagName() ==
"buttonarea")
2838 else if (element.tagName() ==
"layout")
2842 if (layout ==
"grid")
2844 else if (layout ==
"horizontal")
2849 else if (element.tagName() ==
"arrange")
2853 if (arrange ==
"fill")
2855 else if (arrange ==
"spread")
2857 else if (arrange ==
"stack")
2863 else if (element.tagName() ==
"align")
2868 else if (element.tagName() ==
"scrollstyle")
2872 if (layout ==
"center")
2874 else if (layout ==
"groupcenter")
2876 else if (layout ==
"free")
2879 else if (element.tagName() ==
"wrapstyle")
2883 if (wrapstyle ==
"captive")
2885 else if (wrapstyle ==
"none")
2887 else if (wrapstyle ==
"selection")
2889 else if (wrapstyle ==
"flowing")
2891 else if (wrapstyle ==
"items")
2894 else if (element.tagName() ==
"showarrow")
2896 else if (element.tagName() ==
"showscrollbar")
2898 else if (element.tagName() ==
"spacing")
2903 else if (element.tagName() ==
"drawfrombottom")
2910 else if (element.tagName() ==
"searchposition")
2914 else if (element.tagName() ==
"triggerevent")
2917 if (!trigger.isEmpty())
2919 QString
action = element.attribute(
"action",
"");
2926 QString context = element.attribute(
"context",
"");
2928 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
2929 QStringList keys = keylist.split(
',', QString::SkipEmptyParts);
2931 QStringList keys = keylist.split(
',', Qt::SkipEmptyParts);
3013 QString name = QString(
"buttonlist button %1").arg(i);
3045 if (lcddev ==
nullptr)
3049 QList<LCDMenuItem> menuItems;
3054 for (
int r = start; r < end; ++r)
3076 text +=
" ~ " + props.
text;
3084 text +=
" ~ " + item->
GetText();
3088 if (!text.isEmpty())
3089 menuItems.append(
LCDMenuItem(selected, state, text));
3094 if (!menuItems.isEmpty())
3111 QRect dialogArea = dlg->GetArea();
3114 x = (screenArea.width() - dialogArea.width()) / 2;
3117 y = (screenArea.height() - dialogArea.height()) / 2;
3119 dlg->SetPosition(x, y);
3132 return DoFind(
false,
true);
3137 return DoFind(
true,
true);
3142 return DoFind(
true,
false);
3154 int currPos = startPos;
3200 if (startPos == currPos)
3210 QString text, QString image,
3212 bool showArrow,
int listPosition)
3213 : m_parent(lbtype), m_text(
std::move(text)), m_imageFilename(
std::move(image)),
3214 m_checkable(checkable), m_state(state), m_showArrow(showArrow)
3217 LOG(VB_GENERAL, LOG_ERR,
"Cannot add a button to a non-existent list!");
3227 const QString &text,
3228 QVariant data,
int listPosition)
3231 LOG(VB_GENERAL, LOG_ERR,
"Cannot add a button to a non-existent list!");
3235 m_data = std::move(data);
3257 QMap<QString, MythImage*>::iterator it;
3267 const QString &state)
3269 if (!name.isEmpty())
3272 textprop.
text = text;
3284 const QString &state)
3286 InfoMap::const_iterator map_it = infoMap.begin();
3288 while (map_it != infoMap.end())
3291 textprop.
text = (*map_it);
3317 bool startsWith)
const
3319 if (fieldList.isEmpty())
3322 return m_text.startsWith(searchStr, Qt::CaseInsensitive);
3323 return m_text.contains(searchStr, Qt::CaseInsensitive);
3325 if (fieldList ==
"**ALL**")
3329 if (
m_text.startsWith(searchStr, Qt::CaseInsensitive))
3334 if (
m_text.contains(searchStr, Qt::CaseInsensitive))
3338 QMap<QString, TextProperties>::const_iterator i =
m_strings.constBegin();
3344 if (i.value().text.startsWith(searchStr, Qt::CaseInsensitive))
3349 if (i.value().text.contains(searchStr, Qt::CaseInsensitive))
3358 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
3359 QStringList fields = fieldList.split(
',', QString::SkipEmptyParts);
3361 QStringList fields = fieldList.split(
',', Qt::SkipEmptyParts);
3364 for (
int x = 0; x < fields.count(); ++x)
3366 if (
m_strings.contains(fields.at(x).trimmed()))
3370 if (
m_strings[fields.at(x)].text.startsWith(searchStr, Qt::CaseInsensitive))
3375 if (
m_strings[fields.at(x)].text.contains(searchStr, Qt::CaseInsensitive))
3386 const QString &name)
3388 if (!name.isEmpty())
3405 if (!name.isEmpty())
3407 QMap<QString, MythImage*>::iterator it =
m_images.find(name);
3440 if (!name.isEmpty())
3442 QMap<QString, MythImage*>::iterator it =
m_images.find(name);
3459 const QString &
filename,
const QString &name,
bool force_reload)
3461 bool do_update = force_reload;
3463 if (!name.isEmpty())
3502 const QString &name)
3507 bool do_update =
false;
3508 InfoMap::iterator it =
m_states.find(name);
3515 else if (*it !=
state)
3579 m_data = std::move(data);
3622 LOG(VB_GUI, LOG_WARNING,
"Theme Error: Missing buttonlist state: disabled");
3628 LOG(VB_GUI, LOG_WARNING,
"Theme Error: Missing buttonlist state: inactive");
3636 LOG(VB_GENERAL, LOG_CRIT, QString(
"Theme Error: Missing buttonlist state: %1")
3641 buttonstate->Reset();
3644 (buttonstate->GetChild(
"buttontext"));
3653 (buttonstate->GetChild(
"buttonimage"));
3660 buttonimage->
Load();
3667 (buttonstate->GetChild(
"buttonarrow"));
3673 (buttonstate->GetChild(
"buttoncheck"));
3690 QMap<QString, TextProperties>::iterator string_it =
m_strings.begin();
3695 (buttonstate->GetChild(string_it.key()));
3701 QString newText = text->GetTemplateText();
3703 QRegularExpression re {R
"(%(([^\|%]+)?\||\|(.))?([\w#]+)(\|(.+?))?%)",
3704 QRegularExpression::DotMatchesEverythingOption};
3706 if (!newText.isEmpty() && newText.contains(re))
3708 QString tempString = newText;
3710 QRegularExpressionMatchIterator i = re.globalMatch(newText);
3711 while (i.hasNext()) {
3712 QRegularExpressionMatch match = i.next();
3713 QString key = match.captured(4).toLower().trimmed();
3714 QString replacement;
3715 QString value =
m_strings.value(key).text;
3717 if (!value.isEmpty())
3719 replacement = QString(
"%1%2%3%4")
3720 .arg(match.captured(2))
3721 .arg(match.captured(3))
3723 .arg(match.captured(6));
3726 tempString.replace(match.captured(0), replacement);
3729 newText = tempString;
3732 newText = textprop.
text;
3734 if (newText.isEmpty())
3737 text->SetText(newText);
3750 (buttonstate->GetChild(imagefile_it.key()));
3753 if (!imagefile_it.value().isEmpty())
3765 QMap<QString, MythImage *>::iterator image_it =
m_images.begin();
3770 (buttonstate->GetChild(image_it.key()));
3773 if (image_it.value())
3782 InfoMap::iterator state_it =
m_states.begin();
3787 (buttonstate->GetChild(state_it.key()));
3790 if (!statetype->DisplayState(state_it.value()))
3818 LOG(VB_GENERAL, LOG_ERR,
"Cannot load screen 'MythSearchListDialog'");
3838 QStringList actions;
3841 for (
int i = 0; i < actions.size() && !handled; ++i)
3843 QString
action = actions[i];