1 #include "common/common_pch.h"
2
3 #include <QAbstractItemView>
4 #include <QCheckBox>
5 #include <QComboBox>
6 #include <QDebug>
7 #include <QGridLayout>
8 #include <QGroupBox>
9 #include <QIcon>
10 #include <QLineEdit>
11 #include <QList>
12 #include <QPushButton>
13 #include <QRadioButton>
14 #include <QScrollArea>
15 #include <QSettings>
16 #include <QSpinBox>
17 #include <QString>
18
19 #include "common/bitvalue.h"
20 #include "common/kax_analyzer.h"
21 #include "common/list_utils.h"
22 #include "common/qt.h"
23 #include "mkvtoolnix-gui/main_window/main_window.h"
24 #include "mkvtoolnix-gui/util/file_dialog.h"
25 #include "mkvtoolnix-gui/util/language_display_widget.h"
26 #include "mkvtoolnix-gui/util/message_box.h"
27 #include "mkvtoolnix-gui/util/settings.h"
28 #include "mkvtoolnix-gui/util/settings_names.h"
29 #include "mkvtoolnix-gui/util/widget.h"
30
31 namespace mtx::gui::Util {
32
33 QIcon
loadIcon(QString const & name,QList<int> const & sizes)34 loadIcon(QString const &name,
35 QList<int> const &sizes) {
36 QIcon icon;
37 for (auto size : sizes)
38 icon.addFile(QString{":/icons/%1x%1/%2"}.arg(size).arg(name));
39
40 return icon;
41 }
42
43 bool
setComboBoxIndexIf(QComboBox * comboBox,std::function<bool (QString const &,QVariant const &)> test)44 setComboBoxIndexIf(QComboBox *comboBox,
45 std::function<bool(QString const &, QVariant const &)> test) {
46 auto count = comboBox->count();
47 for (int idx = 0; count > idx; ++idx)
48 if (test(comboBox->itemText(idx), comboBox->itemData(idx))) {
49 comboBox->setCurrentIndex(idx);
50 return true;
51 }
52
53 return false;
54 }
55
56 bool
setComboBoxTextByData(QComboBox * comboBox,QString const & data)57 setComboBoxTextByData(QComboBox *comboBox,
58 QString const &data) {
59 return setComboBoxIndexIf(comboBox, [&data](QString const &, QVariant const &itemData) { return itemData.isValid() && (itemData.toString() == data); });
60 }
61
62 void
setComboBoxTexts(QComboBox * comboBox,QStringList const & texts)63 setComboBoxTexts(QComboBox *comboBox,
64 QStringList const &texts) {
65 auto numItems = comboBox->count();
66 auto numTexts = texts.count();
67 auto textIdx = 0;
68 auto comboBoxIdx = 0;
69
70 while ((comboBoxIdx < numItems) && (textIdx < numTexts)) {
71 if (comboBox->itemData(comboBoxIdx).isValid()) {
72 comboBox->setItemText(comboBoxIdx, texts[textIdx]);
73 ++textIdx;
74 }
75
76 ++comboBoxIdx;
77 }
78 }
79
80 void
enableWidgets(QList<QWidget * > const & widgets,bool enable)81 enableWidgets(QList<QWidget *> const &widgets,
82 bool enable) {
83 for (auto &widget : widgets)
84 widget->setEnabled(enable);
85 }
86
87 void
enableChildren(QObject * parent,bool enable)88 enableChildren(QObject *parent,
89 bool enable) {
90 for (auto const &child : parent->children()) {
91 auto widget = qobject_cast<QWidget *>(child);
92 if (widget)
93 widget->setEnabled(enable);
94 }
95 }
96
97 QPushButton *
buttonForRole(QDialogButtonBox * box,QDialogButtonBox::ButtonRole role)98 buttonForRole(QDialogButtonBox *box,
99 QDialogButtonBox::ButtonRole role) {
100 auto buttons = box->buttons();
101 auto button = std::find_if(buttons.begin(), buttons.end(), [box, role](auto *b) { return box->buttonRole(b) == role; });
102 return button == buttons.end() ? nullptr : static_cast<QPushButton *>(*button);
103 }
104
105 void
setToolTip(QWidget * widget,QString const & toolTip)106 setToolTip(QWidget *widget,
107 QString const &toolTip) {
108 if (dynamic_cast<LanguageDisplayWidget *>(widget)) {
109 static_cast<LanguageDisplayWidget &>(*widget).setAdditionalToolTip(toolTip);
110 return;
111 }
112
113 if (Util::Settings::get().m_uiDisableToolTips) {
114 widget->setToolTip({});
115 return;
116 }
117
118 // Qt up to and including 5.3 only word-wraps tool tips
119 // automatically if the format is recognized to be Rich Text. See
120 // http://doc.qt.io/qt-5/qstandarditem.html
121
122 widget->setToolTip(toolTip.isEmpty() || toolTip.startsWith('<') ? toolTip : Q("<span>%1</span>").arg(toolTip.toHtmlEscaped()));
123 }
124
125 void
saveWidgetGeometry(QWidget * widget)126 saveWidgetGeometry(QWidget *widget) {
127 auto reg = Util::Settings::registry();
128
129 reg->beginGroup(s_grpWindowGeometry);
130 reg->setValue(widget->objectName(), widget->saveGeometry());
131 reg->endGroup();
132 }
133
134 void
restoreWidgetGeometry(QWidget * widget)135 restoreWidgetGeometry(QWidget *widget) {
136 auto reg = Util::Settings::registry();
137
138 reg->beginGroup(s_grpWindowGeometry);
139 widget->restoreGeometry(reg->value(widget->objectName()).toByteArray());
140 reg->endGroup();
141 }
142
143 QWidget *
tabWidgetCloseTabButton(QTabWidget & tabWidget,int tabIdx)144 tabWidgetCloseTabButton(QTabWidget &tabWidget,
145 int tabIdx) {
146 auto tabBar = tabWidget.tabBar();
147 auto result = mtx::first_of<QWidget *>([](QWidget *button) { return !!button; }, tabBar->tabButton(tabIdx, QTabBar::LeftSide), tabBar->tabButton(tabIdx, QTabBar::RightSide));
148
149 return result.value_or(nullptr);
150 }
151
152 void
fixScrollAreaBackground(QScrollArea * scrollArea)153 fixScrollAreaBackground(QScrollArea *scrollArea) {
154 scrollArea->setBackgroundRole(QPalette::Base);
155 }
156
157 void
preventScrollingWithoutFocus(QObject * parent)158 preventScrollingWithoutFocus(QObject *parent) {
159 auto install = [](QWidget *widget) {
160 widget->installEventFilter(MainWindow::get());
161 widget->setFocusPolicy(Qt::StrongFocus);
162 };
163
164 for (auto const &child : parent->findChildren<QCheckBox *>())
165 install(child);
166
167 for (auto const &child : parent->findChildren<QComboBox *>())
168 install(child);
169
170 for (auto const &child : parent->findChildren<QRadioButton *>())
171 install(child);
172
173 for (auto const &child : parent->findChildren<QSpinBox *>())
174 install(child);
175 }
176
177 void
fixComboBoxViewWidth(QComboBox & comboBox)178 fixComboBoxViewWidth(QComboBox &comboBox) {
179 comboBox.setSizeAdjustPolicy(QComboBox::AdjustToContents);
180 comboBox.view()->setMinimumWidth(comboBox.sizeHint().width());
181 }
182
183 void
addSegmentUIDFromFileToLineEdit(QWidget & parent,QLineEdit & lineEdit,bool append)184 addSegmentUIDFromFileToLineEdit(QWidget &parent,
185 QLineEdit &lineEdit,
186 bool append) {
187 auto &settings = Util::Settings::get();
188 auto dir = settings.lastOpenDirPath();
189 auto filter = QY("Matroska and WebM files") + Q(" (*.mkv *.mka *.mks *.mk3d *.webm);;")
190 + QY("All files") + Q(" (*)");
191 auto fileName = Util::getOpenFileName(&parent, QY("Select Matroska file to read segment UID from"), dir, filter);
192
193 if (fileName.isEmpty())
194 return;
195
196 settings.m_lastOpenDir.setPath(QFileInfo{fileName}.path());
197 settings.save();
198
199 try {
200 auto segmentUID = kax_analyzer_c::read_segment_uid_from(to_utf8(fileName));
201 auto uidString = QString{};
202 auto src = segmentUID->data();
203
204 for (int idx = 0, numBytes = segmentUID->byte_size(); idx < numBytes; ++idx)
205 uidString += Q("%1").arg(QString::number(src[idx], 16), 2, '0').toUpper();
206
207 if (!append || lineEdit.text().isEmpty())
208 lineEdit.setText(uidString);
209
210 else
211 lineEdit.setText(Q("%1,%2").arg(lineEdit.text()).arg(uidString));
212
213 } catch (mtx::kax_analyzer_x &ex) {
214 Util::MessageBox::critical(&parent)
215 ->title(QY("Reading the segment UID failed"))
216 .text(Q(ex.what()))
217 .exec();
218 }
219 }
220
221 #if defined(SYS_APPLE)
222 constexpr auto OS_SPECIFIC_ELIDE_POSITION = Qt::ElideRight;
223 #else
224 constexpr auto OS_SPECIFIC_ELIDE_POSITION = Qt::ElideMiddle;
225 #endif
226
227 void
setupTabWidgetHeaders(QTabWidget & tabWidget)228 setupTabWidgetHeaders(QTabWidget &tabWidget) {
229 auto &cfg = Util::Settings::get();
230
231 tabWidget.setTabPosition(Util::Settings::get().m_tabPosition);
232 tabWidget.setElideMode(cfg.m_elideTabHeaderLabels ? OS_SPECIFIC_ELIDE_POSITION : Qt::ElideNone);
233 }
234
235 void
autoGroupBoxGridLayout(QGroupBox & box,unsigned int numColumns)236 autoGroupBoxGridLayout(QGroupBox &box,
237 unsigned int numColumns) {
238 auto previousLayout = dynamic_cast<QGridLayout *>(box.layout());
239 if (!previousLayout) {
240 qDebug() << "autoGroupBoxGridLayout: current layout is not a grid layout?";
241 return;
242 }
243
244 QVector<QObject *> entries;
245 auto numPreviousColumns = previousLayout->columnCount();
246 auto numPreviousRows = previousLayout->rowCount();
247
248 for (auto column = 0; column < numPreviousColumns; column += 2) {
249 for (auto row = 0; row < numPreviousRows; ++row) {
250 for (auto offset = 0; offset < 2; ++offset) {
251 auto item = previousLayout->itemAtPosition(row, column + offset);
252 if (item && item->widget())
253 entries << item->widget();
254 }
255 }
256 }
257
258 auto newLayout = new QGridLayout;
259 auto numRows = ((entries.size() / 2) + numColumns - 1) / numColumns;
260 auto position = 0;
261
262
263 for (auto entry : entries) {
264 auto column = ((position / 2 / numRows) * 2) + (position % 2);
265 auto row = (position / 2) % numRows;
266
267 newLayout->addWidget(static_cast<QWidget *>(entry), row, column);
268 ++position;
269 }
270
271 delete previousLayout;
272 box.setLayout(newLayout);
273 }
274
275 }
276