1 #include <QCloseEvent>
2 #include <QSettings>
3 #include <QResizeEvent>
4 #include <QMessageBox>
5 #include <QPainter>
6 #include <QScrollArea>
7 #include <QVBoxLayout>
8 #include <QGroupBox>
9 #include <QFormLayout>
10 #include <QSpinBox>
11 #include <QDoubleSpinBox>
12 #include <QCheckBox>
13 #include <QComboBox>
14 #include <QPushButton>
15 #include <QToolButton>
16 #include <QMenu>
17 #include <QFileInfo>
18 #include <QFileDialog>
19 #include <QTimer>
20 #include <QMainWindow>
21 #include <QApplication>
22 #include <QLineEdit>
23 #include <QDialogButtonBox>
24 #include <QHBoxLayout>
25 #include <QColor>
26 #include <QColorDialog>
27 #include <QLabel>
28 #include <QBitmap>
29 #include <QStackedLayout>
30 #include <QScrollBar>
31 
32 #include "coreoptionsdialog.h"
33 #include "viewoptionsdialog.h"
34 #include "coreinfodialog.h"
35 #include "playlistentrydialog.h"
36 #include "../ui_qt.h"
37 
38 #ifndef CXX_BUILD
39 extern "C" {
40 #endif
41 
42 #ifdef HAVE_CONFIG_H
43 #include "../../../config.h"
44 #endif
45 
46 #include <math.h>
47 #include <string/stdstring.h>
48 #include <streams/file_stream.h>
49 #include <file/file_path.h>
50 
51 #ifdef HAVE_MENU
52 #include "../../../menu/menu_driver.h"
53 #include "../../../menu/menu_entries.h"
54 #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
55 #include "../../../menu/menu_shader.h"
56 #endif
57 #endif
58 
59 #include "../../../command.h"
60 #include "../../../core_info.h"
61 #include "../../../core_option_manager.h"
62 #include "../../../configuration.h"
63 #include "../../../file_path_special.h"
64 #include "../../../msg_hash.h"
65 #include "../../../paths.h"
66 #include "../../../retroarch.h"
67 
68 #ifndef CXX_BUILD
69 }
70 #endif
71 
72 #if defined(HAVE_MENU)
73 #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
74 #include "shaderparamsdialog.h"
75 #endif
76 #endif
77 
78 #include "qt_options.h"
79 
80 #if defined(_MSC_VER) && !defined(_XBOX) && (_MSC_VER >= 1500 && _MSC_VER < 1900)
81 /* https://support.microsoft.com/en-us/kb/980263 */
82 #pragma execution_character_set("utf-8")
83 #pragma warning(disable:4566)
84 #endif
85 
comp_string_lower(const QString & lhs,const QString & rhs)86 static inline bool comp_string_lower(const QString &lhs, const QString &rhs)
87 {
88    return lhs.toLower() < rhs.toLower();
89 }
90 
comp_hash_ui_display_name_key_lower(const QHash<QString,QString> & lhs,const QHash<QString,QString> & rhs)91 static inline bool comp_hash_ui_display_name_key_lower(const QHash<QString, QString> &lhs, const QHash<QString, QString> &rhs)
92 {
93    return lhs.value("ui_display_name").toLower() < rhs.value("ui_display_name").toLower();
94 }
95 
PlaylistEntryDialog(MainWindow * mainwindow,QWidget * parent)96 PlaylistEntryDialog::PlaylistEntryDialog(MainWindow *mainwindow, QWidget *parent) :
97    QDialog(parent)
98    ,m_mainwindow(mainwindow)
99    ,m_settings(mainwindow->settings())
100    ,m_nameLineEdit(new QLineEdit(this))
101    ,m_pathLineEdit(new QLineEdit(this))
102    ,m_extensionsLineEdit(new QLineEdit(this))
103    ,m_coreComboBox(new QComboBox(this))
104    ,m_databaseComboBox(new QComboBox(this))
105    ,m_extensionArchiveCheckBox(new QCheckBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY_FILTER_INSIDE_ARCHIVES), this))
106 {
107    QFormLayout *form                = new QFormLayout();
108    QDialogButtonBox *buttonBox      = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
109    QVBoxLayout *databaseVBoxLayout  = new QVBoxLayout();
110    QHBoxLayout *pathHBoxLayout      = new QHBoxLayout();
111    QHBoxLayout *extensionHBoxLayout = new QHBoxLayout();
112    QLabel *databaseLabel            = new QLabel(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FOR_THUMBNAILS), this);
113    QToolButton *pathPushButton      = new QToolButton(this);
114 
115    pathPushButton->setText(QStringLiteral("..."));
116 
117    pathHBoxLayout->addWidget(m_pathLineEdit);
118    pathHBoxLayout->addWidget(pathPushButton);
119 
120    databaseVBoxLayout->addWidget(m_databaseComboBox);
121    databaseVBoxLayout->addWidget(databaseLabel);
122 
123    extensionHBoxLayout->addWidget(m_extensionsLineEdit);
124    extensionHBoxLayout->addWidget(m_extensionArchiveCheckBox);
125 
126    m_extensionsLineEdit->setPlaceholderText(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY_EXTENSIONS_PLACEHOLDER));
127 
128    /* Ensure placeholder text is completely visible. */
129    m_extensionsLineEdit->setMinimumWidth(QFontMetrics(m_extensionsLineEdit->font()).boundingRect(m_extensionsLineEdit->placeholderText()).width() + m_extensionsLineEdit->frameSize().width());
130 
131    setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY));
132 
133    form->setFormAlignment(Qt::AlignCenter);
134    form->setLabelAlignment(Qt::AlignCenter);
135 
136    setLayout(new QVBoxLayout(this));
137 
138    connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
139    connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
140 
141    connect(this, SIGNAL(accepted()), this, SLOT(onAccepted()));
142    connect(this, SIGNAL(rejected()), this, SLOT(onRejected()));
143 
144    form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY_NAME), m_nameLineEdit);
145    form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY_PATH), pathHBoxLayout);
146    form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY_CORE), m_coreComboBox);
147    form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY_DATABASE), databaseVBoxLayout);
148    form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PLAYLIST_ENTRY_EXTENSIONS), extensionHBoxLayout);
149 
150    qobject_cast<QVBoxLayout*>(layout())->addLayout(form);
151    layout()->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
152    layout()->addWidget(buttonBox);
153 
154    connect(pathPushButton, SIGNAL(clicked()), this, SLOT(onPathClicked()));
155 }
156 
filterInArchive()157 bool PlaylistEntryDialog::filterInArchive()
158 {
159    return m_extensionArchiveCheckBox->isChecked();
160 }
161 
onPathClicked()162 void PlaylistEntryDialog::onPathClicked()
163 {
164    QString filePath = QFileDialog::getOpenFileName(this);
165 
166    if (filePath.isEmpty())
167       return;
168 
169    m_pathLineEdit->setText(filePath);
170 }
171 
loadPlaylistOptions()172 void PlaylistEntryDialog::loadPlaylistOptions()
173 {
174    unsigned i, j;
175    core_info_list_t *core_info_list = NULL;
176 
177    m_nameLineEdit->clear();
178    m_pathLineEdit->clear();
179    m_coreComboBox->clear();
180    m_databaseComboBox->clear();
181 
182    m_coreComboBox->addItem(
183          msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CORE_SELECTION_ASK));
184    m_databaseComboBox->addItem(
185            QString("<")
186          + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE)
187          + ">",
188          QFileInfo(m_mainwindow->getCurrentPlaylistPath()).fileName().remove(".lpl"));
189 
190    core_info_get_list(&core_info_list);
191 
192    if (core_info_list && core_info_list->count > 0)
193    {
194       QVector<QHash<QString, QString> > allCores;
195       QStringList allDatabases;
196 
197       for (i = 0; i < core_info_list->count; i++)
198       {
199          QString ui_display_name;
200          QHash<QString, QString> hash;
201          const core_info_t *core = &core_info_list->list[i];
202          QStringList databases   = string_split_to_qt(QString(core->databases), '|');
203 
204          hash["core_name"]         = core->core_name;
205          hash["core_display_name"] = core->display_name;
206          hash["core_path"]         = core->path;
207          hash["core_databases"]    = core->databases;
208 
209          ui_display_name           = hash.value("core_name");
210 
211          if (ui_display_name.isEmpty())
212             ui_display_name        = hash.value("core_display_name");
213          if (ui_display_name.isEmpty())
214             ui_display_name        = QFileInfo(
215                   hash.value("core_path")).fileName();
216 
217          if (ui_display_name.isEmpty())
218             continue;
219 
220          hash["ui_display_name"] = ui_display_name;
221 
222          for (j = 0; static_cast<int>(j) < databases.count(); j++)
223          {
224             QString database = databases.at(static_cast<int>(j));
225 
226             if (database.isEmpty())
227                continue;
228 
229             if (!allDatabases.contains(database))
230                allDatabases.append(database);
231          }
232 
233          if (!allCores.contains(hash))
234             allCores.append(hash);
235       }
236 
237       std::sort(allCores.begin(), allCores.end(), comp_hash_ui_display_name_key_lower);
238       std::sort(allDatabases.begin(), allDatabases.end(), comp_string_lower);
239 
240       for (j = 0; static_cast<int>(j) < allCores.count(); j++)
241       {
242          const QHash<QString, QString> &hash = allCores.at(static_cast<int>(j));
243 
244          m_coreComboBox->addItem(hash.value("ui_display_name"), QVariant::fromValue(hash));
245       }
246 
247       for (j = 0; static_cast<int>(j) < allDatabases.count(); j++)
248       {
249          QString database = allDatabases.at(static_cast<int>(j));
250          m_databaseComboBox->addItem(database, database);
251       }
252    }
253 }
254 
nameFieldEnabled()255 bool PlaylistEntryDialog::nameFieldEnabled()
256 {
257    return m_nameLineEdit->isEnabled();
258 }
259 
setEntryValues(const QHash<QString,QString> & contentHash)260 void PlaylistEntryDialog::setEntryValues(
261       const QHash<QString, QString> &contentHash)
262 {
263    QString db;
264    QString coreName = contentHash.value("core_name");
265    int foundDB = 0;
266    int i       = 0;
267 
268    loadPlaylistOptions();
269 
270    if (contentHash.isEmpty())
271    {
272       m_nameLineEdit->setText(
273             msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FIELD_MULTIPLE));
274       m_pathLineEdit->setText(
275             msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_FIELD_MULTIPLE));
276       m_nameLineEdit->setEnabled(false);
277       m_pathLineEdit->setEnabled(false);
278    }
279    else
280    {
281       m_nameLineEdit->setText(contentHash.value("label"));
282       m_pathLineEdit->setText(contentHash.value("path"));
283       m_nameLineEdit->setEnabled(true);
284       m_pathLineEdit->setEnabled(true);
285    }
286 
287    for (i = 0; i < m_coreComboBox->count(); i++)
288    {
289       const QHash<QString, QString> hash = m_coreComboBox->itemData(i, Qt::UserRole).value<QHash<QString, QString> >();
290 
291       if (hash.isEmpty() || coreName.isEmpty())
292          continue;
293 
294       if (hash.value("core_name") == coreName)
295       {
296          m_coreComboBox->setCurrentIndex(i);
297          break;
298       }
299    }
300 
301    db = contentHash.value("db_name");
302 
303    if (!db.isEmpty())
304    {
305       foundDB = m_databaseComboBox->findText(db);
306 
307       if (foundDB >= 0)
308          m_databaseComboBox->setCurrentIndex(foundDB);
309    }
310 }
311 
getSelectedCore()312 const QHash<QString, QString> PlaylistEntryDialog::getSelectedCore()
313 {
314    return m_coreComboBox->currentData(Qt::UserRole).value<QHash<QString, QString> >();
315 }
316 
getSelectedName()317 const QString PlaylistEntryDialog::getSelectedName()
318 {
319    return m_nameLineEdit->text();
320 }
321 
getSelectedPath()322 const QString PlaylistEntryDialog::getSelectedPath()
323 {
324    return m_pathLineEdit->text();
325 }
326 
getSelectedDatabase()327 const QString PlaylistEntryDialog::getSelectedDatabase()
328 {
329    return m_databaseComboBox->currentData(Qt::UserRole).toString();
330 }
331 
getSelectedExtensions()332 const QStringList PlaylistEntryDialog::getSelectedExtensions()
333 {
334    QStringList list;
335    QString text = m_extensionsLineEdit->text();
336 
337    /* Otherwise it would create a QStringList with a single blank entry... */
338    if (!text.isEmpty())
339       list   = string_split_to_qt(text, ' ');
340    return list;
341 }
342 
onAccepted()343 void PlaylistEntryDialog::onAccepted()
344 {
345 }
346 
onRejected()347 void PlaylistEntryDialog::onRejected()
348 {
349 }
350 
showDialog(const QHash<QString,QString> & hash)351 bool PlaylistEntryDialog::showDialog(const QHash<QString, QString> &hash)
352 {
353    loadPlaylistOptions();
354    setEntryValues(hash);
355 
356    if (exec() == QDialog::Accepted)
357       return true;
358    return false;
359 }
360 
hideDialog()361 void PlaylistEntryDialog::hideDialog()
362 {
363    reject();
364 }
365 
CoreInfoDialog(MainWindow * mainwindow,QWidget * parent)366 CoreInfoDialog::CoreInfoDialog(MainWindow *mainwindow, QWidget *parent) :
367    QDialog(parent)
368    ,m_formLayout(new QFormLayout())
369    ,m_mainwindow(mainwindow)
370 {
371    QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok);
372 
373    connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
374    connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
375 
376    setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFORMATION));
377 
378    m_formLayout->setFormAlignment(Qt::AlignCenter);
379    m_formLayout->setLabelAlignment(Qt::AlignCenter);
380 
381    setLayout(new QVBoxLayout());
382 
383    qobject_cast<QVBoxLayout*>(layout())->addLayout(m_formLayout);
384    layout()->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
385    layout()->addWidget(buttonBox);
386 }
387 
showCoreInfo()388 void CoreInfoDialog::showCoreInfo()
389 {
390    int      row  = 0;
391    int row_count = m_formLayout->rowCount();
392    int       i   = 0;
393    QVector<QHash<QString, QString> > info_list
394                  = m_mainwindow->getCoreInfo();
395 
396    if (row_count > 0)
397    {
398       for (row = 0; row < row_count; row++)
399       {
400 #if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
401          /* removeRow() and takeRow() was only added in 5.8! */
402          m_formLayout->removeRow(0);
403 #else
404          /* something is buggy here...
405           * sometimes items appear duplicated, and other times not */
406          QLayoutItem *item = m_formLayout->itemAt(0);
407          QWidget        *w = NULL;
408 
409          if (item)
410          {
411             w = item->widget();
412 
413             if (w)
414             {
415                QWidget *label = m_formLayout->labelForField(w);
416 
417                if (label)
418                   delete label;
419 
420                m_formLayout->removeWidget(w);
421 
422                delete w;
423             }
424          }
425 #endif
426       }
427    }
428 
429    if (info_list.count() == 0)
430       return;
431 
432    for (i = 0; i < info_list.count(); i++)
433    {
434       const QHash<QString, QString> &line = info_list.at(i);
435       QLabel                       *label = new QLabel(line.value("key"));
436       CoreInfoLabel                *value = new CoreInfoLabel(line.value("value"));
437       QString                  labelStyle = line.value("label_style");
438       QString                  valueStyle = line.value("value_style");
439 
440       if (!labelStyle.isEmpty())
441          label->setStyleSheet(labelStyle);
442 
443       if (!valueStyle.isEmpty())
444          value->setStyleSheet(valueStyle);
445 
446       m_formLayout->addRow(label, value);
447    }
448 
449    show();
450 }
451 
452 #ifdef HAVE_MENU
getColorizedPixmap(const QPixmap & oldPixmap,const QColor & color)453 QPixmap getColorizedPixmap(const QPixmap& oldPixmap, const QColor& color)
454 {
455    QPixmap pixmap = oldPixmap;
456    QBitmap   mask = pixmap.createMaskFromColor(Qt::transparent, Qt::MaskInColor);
457    pixmap.fill(color);
458    pixmap.setMask(mask);
459    return pixmap;
460 }
461 
getLabelColor(const QString & objectName)462 QColor getLabelColor(const QString& objectName)
463 {
464    QLabel dummyColor;
465    dummyColor.setObjectName(objectName);
466    dummyColor.ensurePolished();
467    return dummyColor.palette().color(QPalette::Foreground);
468 }
469 
470 /* stolen from Qt Creator */
471 class SmartScrollArea : public QScrollArea
472 {
473 public:
SmartScrollArea(QWidget * parent)474    explicit SmartScrollArea(QWidget *parent) :
475       QScrollArea(parent)
476    {
477       setFrameStyle(QFrame::NoFrame | QFrame::Plain);
478       viewport()->setAutoFillBackground(false);
479       setWidgetResizable(true);
480    }
481 private:
resizeEvent(QResizeEvent * event)482    void resizeEvent(QResizeEvent *event) final
483    {
484       QWidget *inner = widget();
485 
486       if (inner)
487       {
488          int              fw = frameWidth() * 2;
489          QSize     innerSize = event->size() - QSize(fw, fw);
490          QSize innerSizeHint = inner->minimumSizeHint();
491 
492          /* Widget wants to be bigger than available space */
493          if (innerSizeHint.height() > innerSize.height())
494          {
495             innerSize.setWidth(innerSize.width() - scrollBarWidth());
496             innerSize.setHeight(innerSizeHint.height());
497          }
498          inner->resize(innerSize);
499       }
500       QScrollArea::resizeEvent(event);
501    }
502 
minimumSizeHint() const503    QSize minimumSizeHint() const final
504    {
505       static const int max_min_width  = 250;
506       static const int max_min_height = 250;
507       QWidget *inner                  = widget();
508 
509       if (inner)
510       {
511          int        fw                = frameWidth() * 2;
512          QSize minSize                = inner->minimumSizeHint();
513 
514          minSize                     += QSize(fw, fw);
515          minSize                     += QSize(scrollBarWidth(), 0);
516          minSize.setWidth(qMin(minSize.width(), max_min_width));
517          minSize.setHeight(qMin(minSize.height(), max_min_height));
518          return minSize;
519       }
520       return QSize(0, 0);
521    }
522 
event(QEvent * event)523    bool event(QEvent *event) final
524    {
525       if (event->type() == QEvent::LayoutRequest)
526          updateGeometry();
527       return QScrollArea::event(event);
528    }
529 
scrollBarWidth() const530    int scrollBarWidth() const
531    {
532       SmartScrollArea *that = const_cast<SmartScrollArea *>(this);
533       QWidgetList list = that->scrollBarWidgets(Qt::AlignRight);
534       if (list.isEmpty())
535          return 0;
536       return list.first()->sizeHint().width();
537    }
538 };
539 
ViewOptionsDialog(MainWindow * mainwindow,QWidget * parent)540 ViewOptionsDialog::ViewOptionsDialog(MainWindow *mainwindow, QWidget *parent) :
541    QDialog(mainwindow)
542    ,m_optionsList(new QListWidget(this))
543    ,m_optionsStack(new QStackedLayout)
544 {
545    int width;
546    QGridLayout        *layout = new QGridLayout(this);
547    QLabel      *m_headerLabel = new QLabel(this);
548    /* Header label with large font and a bit of spacing
549     * (align with group boxes) */
550    QFont      headerLabelFont = m_headerLabel->font();
551    const int        pointSize = headerLabelFont.pointSize();
552    QHBoxLayout *headerHLayout = new QHBoxLayout;
553    const int       leftMargin = QApplication::style()->pixelMetric(QStyle::PM_LayoutLeftMargin);
554 
555    m_optionsStack->setMargin(0);
556 
557    headerLabelFont.setBold(true);
558 
559    /* Paranoia: Should a font be set in pixels... */
560    if (pointSize > 0)
561       headerLabelFont.setPointSize(pointSize + 2);
562 
563    m_headerLabel->setFont(headerLabelFont);
564 
565    headerHLayout->addSpacerItem(new QSpacerItem(leftMargin, 0, QSizePolicy::Fixed, QSizePolicy::Ignored));
566    headerHLayout->addWidget(m_headerLabel);
567 
568    addCategory(new DriversCategory(this));
569    addCategory(new VideoCategory(this));
570    addCategory(new AudioCategory(this));
571    addCategory(new InputCategory(this));
572    addCategory(new LatencyCategory(this));
573    addCategory(new CoreCategory(this));
574    addCategory(new ConfigurationCategory(this));
575    addCategory(new SavingCategory(this));
576    addCategory(new LoggingCategory(this));
577    addCategory(new FrameThrottleCategory(this));
578    addCategory(new RecordingCategory(this));
579    addCategory(new OnscreenDisplayCategory(this));
580    addCategory(new UserInterfaceCategory(mainwindow, this));
581    addCategory(new AIServiceCategory(this));
582    addCategory(new AchievementsCategory(this));
583    addCategory(new NetworkCategory(this));
584    addCategory(new PlaylistsCategory(this));
585    addCategory(new UserCategory(this));
586    addCategory(new DirectoryCategory(this));
587 
588    width  =
589         m_optionsList->sizeHintForColumn(0)
590       + m_optionsList->frameWidth() * 2
591       + 5;
592    width += m_optionsList->verticalScrollBar()->sizeHint().width();
593 
594    m_optionsList->setMaximumWidth(width);
595    m_optionsList->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
596 
597    setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_TITLE));
598 
599    layout->addWidget(m_optionsList, 0, 0, 2, 1);
600    layout->addLayout(headerHLayout, 0, 1);
601    layout->addLayout(m_optionsStack, 1, 1);
602 
603    connect(m_optionsList, SIGNAL(currentRowChanged(int)), m_optionsStack, SLOT(setCurrentIndex(int)));
604    connect(m_optionsList, SIGNAL(currentTextChanged(const QString&)), m_headerLabel, SLOT(setText(const QString&)));
605 
606    connect(this, SIGNAL(rejected()), this, SLOT(onRejected()));
607 }
608 
getIcon(OptionsCategory * category)609 QIcon getIcon(OptionsCategory *category)
610 {
611    settings_t *settings        = config_get_ptr();
612    const char *path_dir_assets = settings->paths.directory_assets;
613    QPixmap pixmap              = QPixmap(QString(path_dir_assets)
614          + "/xmb/monochrome/png/"
615          + category->categoryIconName()
616          + ".png");
617    return QIcon(getColorizedPixmap(pixmap, getLabelColor("iconColor")));
618 }
619 
addCategory(OptionsCategory * category)620 void ViewOptionsDialog::addCategory(OptionsCategory *category)
621 {
622    QTabWidget *tabWidget = new QTabWidget();
623 
624    m_categoryList.append(category);
625 
626    for (OptionsPage* page : category->pages())
627    {
628       SmartScrollArea *scrollArea = new SmartScrollArea(this);
629       QWidget             *widget = page->widget();
630 
631       scrollArea->setWidget(widget);
632       widget->setAutoFillBackground(false);
633       tabWidget->addTab(scrollArea, page->displayName());
634    }
635 
636 #if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
637    tabWidget->setTabBarAutoHide(true);
638 #else
639    /* TODO remove the tabBar's space */
640    if (tabWidget->count() < 2)
641       tabWidget->tabBar()->hide();
642 #endif
643    m_optionsList->addItem(
644          new QListWidgetItem(getIcon(category),
645             category->displayName()));
646    m_optionsStack->addWidget(tabWidget);
647 }
648 
repaintIcons()649 void ViewOptionsDialog::repaintIcons()
650 {
651    unsigned i;
652    size_t list_size = (size_t)m_categoryList.size();
653 
654    for (i = 0; i < list_size; i++)
655       m_optionsList->item(i)->setIcon(getIcon(m_categoryList.at(i)));
656 }
657 #else
ViewOptionsDialog(MainWindow * mainwindow,QWidget * parent)658 ViewOptionsDialog::ViewOptionsDialog(MainWindow *mainwindow, QWidget *parent) :
659    QDialog(mainwindow)
660    , m_viewOptionsWidget(new ViewOptionsWidget(mainwindow))
661 {
662    QVBoxLayout         *layout = new QVBoxLayout;
663    QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
664 
665    connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
666    connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
667 
668    connect(this, SIGNAL(accepted()), m_viewOptionsWidget, SLOT(onAccepted()));
669    connect(this, SIGNAL(rejected()), m_viewOptionsWidget, SLOT(onRejected()));
670 
671    setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_TITLE));
672 
673    layout->setContentsMargins(0, 0, 0, 0);
674 
675    layout->addWidget(m_viewOptionsWidget);
676    layout->addWidget(buttonBox);
677 
678    setLayout(layout);
679 }
680 #endif
681 
showDialog()682 void ViewOptionsDialog::showDialog()
683 {
684 #ifdef HAVE_MENU
685    unsigned i;
686    size_t list_size = (size_t)m_categoryList.size();
687    for (i = 0; i < list_size; i++)
688       m_categoryList.at(i)->load();
689 #else
690    m_viewOptionsWidget->loadViewOptions();
691 #endif
692    show();
693    activateWindow();
694 }
695 
hideDialog()696 void ViewOptionsDialog::hideDialog()
697 {
698    reject();
699 }
700 
onRejected()701 void ViewOptionsDialog::onRejected()
702 {
703 #ifdef HAVE_MENU
704    unsigned i;
705    size_t list_size = (size_t)m_categoryList.size();
706    for (i = 0; i < list_size; i++)
707       m_categoryList.at(i)->apply();
708 #endif
709 }
710 
ViewOptionsWidget(MainWindow * mainwindow,QWidget * parent)711 ViewOptionsWidget::ViewOptionsWidget(MainWindow *mainwindow, QWidget *parent) :
712    QWidget(parent)
713    ,m_mainwindow(mainwindow)
714    ,m_settings(mainwindow->settings())
715    ,m_saveGeometryCheckBox(new QCheckBox(this))
716    ,m_saveDockPositionsCheckBox(new QCheckBox(this))
717    ,m_saveLastTabCheckBox(new QCheckBox(this))
718    ,m_showHiddenFilesCheckBox(new QCheckBox(this))
719    ,m_themeComboBox(new QComboBox(this))
720    ,m_thumbnailCacheSpinBox(new QSpinBox(this))
721    ,m_thumbnailDropSizeSpinBox(new QSpinBox(this))
722    ,m_startupPlaylistComboBox(new QComboBox(this))
723    ,m_highlightColorPushButton(new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CHOOSE), this))
724    ,m_highlightColor()
725    ,m_highlightColorLabel(new QLabel(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_HIGHLIGHT_COLOR), this))
726    ,m_customThemePath()
727    ,m_suggestLoadedCoreFirstCheckBox(new QCheckBox(this))
728    /* ,m_allPlaylistsListMaxCountSpinBox(new QSpinBox(this)) */
729    /* ,m_allPlaylistsGridMaxCountSpinBox(new QSpinBox(this)) */
730 {
731    QVBoxLayout *layout = new QVBoxLayout;
732    QFormLayout *form   = new QFormLayout;
733 
734    m_themeComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME_SYSTEM_DEFAULT), MainWindow::THEME_SYSTEM_DEFAULT);
735    m_themeComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME_DARK), MainWindow::THEME_DARK);
736    m_themeComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME_CUSTOM), MainWindow::THEME_CUSTOM);
737 
738    m_thumbnailCacheSpinBox->setSuffix(" MB");
739    m_thumbnailCacheSpinBox->setRange(0, 99999);
740 
741    m_thumbnailDropSizeSpinBox->setSuffix(" px");
742    m_thumbnailDropSizeSpinBox->setRange(0, 99999);
743 
744    /* m_allPlaylistsListMaxCountSpinBox->setRange(0, 99999); */
745    /* m_allPlaylistsGridMaxCountSpinBox->setRange(0, 99999); */
746 
747    form->setFormAlignment(Qt::AlignCenter);
748    form->setLabelAlignment(Qt::AlignCenter);
749 
750    form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SAVE_GEOMETRY), m_saveGeometryCheckBox);
751    form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SAVE_DOCK_POSITIONS), m_saveDockPositionsCheckBox);
752    form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SAVE_LAST_TAB), m_saveLastTabCheckBox);
753    form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SHOW_HIDDEN_FILES), m_showHiddenFilesCheckBox);
754    form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SUGGEST_LOADED_CORE_FIRST), m_suggestLoadedCoreFirstCheckBox);
755 #if 0
756    form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_ALL_PLAYLISTS_LIST_MAX_COUNT), m_allPlaylistsListMaxCountSpinBox);
757    form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_ALL_PLAYLISTS_GRID_MAX_COUNT), m_allPlaylistsGridMaxCountSpinBox);
758 #endif
759    form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_STARTUP_PLAYLIST), m_startupPlaylistComboBox);
760    form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THUMBNAIL_CACHE_LIMIT), m_thumbnailCacheSpinBox);
761    form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THUMBNAIL_DROP_SIZE_LIMIT), m_thumbnailDropSizeSpinBox);
762    form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME), m_themeComboBox);
763    form->addRow(m_highlightColorLabel, m_highlightColorPushButton);
764 
765    layout->addLayout(form);
766 
767    layout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
768 
769    setLayout(layout);
770 
771    loadViewOptions();
772 
773    connect(m_themeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onThemeComboBoxIndexChanged(int)));
774    connect(m_highlightColorPushButton, SIGNAL(clicked()), this, SLOT(onHighlightColorChoose()));
775 }
776 
onThemeComboBoxIndexChanged(int)777 void ViewOptionsWidget::onThemeComboBoxIndexChanged(int)
778 {
779    MainWindow::Theme theme = static_cast<MainWindow::Theme>(m_themeComboBox->currentData(Qt::UserRole).toInt());
780 
781    if (theme == MainWindow::THEME_CUSTOM)
782    {
783       QString filePath = QFileDialog::getOpenFileName(this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SELECT_THEME));
784 
785       if (filePath.isEmpty())
786       {
787          int oldThemeIndex = m_themeComboBox->findData(m_mainwindow->getThemeFromString(m_settings->value("theme", "default").toString()));
788 
789          if (m_themeComboBox->count() > oldThemeIndex)
790          {
791             disconnect(m_themeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onThemeComboBoxIndexChanged(int)));
792             m_themeComboBox->setCurrentIndex(oldThemeIndex);
793             connect(m_themeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onThemeComboBoxIndexChanged(int)));
794          }
795       }
796       else
797       {
798          m_customThemePath = filePath;
799 
800          if (m_mainwindow->setCustomThemeFile(filePath))
801             m_mainwindow->setTheme(theme);
802       }
803    }
804    else
805       m_mainwindow->setTheme(theme);
806 
807    showOrHideHighlightColor();
808 }
809 
onHighlightColorChoose()810 void ViewOptionsWidget::onHighlightColorChoose()
811 {
812    QPixmap highlightPixmap(m_highlightColorPushButton->iconSize());
813    QColor currentHighlightColor = m_settings->value("highlight_color",
814          QApplication::palette().highlight().color()).value<QColor>();
815    QColor newHighlightColor     = QColorDialog::getColor(
816          currentHighlightColor, this,
817          msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SELECT_COLOR));
818 
819    if (newHighlightColor.isValid())
820    {
821       MainWindow::Theme theme = static_cast<MainWindow::Theme>(
822             m_themeComboBox->currentData(Qt::UserRole).toInt());
823 
824       m_highlightColor = newHighlightColor;
825       m_settings->setValue("highlight_color", m_highlightColor);
826       highlightPixmap.fill(m_highlightColor);
827       m_highlightColorPushButton->setIcon(highlightPixmap);
828       m_mainwindow->setTheme(theme);
829    }
830 }
831 
loadViewOptions()832 void ViewOptionsWidget::loadViewOptions()
833 {
834    int i;
835    int themeIndex    = 0;
836    int playlistIndex = 0;
837    QColor highlightColor                       =
838       m_settings->value("highlight_color",
839             QApplication::palette().highlight().color()).value<QColor>();
840    QPixmap highlightPixmap(m_highlightColorPushButton->iconSize());
841    QVector<QPair<QString, QString> > playlists = m_mainwindow->getPlaylists();
842    QString initialPlaylist = m_settings->value("initial_playlist",
843          m_mainwindow->getSpecialPlaylistPath(
844             SPECIAL_PLAYLIST_HISTORY)).toString();
845 
846    m_saveGeometryCheckBox->setChecked(m_settings->value("save_geometry", false).toBool());
847    m_saveDockPositionsCheckBox->setChecked(m_settings->value("save_dock_positions", false).toBool());
848    m_saveLastTabCheckBox->setChecked(m_settings->value("save_last_tab", false).toBool());
849    m_showHiddenFilesCheckBox->setChecked(m_settings->value("show_hidden_files", true).toBool());
850    m_suggestLoadedCoreFirstCheckBox->setChecked(m_settings->value("suggest_loaded_core_first", false).toBool());
851 #if 0
852    m_allPlaylistsListMaxCountSpinBox->setValue(m_settings->value("all_playlists_list_max_count", 0).toInt());
853    m_allPlaylistsGridMaxCountSpinBox->setValue(m_settings->value("all_playlists_grid_max_count", 5000).toInt());
854 #endif
855    m_thumbnailCacheSpinBox->setValue(m_settings->value("thumbnail_cache_limit", 512).toInt());
856    m_thumbnailDropSizeSpinBox->setValue(m_settings->value("thumbnail_max_size", 0).toInt());
857 
858    themeIndex = m_themeComboBox->findData(m_mainwindow->getThemeFromString(m_settings->value("theme", "default").toString()));
859 
860    if (m_themeComboBox->count() > themeIndex)
861       m_themeComboBox->setCurrentIndex(themeIndex);
862 
863    if (highlightColor.isValid())
864    {
865       m_highlightColor = highlightColor;
866       highlightPixmap.fill(m_highlightColor);
867       m_highlightColorPushButton->setIcon(highlightPixmap);
868    }
869 
870    showOrHideHighlightColor();
871 
872    m_startupPlaylistComboBox->clear();
873 
874    for (i = 0; i < playlists.count(); i++)
875    {
876       const QPair<QString, QString> &pair = playlists.at(i);
877 
878       m_startupPlaylistComboBox->addItem(pair.first, pair.second);
879    }
880 
881    playlistIndex = m_startupPlaylistComboBox->findData(
882          initialPlaylist, Qt::UserRole, Qt::MatchFixedString);
883 
884    if (playlistIndex >= 0)
885       m_startupPlaylistComboBox->setCurrentIndex(playlistIndex);
886 }
887 
showOrHideHighlightColor()888 void ViewOptionsWidget::showOrHideHighlightColor()
889 {
890    if (m_mainwindow->theme() == MainWindow::THEME_DARK)
891    {
892       m_highlightColorLabel->show();
893       m_highlightColorPushButton->show();
894    }
895    else
896    {
897       m_highlightColorLabel->hide();
898       m_highlightColorPushButton->hide();
899    }
900 }
901 
saveViewOptions()902 void ViewOptionsWidget::saveViewOptions()
903 {
904    m_settings->setValue("save_geometry", m_saveGeometryCheckBox->isChecked());
905    m_settings->setValue("save_dock_positions", m_saveDockPositionsCheckBox->isChecked());
906    m_settings->setValue("save_last_tab", m_saveLastTabCheckBox->isChecked());
907    m_settings->setValue("theme", m_mainwindow->getThemeString(static_cast<MainWindow::Theme>(m_themeComboBox->currentData(Qt::UserRole).toInt())));
908    m_settings->setValue("show_hidden_files", m_showHiddenFilesCheckBox->isChecked());
909    m_settings->setValue("highlight_color", m_highlightColor);
910    m_settings->setValue("suggest_loaded_core_first", m_suggestLoadedCoreFirstCheckBox->isChecked());
911 #if 0
912    m_settings->setValue("all_playlists_list_max_count", m_allPlaylistsListMaxCountSpinBox->value());
913    m_settings->setValue("all_playlists_grid_max_count", m_allPlaylistsGridMaxCountSpinBox->value());
914 #endif
915    m_settings->setValue("initial_playlist", m_startupPlaylistComboBox->currentData(Qt::UserRole).toString());
916    m_settings->setValue("thumbnail_cache_limit", m_thumbnailCacheSpinBox->value());
917    m_settings->setValue("thumbnail_max_size", m_thumbnailDropSizeSpinBox->value());
918 
919    if (!m_mainwindow->customThemeString().isEmpty())
920       m_settings->setValue("custom_theme", m_customThemePath);
921 
922 #if 0
923    m_mainwindow->setAllPlaylistsListMaxCount(m_allPlaylistsListMaxCountSpinBox->value());
924    m_mainwindow->setAllPlaylistsGridMaxCount(m_allPlaylistsGridMaxCountSpinBox->value());
925 #endif
926    m_mainwindow->setThumbnailCacheLimit(m_thumbnailCacheSpinBox->value());
927 }
928 
onAccepted()929 void ViewOptionsWidget::onAccepted()
930 {
931    saveViewOptions();
932 }
933 
onRejected()934 void ViewOptionsWidget::onRejected()
935 {
936    loadViewOptions();
937 }
938 
CoreOptionsDialog(QWidget * parent)939 CoreOptionsDialog::CoreOptionsDialog(QWidget *parent) :
940    QDialog(parent)
941    ,m_layout()
942    ,m_scrollArea()
943 {
944    setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CORE_OPTIONS));
945    setObjectName("coreOptionsDialog");
946 
947    resize(720, 480);
948 
949    QTimer::singleShot(0, this, SLOT(clearLayout()));
950 }
951 
~CoreOptionsDialog()952 CoreOptionsDialog::~CoreOptionsDialog()
953 {
954 }
955 
resizeEvent(QResizeEvent * event)956 void CoreOptionsDialog::resizeEvent(QResizeEvent *event)
957 {
958    QDialog::resizeEvent(event);
959 
960    if (!m_scrollArea)
961       return;
962 
963    m_scrollArea->resize(event->size());
964 
965    emit resized(event->size());
966 }
967 
closeEvent(QCloseEvent * event)968 void CoreOptionsDialog::closeEvent(QCloseEvent *event)
969 {
970    QDialog::closeEvent(event);
971 
972    emit closed();
973 }
974 
paintEvent(QPaintEvent * event)975 void CoreOptionsDialog::paintEvent(QPaintEvent *event)
976 {
977    QStyleOption o;
978    QPainter p;
979    o.initFrom(this);
980    p.begin(this);
981    style()->drawPrimitive(
982       QStyle::PE_Widget, &o, &p, this);
983    p.end();
984 
985    QDialog::paintEvent(event);
986 }
987 
clearLayout()988 void CoreOptionsDialog::clearLayout()
989 {
990    QWidget *widget = NULL;
991 
992    if (m_scrollArea)
993    {
994       foreach (QObject *obj, children())
995       {
996          obj->deleteLater();
997       }
998    }
999 
1000    m_layout = new QVBoxLayout();
1001 
1002    widget = new QWidget();
1003    widget->setLayout(m_layout);
1004    widget->setObjectName("coreOptionsWidget");
1005 
1006    m_scrollArea = new QScrollArea();
1007 
1008    m_scrollArea->setParent(this);
1009    m_scrollArea->setWidgetResizable(true);
1010    m_scrollArea->setWidget(widget);
1011    m_scrollArea->setObjectName("coreOptionsScrollArea");
1012    m_scrollArea->show();
1013 }
1014 
reload()1015 void CoreOptionsDialog::reload()
1016 {
1017    buildLayout();
1018 }
1019 
onSaveGameSpecificOptions()1020 void CoreOptionsDialog::onSaveGameSpecificOptions()
1021 {
1022 #ifdef HAVE_MENU
1023    bool refresh = false;
1024 #endif
1025 
1026    if (!core_options_create_override(true))
1027       QMessageBox::critical(this, msg_hash_to_str(MSG_ERROR), msg_hash_to_str(MSG_ERROR_SAVING_CORE_OPTIONS_FILE));
1028 
1029 #ifdef HAVE_MENU
1030    menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh);
1031    menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL);
1032 #endif
1033 }
1034 
onSaveFolderSpecificOptions()1035 void CoreOptionsDialog::onSaveFolderSpecificOptions()
1036 {
1037 #ifdef HAVE_MENU
1038    bool refresh = false;
1039 #endif
1040 
1041    if (!core_options_create_override(false))
1042       QMessageBox::critical(this, msg_hash_to_str(MSG_ERROR), msg_hash_to_str(MSG_ERROR_SAVING_CORE_OPTIONS_FILE));
1043 
1044 #ifdef HAVE_MENU
1045    menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh);
1046    menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL);
1047 #endif
1048 }
1049 
onCoreOptionComboBoxCurrentIndexChanged(int index)1050 void CoreOptionsDialog::onCoreOptionComboBoxCurrentIndexChanged(int index)
1051 {
1052    unsigned i, k;
1053    QString key, val;
1054    size_t          opts = 0;
1055    QComboBox *combo_box = qobject_cast<QComboBox*>(sender());
1056 
1057    if (!combo_box)
1058       return;
1059 
1060    key = combo_box->itemData(index, Qt::UserRole).toString();
1061    val = combo_box->itemText(index);
1062 
1063    if (rarch_ctl(RARCH_CTL_HAS_CORE_OPTIONS, NULL))
1064    {
1065       rarch_ctl(RARCH_CTL_GET_CORE_OPTION_SIZE, &opts);
1066 
1067       if (opts)
1068       {
1069          core_option_manager_t *coreopts = NULL;
1070 
1071          rarch_ctl(RARCH_CTL_CORE_OPTIONS_LIST_GET, &coreopts);
1072 
1073          if (coreopts)
1074          {
1075             for (i = 0; i < opts; i++)
1076             {
1077                QString optKey;
1078                struct core_option *option = static_cast<struct core_option*>(&coreopts->opts[i]);
1079 
1080                if (!option)
1081                   continue;
1082 
1083                optKey = option->key;
1084 
1085                if (key == optKey)
1086                {
1087                   for (k = 0; k < option->vals->size; k++)
1088                   {
1089                      QString str = option->vals->elems[k].data;
1090 
1091                      if (!str.isEmpty() && str == val)
1092                         core_option_manager_set_val(coreopts, i, k);
1093                   }
1094                }
1095             }
1096          }
1097       }
1098    }
1099 }
1100 
buildLayout()1101 void CoreOptionsDialog::buildLayout()
1102 {
1103    unsigned j, k;
1104    size_t opts = 0;
1105    QFormLayout    *form  = NULL;
1106    settings_t *settings  = config_get_ptr();
1107    bool has_core_options = rarch_ctl(RARCH_CTL_HAS_CORE_OPTIONS, NULL);
1108 
1109    clearLayout();
1110 
1111    if (has_core_options)
1112    {
1113       rarch_ctl(RARCH_CTL_GET_CORE_OPTION_SIZE, &opts);
1114 
1115       if (opts)
1116       {
1117          core_option_manager_t *coreopts = NULL;
1118 
1119          form = new QFormLayout();
1120 
1121          if (settings->bools.game_specific_options)
1122          {
1123             QString contentLabel;
1124             QString label;
1125             rarch_system_info_t *system = runloop_get_system_info();
1126 
1127             /* TODO/FIXME - why have this check here? system is not used */
1128             if (system)
1129                contentLabel = QFileInfo(path_get(RARCH_PATH_BASENAME)).completeBaseName();
1130 
1131             if (!contentLabel.isEmpty())
1132             {
1133                if (!rarch_ctl(RARCH_CTL_IS_GAME_OPTIONS_ACTIVE, NULL))
1134                   label = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_GAME_SPECIFIC_OPTIONS_CREATE);
1135                else
1136                   label = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_GAME_SPECIFIC_OPTIONS_IN_USE);
1137 
1138                if (!label.isEmpty())
1139                {
1140                   QHBoxLayout *gameOptionsLayout = new QHBoxLayout();
1141                   QPushButton *button = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SAVE), this);
1142 
1143                   connect(button, SIGNAL(clicked()), this, SLOT(onSaveGameSpecificOptions()));
1144 
1145                   gameOptionsLayout->addWidget(new QLabel(contentLabel, this));
1146                   gameOptionsLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Preferred));
1147                   gameOptionsLayout->addWidget(button);
1148 
1149                   form->addRow(label, gameOptionsLayout);
1150                }
1151             }
1152          }
1153 
1154          rarch_ctl(RARCH_CTL_CORE_OPTIONS_LIST_GET, &coreopts);
1155 
1156          if (coreopts)
1157          {
1158             QToolButton *resetAllButton = new QToolButton(this);
1159 
1160             resetAllButton->setDefaultAction(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_RESET_ALL), this));
1161             connect(resetAllButton, SIGNAL(clicked()), this, SLOT(onCoreOptionResetAllClicked()));
1162 
1163             for (j = 0; j < opts; j++)
1164             {
1165                QString desc               =
1166                   core_option_manager_get_desc(coreopts, j);
1167                QString val                =
1168                   core_option_manager_get_val(coreopts, j);
1169                QComboBox *combo_box       = NULL;
1170                QLabel *descLabel          = NULL;
1171                QHBoxLayout *comboLayout   = NULL;
1172                QToolButton *resetButton   = NULL;
1173                struct core_option *option = NULL;
1174 
1175                if (desc.isEmpty() || !coreopts->opts)
1176                   continue;
1177 
1178                option = static_cast<struct core_option*>(&coreopts->opts[j]);
1179 
1180                if (!option->vals || option->vals->size == 0)
1181                   continue;
1182 
1183                comboLayout = new QHBoxLayout();
1184                descLabel   = new QLabel(desc, this);
1185                combo_box   = new QComboBox(this);
1186                combo_box->setObjectName("coreOptionComboBox");
1187                resetButton = new QToolButton(this);
1188                resetButton->setObjectName("resetButton");
1189                resetButton->setDefaultAction(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_RESET), this));
1190                resetButton->setProperty("comboBox",
1191                      QVariant::fromValue(combo_box));
1192 
1193                connect(resetButton, SIGNAL(clicked()),
1194                      this, SLOT(onCoreOptionResetClicked()));
1195 
1196                if (!string_is_empty(option->info))
1197                {
1198                   char *new_info;
1199                   size_t new_info_len = strlen(option->info) + 1;
1200 
1201                   new_info = (char *)malloc(new_info_len);
1202                   if (!new_info)
1203                      return;
1204                   new_info[0] = '\0';
1205 
1206                   word_wrap(new_info, new_info_len, option->info, 50, 100, 0);
1207                   descLabel->setToolTip(new_info);
1208                   combo_box->setToolTip(new_info);
1209                   free(new_info);
1210                }
1211 
1212                for (k = 0; k < option->vals->size; k++)
1213                   combo_box->addItem(option->vals->elems[k].data, option->key);
1214 
1215                combo_box->setCurrentText(val);
1216                combo_box->setProperty("default_index",
1217                      static_cast<unsigned>(option->default_index));
1218 
1219                /* Only connect the signal after setting the default item */
1220                connect(combo_box, SIGNAL(currentIndexChanged(int)),
1221                      this,
1222                      SLOT(onCoreOptionComboBoxCurrentIndexChanged(int)));
1223 
1224                comboLayout->addWidget(combo_box);
1225                comboLayout->addWidget(resetButton);
1226 
1227                form->addRow(descLabel, comboLayout);
1228             }
1229 
1230             form->addRow(resetAllButton, new QWidget(this));
1231 
1232             m_layout->addLayout(form);
1233          }
1234       }
1235    }
1236 
1237    if (!opts)
1238    {
1239       QLabel *noParamsLabel = new QLabel(msg_hash_to_str(
1240                MENU_ENUM_LABEL_VALUE_NO_CORE_OPTIONS_AVAILABLE), this);
1241       noParamsLabel->setAlignment(Qt::AlignCenter);
1242 
1243       m_layout->addWidget(noParamsLabel);
1244    }
1245 
1246    m_layout->addItem(new QSpacerItem(20, 20,
1247             QSizePolicy::Minimum, QSizePolicy::Expanding));
1248 
1249    resize(width() + 1, height());
1250    show();
1251    resize(width() - 1, height());
1252 }
1253 
onCoreOptionResetClicked()1254 void CoreOptionsDialog::onCoreOptionResetClicked()
1255 {
1256    bool ok              = false;
1257    QToolButton *button  = qobject_cast<QToolButton*>(sender());
1258    QComboBox *combo_box = NULL;
1259    int default_index    = 0;
1260 
1261    if (!button)
1262       return;
1263 
1264    combo_box = qobject_cast<QComboBox*>(button->property("comboBox").value<QComboBox*>());
1265 
1266    if (!combo_box)
1267       return;
1268 
1269    default_index = combo_box->property("default_index").toInt(&ok);
1270 
1271    if (!ok)
1272       return;
1273 
1274    if (default_index >= 0 && default_index < combo_box->count())
1275       combo_box->setCurrentIndex(default_index);
1276 }
1277 
onCoreOptionResetAllClicked()1278 void CoreOptionsDialog::onCoreOptionResetAllClicked()
1279 {
1280    int i;
1281    QList<QComboBox*> combo_boxes = findChildren<QComboBox*>("coreOptionComboBox");
1282 
1283    for (i = 0; i < combo_boxes.count(); i++)
1284    {
1285       int   default_index  = 0;
1286       bool             ok  = false;
1287       QComboBox *combo_box = combo_boxes.at(i);
1288 
1289       if (!combo_box)
1290          continue;
1291 
1292       default_index        = combo_box->property("default_index").toInt(&ok);
1293 
1294       if (!ok)
1295          continue;
1296 
1297       if (default_index >= 0 && default_index < combo_box->count())
1298          combo_box->setCurrentIndex(default_index);
1299    }
1300 }
1301 
1302 #if defined(HAVE_MENU)
1303 #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
1304 enum
1305 {
1306    QT_SHADER_PRESET_GLOBAL = 0,
1307    QT_SHADER_PRESET_CORE,
1308    QT_SHADER_PRESET_PARENT,
1309    QT_SHADER_PRESET_GAME,
1310    QT_SHADER_PRESET_NORMAL
1311 };
1312 
ShaderPass(struct video_shader_pass * passToCopy)1313 ShaderPass::ShaderPass(struct video_shader_pass *passToCopy) :
1314    pass(NULL)
1315 {
1316    if (passToCopy)
1317    {
1318       pass = (struct video_shader_pass*)calloc(1, sizeof(*pass));
1319       memcpy(pass, passToCopy, sizeof(*pass));
1320    }
1321 }
1322 
~ShaderPass()1323 ShaderPass::~ShaderPass()
1324 {
1325    if (pass)
1326       free(pass);
1327 }
1328 
operator =(const ShaderPass & other)1329 ShaderPass& ShaderPass::operator=(const ShaderPass &other)
1330 {
1331    if (this != &other && other.pass)
1332    {
1333       pass = (struct video_shader_pass*)calloc(1, sizeof(*pass));
1334       memcpy(pass, other.pass, sizeof(*pass));
1335    }
1336 
1337    return *this;
1338 }
1339 
ShaderParamsDialog(QWidget * parent)1340 ShaderParamsDialog::ShaderParamsDialog(QWidget *parent) :
1341    QDialog(parent)
1342    ,m_layout()
1343    ,m_scrollArea()
1344 {
1345    setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SHADER_OPTIONS));
1346    setObjectName("shaderParamsDialog");
1347 
1348    resize(720, 480);
1349 
1350    QTimer::singleShot(0, this, SLOT(clearLayout()));
1351 }
1352 
~ShaderParamsDialog()1353 ShaderParamsDialog::~ShaderParamsDialog()
1354 {
1355 }
1356 
resizeEvent(QResizeEvent * event)1357 void ShaderParamsDialog::resizeEvent(QResizeEvent *event)
1358 {
1359    QDialog::resizeEvent(event);
1360 
1361    if (!m_scrollArea)
1362       return;
1363 
1364    m_scrollArea->resize(event->size());
1365 
1366    emit resized(event->size());
1367 }
1368 
closeEvent(QCloseEvent * event)1369 void ShaderParamsDialog::closeEvent(QCloseEvent *event)
1370 {
1371    QDialog::closeEvent(event);
1372 
1373    emit closed();
1374 }
1375 
paintEvent(QPaintEvent * event)1376 void ShaderParamsDialog::paintEvent(QPaintEvent *event)
1377 {
1378    QStyleOption o;
1379    QPainter p;
1380    o.initFrom(this);
1381    p.begin(this);
1382    style()->drawPrimitive(
1383       QStyle::PE_Widget, &o, &p, this);
1384    p.end();
1385 
1386    QDialog::paintEvent(event);
1387 }
1388 
getFilterLabel(unsigned filter)1389 QString ShaderParamsDialog::getFilterLabel(unsigned filter)
1390 {
1391    QString filterString;
1392 
1393    switch (filter)
1394    {
1395       case 0:
1396          filterString = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DONT_CARE);
1397          break;
1398       case 1:
1399          filterString = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_LINEAR);
1400          break;
1401       case 2:
1402          filterString = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NEAREST);
1403          break;
1404       default:
1405          break;
1406    }
1407 
1408    return filterString;
1409 }
1410 
clearLayout()1411 void ShaderParamsDialog::clearLayout()
1412 {
1413    QWidget *widget = NULL;
1414 
1415    if (m_scrollArea)
1416    {
1417       foreach (QObject *obj, children())
1418          delete obj;
1419    }
1420 
1421    m_layout = new QVBoxLayout();
1422 
1423    widget   = new QWidget();
1424    widget->setLayout(m_layout);
1425    widget->setObjectName("shaderParamsWidget");
1426 
1427    m_scrollArea = new QScrollArea();
1428 
1429    m_scrollArea->setParent(this);
1430    m_scrollArea->setWidgetResizable(true);
1431    m_scrollArea->setWidget(widget);
1432    m_scrollArea->setObjectName("shaderParamsScrollArea");
1433    m_scrollArea->show();
1434 }
1435 
getShaders(struct video_shader ** menu_shader,struct video_shader ** video_shader)1436 void ShaderParamsDialog::getShaders(struct video_shader **menu_shader, struct video_shader **video_shader)
1437 {
1438    video_shader_ctx_t shader_info = {0};
1439    struct video_shader    *shader = menu_shader_get();
1440 
1441    if (menu_shader)
1442    {
1443       if (shader)
1444          *menu_shader = shader;
1445       else
1446          *menu_shader = NULL;
1447    }
1448 
1449    if (video_shader)
1450    {
1451       if (shader)
1452          *video_shader = shader_info.data;
1453       else
1454          *video_shader = NULL;
1455    }
1456 
1457    if (video_shader)
1458    {
1459       if (!video_shader_driver_get_current_shader(&shader_info))
1460       {
1461          *video_shader = NULL;
1462          return;
1463       }
1464 
1465       if (!shader_info.data || shader_info.data->num_parameters > GFX_MAX_PARAMETERS)
1466       {
1467          *video_shader = NULL;
1468          return;
1469       }
1470 
1471       if (shader_info.data)
1472          *video_shader = shader_info.data;
1473       else
1474          *video_shader = NULL;
1475    }
1476 }
1477 
onFilterComboBoxIndexChanged(int)1478 void ShaderParamsDialog::onFilterComboBoxIndexChanged(int)
1479 {
1480    QVariant passVariant;
1481    QComboBox               *comboBox = qobject_cast<QComboBox*>(sender());
1482    int pass                          = 0;
1483    bool ok                           = false;
1484    struct video_shader *menu_shader  = NULL;
1485    struct video_shader *video_shader = NULL;
1486 
1487    getShaders(&menu_shader, &video_shader);
1488 
1489    if (!comboBox)
1490       return;
1491 
1492    passVariant = comboBox->property("pass");
1493 
1494    if (!passVariant.isValid())
1495       return;
1496 
1497    pass = passVariant.toInt(&ok);
1498 
1499    if (!ok)
1500       return;
1501 
1502    if (     menu_shader
1503          && (pass >= 0)
1504          && (pass < static_cast<int>(menu_shader->passes)))
1505    {
1506       QVariant data = comboBox->currentData();
1507 
1508       if (data.isValid())
1509       {
1510          unsigned filter = data.toUInt(&ok);
1511 
1512          if (ok)
1513          {
1514             if (menu_shader)
1515                menu_shader->pass[pass].filter = filter;
1516             if (video_shader)
1517                video_shader->pass[pass].filter = filter;
1518 
1519             video_shader->modified = true;
1520 
1521             command_event(CMD_EVENT_SHADERS_APPLY_CHANGES, NULL);
1522          }
1523       }
1524    }
1525 }
1526 
onScaleComboBoxIndexChanged(int)1527 void ShaderParamsDialog::onScaleComboBoxIndexChanged(int)
1528 {
1529    QVariant passVariant;
1530    QComboBox *comboBox               = qobject_cast<QComboBox*>(sender());
1531    int pass                          = 0;
1532    bool ok                           = false;
1533    struct video_shader *menu_shader  = NULL;
1534    struct video_shader *video_shader = NULL;
1535 
1536    getShaders(&menu_shader, &video_shader);
1537 
1538    if (!comboBox)
1539       return;
1540 
1541    passVariant = comboBox->property("pass");
1542 
1543    if (!passVariant.isValid())
1544       return;
1545 
1546    pass = passVariant.toInt(&ok);
1547 
1548    if (!ok)
1549       return;
1550 
1551    if (menu_shader && pass >= 0 && pass < static_cast<int>(menu_shader->passes))
1552    {
1553       QVariant data = comboBox->currentData();
1554 
1555       if (data.isValid())
1556       {
1557          unsigned scale = data.toUInt(&ok);
1558 
1559          if (ok)
1560          {
1561             if (menu_shader)
1562             {
1563                menu_shader->pass[pass].fbo.scale_x = scale;
1564                menu_shader->pass[pass].fbo.scale_y = scale;
1565                menu_shader->pass[pass].fbo.valid = scale;
1566             }
1567 
1568             if (video_shader)
1569             {
1570                video_shader->pass[pass].fbo.scale_x = scale;
1571                video_shader->pass[pass].fbo.scale_y = scale;
1572                video_shader->pass[pass].fbo.valid = scale;
1573             }
1574 
1575             video_shader->modified = true;
1576 
1577             command_event(CMD_EVENT_SHADERS_APPLY_CHANGES, NULL);
1578          }
1579       }
1580    }
1581 }
1582 
onShaderPassMoveDownClicked()1583 void ShaderParamsDialog::onShaderPassMoveDownClicked()
1584 {
1585    QVariant passVariant;
1586    bool ok                           = false;
1587    struct video_shader *menu_shader  = NULL;
1588    struct video_shader *video_shader = NULL;
1589    QToolButton *button               = qobject_cast<QToolButton*>(sender());
1590    int pass                          = 0;
1591 
1592    getShaders(&menu_shader, &video_shader);
1593 
1594    if (!button)
1595       return;
1596 
1597    passVariant                       = button->property("pass");
1598 
1599    if (!passVariant.isValid())
1600       return;
1601 
1602    pass = passVariant.toInt(&ok);
1603 
1604    if (!ok || pass < 0)
1605       return;
1606 
1607    if (video_shader)
1608    {
1609       ShaderPass tempPass;
1610       int i;
1611 
1612       if (pass >= static_cast<int>(video_shader->passes) - 1)
1613          return;
1614 
1615       for (i = 0; i < static_cast<int>(video_shader->num_parameters); i++)
1616       {
1617          struct video_shader_parameter *param = &video_shader->parameters[i];
1618 
1619          if (param->pass == pass)
1620             param->pass += 1;
1621          else if (param->pass == pass + 1)
1622             param->pass -= 1;
1623       }
1624 
1625       tempPass = ShaderPass(&video_shader->pass[pass]);
1626       memcpy(&video_shader->pass[pass], &video_shader->pass[pass + 1], sizeof(struct video_shader_pass));
1627       memcpy(&video_shader->pass[pass + 1], tempPass.pass, sizeof(struct video_shader_pass));
1628    }
1629 
1630    if (menu_shader)
1631    {
1632       ShaderPass tempPass;
1633       int i;
1634 
1635       if (pass >= static_cast<int>(menu_shader->passes) - 1)
1636          return;
1637 
1638       for (i = 0; i < static_cast<int>(menu_shader->num_parameters); i++)
1639       {
1640          struct video_shader_parameter *param = &menu_shader->parameters[i];
1641 
1642          if (param->pass == pass)
1643             param->pass += 1;
1644          else if (param->pass == pass + 1)
1645             param->pass -= 1;
1646       }
1647 
1648       tempPass = ShaderPass(&menu_shader->pass[pass]);
1649       memcpy(&menu_shader->pass[pass], &menu_shader->pass[pass + 1], sizeof(struct video_shader_pass));
1650       memcpy(&menu_shader->pass[pass + 1], tempPass.pass, sizeof(struct video_shader_pass));
1651    }
1652 
1653    menu_shader->modified = true;
1654 
1655    reload();
1656 }
1657 
onShaderPassMoveUpClicked()1658 void ShaderParamsDialog::onShaderPassMoveUpClicked()
1659 {
1660    QVariant passVariant;
1661    int pass                          = 0;
1662    bool ok                           = false;
1663    struct video_shader *menu_shader  = NULL;
1664    struct video_shader *video_shader = NULL;
1665    QToolButton *button               = qobject_cast<QToolButton*>(sender());
1666 
1667    getShaders(&menu_shader, &video_shader);
1668 
1669    if (!button)
1670       return;
1671 
1672    passVariant = button->property("pass");
1673 
1674    if (!passVariant.isValid())
1675       return;
1676 
1677    pass = passVariant.toInt(&ok);
1678 
1679    if (!ok || pass <= 0)
1680       return;
1681 
1682    if (video_shader)
1683    {
1684       ShaderPass tempPass;
1685       int i;
1686 
1687       if (pass > static_cast<int>(video_shader->passes) - 1)
1688          return;
1689 
1690       for (i = 0; i < static_cast<int>(video_shader->num_parameters); i++)
1691       {
1692          struct video_shader_parameter *param = &video_shader->parameters[i];
1693 
1694          if (param->pass == pass)
1695             param->pass -= 1;
1696          else if (param->pass == pass - 1)
1697             param->pass += 1;
1698       }
1699 
1700       tempPass = ShaderPass(&video_shader->pass[pass - 1]);
1701       memcpy(&video_shader->pass[pass - 1], &video_shader->pass[pass], sizeof(struct video_shader_pass));
1702       memcpy(&video_shader->pass[pass], tempPass.pass, sizeof(struct video_shader_pass));
1703    }
1704 
1705    if (menu_shader)
1706    {
1707       ShaderPass tempPass;
1708       int i;
1709 
1710       if (pass > static_cast<int>(menu_shader->passes) - 1)
1711          return;
1712 
1713       for (i = 0; i < static_cast<int>(menu_shader->num_parameters); i++)
1714       {
1715          struct video_shader_parameter *param = &menu_shader->parameters[i];
1716 
1717          if (param->pass == pass)
1718             param->pass -= 1;
1719          else if (param->pass == pass - 1)
1720             param->pass += 1;
1721       }
1722 
1723       tempPass = ShaderPass(&menu_shader->pass[pass - 1]);
1724       memcpy(&menu_shader->pass[pass - 1], &menu_shader->pass[pass], sizeof(struct video_shader_pass));
1725       memcpy(&menu_shader->pass[pass], tempPass.pass, sizeof(struct video_shader_pass));
1726    }
1727 
1728    menu_shader->modified = true;
1729 
1730    reload();
1731 }
1732 
onShaderLoadPresetClicked()1733 void ShaderParamsDialog::onShaderLoadPresetClicked()
1734 {
1735    QString path, filter;
1736    QByteArray pathArray;
1737    struct video_shader *menu_shader  = NULL;
1738    struct video_shader *video_shader = NULL;
1739    const char *pathData              = NULL;
1740    enum rarch_shader_type type       = RARCH_SHADER_NONE;
1741 #if !defined(HAVE_MENU)
1742    settings_t *settings              = config_get_ptr();
1743    const char *shader_preset_dir     = settings->paths.directory_video_shader;
1744 #else
1745    const char *shader_preset_dir     = NULL;
1746 
1747    menu_driver_get_last_shader_preset_path(&shader_preset_dir, NULL);
1748 #endif
1749 
1750    getShaders(&menu_shader, &video_shader);
1751 
1752    if (!menu_shader)
1753       return;
1754 
1755    filter.append("Shader Preset (");
1756 
1757    /* NOTE: Maybe we should have a way to get a list
1758     * of all shader types instead of hard-coding this? */
1759    if (video_shader_is_supported(RARCH_SHADER_CG))
1760    {
1761       filter.append(QLatin1Literal(" *"));
1762       filter.append(".cgp");
1763    }
1764 
1765    if (video_shader_is_supported(RARCH_SHADER_GLSL))
1766    {
1767       filter.append(QLatin1Literal(" *"));
1768       filter.append(".glslp");
1769    }
1770 
1771    if (video_shader_is_supported(RARCH_SHADER_SLANG))
1772    {
1773       filter.append(QLatin1Literal(" *"));
1774       filter.append(".slangp");
1775    }
1776 
1777    filter.append(")");
1778    path       = QFileDialog::getOpenFileName(
1779          this,
1780          msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET),
1781          shader_preset_dir, filter);
1782 
1783    if (path.isEmpty())
1784       return;
1785 
1786    pathArray  = path.toUtf8();
1787    pathData   = pathArray.constData();
1788    type       = video_shader_parse_type(pathData);
1789 
1790 #if defined(HAVE_MENU)
1791    /* Cache selected shader parent directory */
1792    menu_driver_set_last_shader_preset_path(pathData);
1793 #endif
1794 
1795    menu_shader_manager_set_preset(menu_shader, type, pathData, true);
1796 }
1797 
onShaderResetPass(int pass)1798 void ShaderParamsDialog::onShaderResetPass(int pass)
1799 {
1800    unsigned i;
1801    struct video_shader *menu_shader  = NULL;
1802    struct video_shader *video_shader = NULL;
1803 
1804    getShaders(&menu_shader, &video_shader);
1805 
1806    if (menu_shader)
1807    {
1808       for (i = 0; i < menu_shader->num_parameters; i++)
1809       {
1810          struct video_shader_parameter *param = &menu_shader->parameters[i];
1811 
1812          /* if pass < 0, reset all params,
1813           * otherwise only reset the selected pass */
1814          if (pass >= 0 && param->pass != pass)
1815             continue;
1816 
1817          param->current = param->initial;
1818       }
1819    }
1820 
1821    if (video_shader)
1822    {
1823       for (i = 0; i < video_shader->num_parameters; i++)
1824       {
1825          struct video_shader_parameter *param = &video_shader->parameters[i];
1826 
1827          /* if pass < 0, reset all params,
1828           * otherwise only reset the selected pass */
1829          if (pass >= 0 && param->pass != pass)
1830             continue;
1831 
1832          param->current = param->initial;
1833       }
1834 
1835       video_shader->modified = true;
1836    }
1837 
1838    reload();
1839 }
1840 
onShaderResetParameter(QString parameter)1841 void ShaderParamsDialog::onShaderResetParameter(QString parameter)
1842 {
1843    struct video_shader *menu_shader  = NULL;
1844    struct video_shader *video_shader = NULL;
1845 
1846    getShaders(&menu_shader, &video_shader);
1847 
1848    if (menu_shader)
1849    {
1850       int i;
1851       struct video_shader_parameter *param = NULL;
1852 
1853       for (i = 0; i < static_cast<int>(menu_shader->num_parameters); i++)
1854       {
1855          QString id = menu_shader->parameters[i].id;
1856 
1857          if (id == parameter)
1858             param = &menu_shader->parameters[i];
1859       }
1860 
1861       if (param)
1862          param->current = param->initial;
1863    }
1864 
1865    if (video_shader)
1866    {
1867       int i;
1868       struct video_shader_parameter *param = NULL;
1869 
1870       for (i = 0; i < static_cast<int>(video_shader->num_parameters); i++)
1871       {
1872          QString id = video_shader->parameters[i].id;
1873 
1874          if (id == parameter)
1875             param = &video_shader->parameters[i];
1876       }
1877 
1878       if (param)
1879          param->current = param->initial;
1880 
1881       video_shader->modified = true;
1882    }
1883 
1884    reload();
1885 }
1886 
onShaderResetAllPasses()1887 void ShaderParamsDialog::onShaderResetAllPasses()
1888 {
1889    onShaderResetPass(-1);
1890 }
1891 
onShaderAddPassClicked()1892 void ShaderParamsDialog::onShaderAddPassClicked()
1893 {
1894    QString path, filter;
1895    QByteArray pathArray;
1896    struct video_shader *menu_shader      = NULL;
1897    struct video_shader *video_shader     = NULL;
1898    struct video_shader_pass *shader_pass = NULL;
1899    const char *pathData                  = NULL;
1900 #if !defined(HAVE_MENU)
1901    settings_t *settings                  = config_get_ptr();
1902    const char *shader_pass_dir           = settings->paths.directory_video_shader;
1903 #else
1904    const char *shader_pass_dir           = NULL;
1905 
1906    menu_driver_get_last_shader_pass_path(&shader_pass_dir, NULL);
1907 #endif
1908 
1909    getShaders(&menu_shader, &video_shader);
1910 
1911    if (!menu_shader)
1912       return;
1913 
1914    filter.append("Shader (");
1915 
1916    /* NOTE: Maybe we should have a way to get a list
1917     * of all shader types instead of hard-coding this? */
1918    if (video_shader_is_supported(RARCH_SHADER_CG))
1919       filter.append(QLatin1Literal(" *.cg"));
1920 
1921    if (video_shader_is_supported(RARCH_SHADER_GLSL))
1922       filter.append(QLatin1Literal(" *.glsl"));
1923 
1924    if (video_shader_is_supported(RARCH_SHADER_SLANG))
1925       filter.append(QLatin1Literal(" *.slang"));
1926 
1927    filter.append(")");
1928 
1929    path = QFileDialog::getOpenFileName(
1930          this,
1931          msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET),
1932          shader_pass_dir, filter);
1933 
1934    if (path.isEmpty())
1935       return;
1936 
1937    /* Qt uses '/' as a directory separator regardless
1938     * of host platform. Have to convert to native separators,
1939     * or video_shader_resolve_parameters() will fail on
1940     * non-Linux platforms */
1941    path      = QDir::toNativeSeparators(path);
1942 
1943    pathArray = path.toUtf8();
1944    pathData  = pathArray.constData();
1945 
1946    if (menu_shader->passes < GFX_MAX_SHADERS)
1947       menu_shader->passes++;
1948    else
1949       return;
1950 
1951    menu_shader->modified = true;
1952    shader_pass           = &menu_shader->pass[menu_shader->passes - 1];
1953 
1954    if (!shader_pass)
1955       return;
1956 
1957    strlcpy(shader_pass->source.path,
1958          pathData,
1959          sizeof(shader_pass->source.path));
1960 
1961 #if defined(HAVE_MENU)
1962    /* Cache selected shader parent directory */
1963    menu_driver_set_last_shader_pass_path(pathData);
1964 #endif
1965 
1966    video_shader_resolve_parameters(menu_shader);
1967 
1968    command_event(CMD_EVENT_SHADERS_APPLY_CHANGES, NULL);
1969 }
1970 
onShaderSavePresetAsClicked()1971 void ShaderParamsDialog::onShaderSavePresetAsClicked()
1972 {
1973    QByteArray pathArray;
1974    const char *pathData              = NULL;
1975    settings_t *settings              = config_get_ptr();
1976    const char *path_dir_video_shader = settings->paths.directory_video_shader;
1977    QString path                      = QFileDialog::getSaveFileName(this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_AS), path_dir_video_shader);
1978 
1979    if (path.isEmpty())
1980       return;
1981 
1982    pathArray                         = path.toUtf8();
1983    pathData                          = pathArray.constData();
1984 
1985    operateShaderPreset(true, pathData, QT_SHADER_PRESET_NORMAL);
1986 }
1987 
1988 /** save or remove shader preset */
operateShaderPreset(bool save,const char * path,unsigned action_type)1989 void ShaderParamsDialog::operateShaderPreset(bool save, const char *path, unsigned action_type)
1990 {
1991    bool ret;
1992    enum auto_shader_type preset_type;
1993    settings_t              *settings = config_get_ptr();
1994    const char *path_dir_video_shader = settings->paths.directory_video_shader;
1995    const char *path_dir_menu_config  = settings->paths.directory_menu_config;
1996 
1997    switch (action_type)
1998    {
1999       case QT_SHADER_PRESET_GLOBAL:
2000          preset_type = SHADER_PRESET_GLOBAL;
2001          break;
2002       case QT_SHADER_PRESET_CORE:
2003          preset_type = SHADER_PRESET_CORE;
2004          break;
2005       case QT_SHADER_PRESET_PARENT:
2006          preset_type = SHADER_PRESET_PARENT;
2007          break;
2008       case QT_SHADER_PRESET_GAME:
2009          preset_type = SHADER_PRESET_GAME;
2010          break;
2011       case QT_SHADER_PRESET_NORMAL:
2012          break;
2013       default:
2014          return;
2015    }
2016 
2017    if (save)
2018    {
2019       if (action_type == QT_SHADER_PRESET_NORMAL)
2020          ret = menu_shader_manager_save_preset(
2021                menu_shader_get(),
2022                path,
2023                path_dir_video_shader,
2024                path_dir_menu_config,
2025                true);
2026       else
2027          ret = menu_shader_manager_save_auto_preset(
2028                menu_shader_get(),
2029                preset_type,
2030                path_dir_video_shader,
2031                path_dir_menu_config,
2032                true);
2033 
2034       if (ret)
2035          runloop_msg_queue_push(
2036                msg_hash_to_str(MSG_SHADER_PRESET_SAVED_SUCCESSFULLY),
2037                1, 100, true, NULL,
2038                MESSAGE_QUEUE_ICON_DEFAULT,
2039                MESSAGE_QUEUE_CATEGORY_INFO
2040                );
2041       else
2042          runloop_msg_queue_push(
2043                msg_hash_to_str(MSG_ERROR_SAVING_SHADER_PRESET),
2044                1, 100, true, NULL,
2045                MESSAGE_QUEUE_ICON_DEFAULT,
2046                MESSAGE_QUEUE_CATEGORY_ERROR
2047                );
2048    }
2049    else
2050    {
2051       if (action_type != QT_SHADER_PRESET_NORMAL &&
2052             menu_shader_manager_remove_auto_preset(preset_type,
2053                path_dir_video_shader,
2054                path_dir_menu_config))
2055       {
2056 #ifdef HAVE_MENU
2057          bool refresh = false;
2058 #endif
2059 
2060          runloop_msg_queue_push(
2061                msg_hash_to_str(MSG_SHADER_PRESET_REMOVED_SUCCESSFULLY),
2062                1, 100, true, NULL,
2063                MESSAGE_QUEUE_ICON_DEFAULT,
2064                MESSAGE_QUEUE_CATEGORY_INFO
2065                );
2066 
2067 #ifdef HAVE_MENU
2068          menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh);
2069 #endif
2070       }
2071       else
2072          runloop_msg_queue_push(
2073                msg_hash_to_str(MSG_ERROR_REMOVING_SHADER_PRESET),
2074                1, 100, true, NULL,
2075                MESSAGE_QUEUE_ICON_DEFAULT,
2076                MESSAGE_QUEUE_CATEGORY_ERROR
2077                );
2078    }
2079 }
2080 
onShaderSaveGlobalPresetClicked()2081 void ShaderParamsDialog::onShaderSaveGlobalPresetClicked()
2082 {
2083    operateShaderPreset(true, NULL, QT_SHADER_PRESET_GLOBAL);
2084 }
2085 
onShaderSaveCorePresetClicked()2086 void ShaderParamsDialog::onShaderSaveCorePresetClicked()
2087 {
2088    operateShaderPreset(true, NULL, QT_SHADER_PRESET_CORE);
2089 }
2090 
onShaderSaveParentPresetClicked()2091 void ShaderParamsDialog::onShaderSaveParentPresetClicked()
2092 {
2093    operateShaderPreset(true, NULL, QT_SHADER_PRESET_PARENT);
2094 }
2095 
onShaderSaveGamePresetClicked()2096 void ShaderParamsDialog::onShaderSaveGamePresetClicked()
2097 {
2098    operateShaderPreset(true, NULL, QT_SHADER_PRESET_GAME);
2099 }
2100 
onShaderRemoveGlobalPresetClicked()2101 void ShaderParamsDialog::onShaderRemoveGlobalPresetClicked()
2102 {
2103    operateShaderPreset(false, NULL, QT_SHADER_PRESET_GLOBAL);
2104 }
2105 
onShaderRemoveCorePresetClicked()2106 void ShaderParamsDialog::onShaderRemoveCorePresetClicked()
2107 {
2108    operateShaderPreset(false, NULL, QT_SHADER_PRESET_CORE);
2109 }
2110 
onShaderRemoveParentPresetClicked()2111 void ShaderParamsDialog::onShaderRemoveParentPresetClicked()
2112 {
2113    operateShaderPreset(false, NULL, QT_SHADER_PRESET_PARENT);
2114 }
2115 
onShaderRemoveGamePresetClicked()2116 void ShaderParamsDialog::onShaderRemoveGamePresetClicked()
2117 {
2118    operateShaderPreset(false, NULL, QT_SHADER_PRESET_GAME);
2119 }
2120 
onShaderRemoveAllPassesClicked()2121 void ShaderParamsDialog::onShaderRemoveAllPassesClicked()
2122 {
2123    struct video_shader *menu_shader  = NULL;
2124    struct video_shader *video_shader = NULL;
2125 
2126    getShaders(&menu_shader, &video_shader);
2127 
2128    if (!menu_shader)
2129       return;
2130 
2131    menu_shader->passes   = 0;
2132    menu_shader->modified = true;
2133 
2134    onShaderApplyClicked();
2135 }
2136 
onShaderRemovePassClicked()2137 void ShaderParamsDialog::onShaderRemovePassClicked()
2138 {
2139    QVariant passVariant;
2140    QAction                   *action = qobject_cast<QAction*>(sender());
2141    int pass                          = 0;
2142    bool ok                           = false;
2143 
2144    if (!action)
2145       return;
2146    passVariant = action->data();
2147 
2148    if (!passVariant.isValid())
2149       return;
2150 
2151    pass = passVariant.toInt(&ok);
2152 
2153    if (!ok)
2154       return;
2155 
2156    onShaderRemovePass(pass);
2157 }
2158 
onShaderRemovePass(int pass)2159 void ShaderParamsDialog::onShaderRemovePass(int pass)
2160 {
2161    int i;
2162    struct video_shader *menu_shader  = NULL;
2163    struct video_shader *video_shader = NULL;
2164 
2165    getShaders(&menu_shader, &video_shader);
2166 
2167    if (!menu_shader || menu_shader->passes == 0)
2168       return;
2169 
2170    if (pass < 0 || pass > static_cast<int>(menu_shader->passes))
2171       return;
2172 
2173    /* move selected pass to the bottom */
2174    for (i = pass; i < static_cast<int>(menu_shader->passes) - 1; i++)
2175       std::swap(menu_shader->pass[i], menu_shader->pass[i + 1]);
2176 
2177    menu_shader->passes--;
2178 
2179    menu_shader->modified = true;
2180 
2181    onShaderApplyClicked();
2182 }
2183 
onShaderApplyClicked()2184 void ShaderParamsDialog::onShaderApplyClicked()
2185 {
2186    command_event(CMD_EVENT_SHADERS_APPLY_CHANGES, NULL);
2187 }
2188 
updateRemovePresetButtonsState()2189 void ShaderParamsDialog::updateRemovePresetButtonsState()
2190 {
2191    settings_t      *settings         = config_get_ptr();
2192    const char *path_dir_video_shader = settings->paths.directory_video_shader;
2193    const char *path_dir_menu_config  = settings->paths.directory_menu_config;
2194 
2195    if (removeGlobalPresetAction)
2196       removeGlobalPresetAction->setEnabled(
2197             menu_shader_manager_auto_preset_exists(
2198                SHADER_PRESET_GLOBAL,
2199                path_dir_video_shader,
2200                path_dir_menu_config
2201                ));
2202    if (removeCorePresetAction)
2203       removeCorePresetAction->setEnabled(
2204             menu_shader_manager_auto_preset_exists(
2205                SHADER_PRESET_CORE,
2206                path_dir_video_shader,
2207                path_dir_menu_config
2208                ));
2209    if (removeParentPresetAction)
2210       removeParentPresetAction->setEnabled(
2211             menu_shader_manager_auto_preset_exists(
2212                SHADER_PRESET_PARENT,
2213                path_dir_video_shader,
2214                path_dir_menu_config
2215                ));
2216    if (removeGamePresetAction)
2217       removeGamePresetAction->setEnabled(
2218             menu_shader_manager_auto_preset_exists(
2219                SHADER_PRESET_GAME,
2220                path_dir_video_shader,
2221                path_dir_menu_config
2222                ));
2223 }
2224 
reload()2225 void ShaderParamsDialog::reload()
2226 {
2227    buildLayout();
2228 }
2229 
buildLayout()2230 void ShaderParamsDialog::buildLayout()
2231 {
2232    unsigned i;
2233    bool hasPasses                           = false;
2234 #if defined(HAVE_MENU)
2235    CheckableSettingsGroup *topSettingsGroup = NULL;
2236 #endif
2237    QPushButton *loadButton                  = NULL;
2238    QPushButton *saveButton                  = NULL;
2239    QPushButton *removeButton                = NULL;
2240    QPushButton *removePassButton            = NULL;
2241    QPushButton *applyButton                 = NULL;
2242    QHBoxLayout *topButtonLayout             = NULL;
2243    QMenu *loadMenu                          = NULL;
2244    QMenu *saveMenu                          = NULL;
2245    QMenu *removeMenu                        = NULL;
2246    QMenu *removePassMenu                    = NULL;
2247    struct video_shader *menu_shader         = NULL;
2248    struct video_shader *video_shader        = NULL;
2249    struct video_shader *avail_shader        = NULL;
2250    const char *shader_path                  = NULL;
2251 
2252    getShaders(&menu_shader, &video_shader);
2253 
2254    /* NOTE: For some reason, menu_shader_get() returns a COPY
2255     * of what get_current_shader() gives us.
2256     * And if you want to be able to change shader settings/parameters
2257     * from both the raster menu and
2258     * Qt at the same time... you must change BOTH or one will
2259     * overwrite the other.
2260     *
2261     * AND, during a context reset, video_shader will be NULL
2262     * but not menu_shader, so don't totally bail
2263     * just because video_shader is NULL.
2264     *
2265     * Someone please fix this mess.
2266     */
2267 
2268    if (video_shader)
2269    {
2270       avail_shader = video_shader;
2271 
2272       if (video_shader->passes == 0)
2273          setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SHADER_OPTIONS));
2274    }
2275    /* Normally we'd only use video_shader,
2276     * but the Vulkan driver returns a NULL shader when there
2277     * are zero passes, so just fall back to menu_shader.
2278     */
2279    else if (menu_shader)
2280    {
2281       avail_shader = menu_shader;
2282 
2283       if (menu_shader->passes == 0)
2284          setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SHADER_OPTIONS));
2285    }
2286    else
2287    {
2288       setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SHADER_OPTIONS));
2289 
2290       /* no shader is available yet, just keep retrying until it is */
2291       QTimer::singleShot(0, this, SLOT(buildLayout()));
2292       return;
2293    }
2294 
2295    clearLayout();
2296 
2297    /* Only check video_shader for the path, menu_shader seems stale...
2298     * e.g. if you remove all the shader passes,
2299     * it still has the old path in it, but video_shader does not
2300     */
2301    if (video_shader)
2302    {
2303       if (!string_is_empty(video_shader->path))
2304       {
2305          shader_path = video_shader->path;
2306          setWindowTitle(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CURRENT_SHADER)) + ": " + QFileInfo(shader_path).fileName());
2307       }
2308    }
2309    else if (menu_shader)
2310    {
2311       if (!string_is_empty(menu_shader->path))
2312       {
2313          shader_path = menu_shader->path;
2314          setWindowTitle(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CURRENT_SHADER)) + ": " + QFileInfo(shader_path).fileName());
2315       }
2316    }
2317    else
2318       setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SHADER_OPTIONS));
2319 
2320    loadButton       = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_LOAD), this);
2321    saveButton       = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SAVE), this);
2322    removeButton     = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_REMOVE), this);
2323    removePassButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_REMOVE_PASSES), this);
2324    applyButton      = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_APPLY), this);
2325 
2326    loadMenu         = new QMenu(loadButton);
2327 
2328    loadMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET), this, SLOT(onShaderLoadPresetClicked()));
2329    loadMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SHADER_ADD_PASS), this, SLOT(onShaderAddPassClicked()));
2330 
2331    loadButton->setMenu(loadMenu);
2332 
2333    saveMenu         = new QMenu(saveButton);
2334    saveMenu->addAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_AS)) + "...", this, SLOT(onShaderSavePresetAsClicked()));
2335    saveMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_GLOBAL), this, SLOT(onShaderSaveGlobalPresetClicked()));
2336    saveMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_CORE), this, SLOT(onShaderSaveCorePresetClicked()));
2337    saveMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_PARENT), this, SLOT(onShaderSaveParentPresetClicked()));
2338    saveMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_GAME), this, SLOT(onShaderSaveGamePresetClicked()));
2339 
2340    saveButton->setMenu(saveMenu);
2341 
2342    removeMenu = new QMenu(removeButton);
2343    removeGlobalPresetAction = removeMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_REMOVE_GLOBAL), this, SLOT(onShaderRemoveGlobalPresetClicked()));
2344    removeCorePresetAction   = removeMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_REMOVE_CORE),   this, SLOT(onShaderRemoveCorePresetClicked()));
2345    removeParentPresetAction = removeMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_REMOVE_PARENT), this, SLOT(onShaderRemoveParentPresetClicked()));
2346    removeGamePresetAction   = removeMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_REMOVE_GAME),   this, SLOT(onShaderRemoveGamePresetClicked()));
2347 
2348    removeButton->setMenu(removeMenu);
2349 
2350    connect(removeMenu, SIGNAL(aboutToShow()), this, SLOT(updateRemovePresetButtonsState()));
2351 
2352    removePassMenu = new QMenu(removeButton);
2353 
2354    /* When there are no passes, at least on first startup, it seems video_shader erroneously shows 1 pass, with an empty source file.
2355     * So we use menu_shader instead for that.
2356     */
2357    if (menu_shader)
2358    {
2359       for (i = 0; i < menu_shader->passes; i++)
2360       {
2361          QFileInfo fileInfo(menu_shader->pass[i].source.path);
2362          QString shaderBasename = fileInfo.completeBaseName();
2363          QAction        *action = removePassMenu->addAction(shaderBasename, this, SLOT(onShaderRemovePassClicked()));
2364 
2365          action->setData(i);
2366       }
2367    }
2368 
2369    removePassMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SHADER_CLEAR_ALL_PASSES), this, SLOT(onShaderRemoveAllPassesClicked()));
2370 
2371    removePassButton->setMenu(removePassMenu);
2372 
2373    connect(applyButton, SIGNAL(clicked()), this, SLOT(onShaderApplyClicked()));
2374 
2375 #if defined(HAVE_MENU)
2376    topSettingsGroup = new CheckableSettingsGroup(MENU_ENUM_LABEL_VIDEO_SHADERS_ENABLE);
2377    topSettingsGroup->add(MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_REFERENCE);
2378    topSettingsGroup->add(MENU_ENUM_LABEL_SHADER_WATCH_FOR_CHANGES);
2379    topSettingsGroup->add(MENU_ENUM_LABEL_VIDEO_SHADER_REMEMBER_LAST_DIR);
2380 #endif
2381 
2382    topButtonLayout = new QHBoxLayout();
2383    topButtonLayout->addWidget(loadButton);
2384    topButtonLayout->addWidget(saveButton);
2385    topButtonLayout->addWidget(removeButton);
2386    topButtonLayout->addWidget(removePassButton);
2387    topButtonLayout->addWidget(applyButton);
2388 
2389 #if defined(HAVE_MENU)
2390    m_layout->addWidget(topSettingsGroup);
2391 #endif
2392    m_layout->addLayout(topButtonLayout);
2393 
2394    /* NOTE: We assume that parameters are always grouped in order by the pass number, e.g., all parameters for pass 0 come first, then params for pass 1, etc. */
2395    for (i = 0; avail_shader && i < avail_shader->passes; i++)
2396    {
2397       QFormLayout                  *form = NULL;
2398       QGroupBox                *groupBox = NULL;
2399       QFileInfo fileInfo(avail_shader->pass[i].source.path);
2400       QString             shaderBasename = fileInfo.completeBaseName();
2401       QHBoxLayout *filterScaleHBoxLayout = NULL;
2402       QComboBox          *filterComboBox = new QComboBox(this);
2403       QComboBox           *scaleComboBox = new QComboBox(this);
2404       QToolButton        *moveDownButton = NULL;
2405       QToolButton          *moveUpButton = NULL;
2406       unsigned                         j = 0;
2407 
2408       /* Sometimes video_shader shows 1 pass with no source file, when there are really 0 passes. */
2409       if (shaderBasename.isEmpty())
2410          continue;
2411 
2412       hasPasses                          = true;
2413 
2414       filterComboBox->setProperty("pass", i);
2415       scaleComboBox->setProperty("pass", i);
2416 
2417       moveDownButton                     = new QToolButton(this);
2418       moveDownButton->setText("↓");
2419       moveDownButton->setProperty("pass", i);
2420 
2421       moveUpButton                       = new QToolButton(this);
2422       moveUpButton->setText("↑");
2423       moveUpButton->setProperty("pass", i);
2424 
2425       /* Can't move down if we're already at the bottom. */
2426       if (i < avail_shader->passes - 1)
2427          connect(moveDownButton, SIGNAL(clicked()),
2428                this, SLOT(onShaderPassMoveDownClicked()));
2429       else
2430          moveDownButton->setDisabled(true);
2431 
2432       /* Can't move up if we're already at the top. */
2433       if (i > 0)
2434          connect(moveUpButton, SIGNAL(clicked()),
2435                this, SLOT(onShaderPassMoveUpClicked()));
2436       else
2437          moveUpButton->setDisabled(true);
2438 
2439       for (;;)
2440       {
2441          QString filterLabel = getFilterLabel(j);
2442 
2443          if (filterLabel.isEmpty())
2444             break;
2445 
2446          if (j == 0)
2447             filterLabel = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DONT_CARE);
2448 
2449          filterComboBox->addItem(filterLabel, j);
2450 
2451          j++;
2452       }
2453 
2454       for (j = 0; j < 7; j++)
2455       {
2456          QString label;
2457 
2458          if (j == 0)
2459             label = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DONT_CARE);
2460          else
2461             label = QString::number(j) + "x";
2462 
2463          scaleComboBox->addItem(label, j);
2464       }
2465 
2466       filterComboBox->setCurrentIndex(static_cast<int>(avail_shader->pass[i].filter));
2467       scaleComboBox->setCurrentIndex(static_cast<int>(avail_shader->pass[i].fbo.scale_x));
2468 
2469       /* connect the signals only after the initial index is set */
2470       connect(filterComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onFilterComboBoxIndexChanged(int)));
2471       connect(scaleComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onScaleComboBoxIndexChanged(int)));
2472 
2473       form     = new QFormLayout();
2474       groupBox = new QGroupBox(shaderBasename);
2475       groupBox->setLayout(form);
2476       groupBox->setProperty("pass", i);
2477       groupBox->setContextMenuPolicy(Qt::CustomContextMenu);
2478 
2479       connect(groupBox, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(onGroupBoxContextMenuRequested(const QPoint&)));
2480 
2481       m_layout->addWidget(groupBox);
2482 
2483       filterScaleHBoxLayout = new QHBoxLayout();
2484       filterScaleHBoxLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Preferred));
2485       filterScaleHBoxLayout->addWidget(new QLabel(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_FILTER)) + ":", this));
2486       filterScaleHBoxLayout->addWidget(filterComboBox);
2487       filterScaleHBoxLayout->addWidget(new QLabel(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SCALE)) + ":", this));
2488       filterScaleHBoxLayout->addWidget(scaleComboBox);
2489       filterScaleHBoxLayout->addSpacerItem(new QSpacerItem(20, 0, QSizePolicy::Preferred, QSizePolicy::Preferred));
2490 
2491       if (moveUpButton)
2492          filterScaleHBoxLayout->addWidget(moveUpButton);
2493 
2494       if (moveDownButton)
2495          filterScaleHBoxLayout->addWidget(moveDownButton);
2496 
2497       form->addRow("", filterScaleHBoxLayout);
2498 
2499       for (j = 0; j < avail_shader->num_parameters; j++)
2500       {
2501          struct video_shader_parameter *param = &avail_shader->parameters[j];
2502 
2503          if (param->pass != static_cast<int>(i))
2504             continue;
2505 
2506          addShaderParam(param, form);
2507       }
2508    }
2509 
2510    if (!hasPasses)
2511    {
2512       QLabel *noParamsLabel = new QLabel(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SHADER_NO_PASSES), this);
2513       noParamsLabel->setAlignment(Qt::AlignCenter);
2514 
2515       m_layout->addWidget(noParamsLabel);
2516    }
2517 
2518    m_layout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
2519 
2520    /* Why is this required?? The layout is corrupt without both resizes. */
2521    resize(width() + 1, height());
2522    show();
2523    resize(width() - 1, height());
2524 }
2525 
onParameterLabelContextMenuRequested(const QPoint &)2526 void ShaderParamsDialog::onParameterLabelContextMenuRequested(const QPoint&)
2527 {
2528    QVariant paramVariant;
2529    QString parameter;
2530    QPointer<QAction> action;
2531    QList<QAction*> actions;
2532    QScopedPointer<QAction> resetParamAction;
2533    QLabel *label = qobject_cast<QLabel*>(sender());
2534 
2535    if (!label)
2536       return;
2537 
2538    paramVariant  = label->property("parameter");
2539 
2540    if (!paramVariant.isValid())
2541       return;
2542 
2543    parameter     = paramVariant.toString();
2544 
2545    resetParamAction.reset(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_RESET_PARAMETER), 0));
2546 
2547    actions.append(resetParamAction.data());
2548 
2549    action        = QMenu::exec(actions, QCursor::pos(), NULL, label);
2550 
2551    if (!action)
2552       return;
2553 
2554    if (action == resetParamAction.data())
2555       onShaderResetParameter(parameter);
2556 }
2557 
onGroupBoxContextMenuRequested(const QPoint &)2558 void ShaderParamsDialog::onGroupBoxContextMenuRequested(const QPoint&)
2559 {
2560    QPointer<QAction> action;
2561    QList<QAction*> actions;
2562    QScopedPointer<QAction> resetPassAction;
2563    QScopedPointer<QAction> resetAllPassesAction;
2564    QVariant passVariant;
2565    int pass            = 0;
2566    bool ok             = false;
2567    QGroupBox *groupBox = qobject_cast<QGroupBox*>(sender());
2568 
2569    if (!groupBox)
2570       return;
2571 
2572    passVariant         = groupBox->property("pass");
2573 
2574    if (!passVariant.isValid())
2575       return;
2576 
2577    pass = passVariant.toInt(&ok);
2578 
2579    if (!ok)
2580       return;
2581 
2582    resetPassAction.reset(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_RESET_PASS), 0));
2583    resetAllPassesAction.reset(new QAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_RESET_ALL_PASSES), 0));
2584 
2585    actions.append(resetPassAction.data());
2586    actions.append(resetAllPassesAction.data());
2587 
2588    action              = QMenu::exec(actions, QCursor::pos(), NULL, groupBox);
2589 
2590    if (!action)
2591       return;
2592 
2593    if (action == resetPassAction.data())
2594       onShaderResetPass(pass);
2595    else if (action == resetAllPassesAction.data())
2596       onShaderResetAllPasses();
2597 }
2598 
addShaderParam(struct video_shader_parameter * param,QFormLayout * form)2599 void ShaderParamsDialog::addShaderParam(struct video_shader_parameter *param, QFormLayout *form)
2600 {
2601    QString      desc = param->desc;
2602    QString parameter = param->id;
2603    QLabel     *label = new QLabel(desc);
2604 
2605    label->setProperty("parameter", parameter);
2606    label->setContextMenuPolicy(Qt::CustomContextMenu);
2607 
2608    connect(label, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(onParameterLabelContextMenuRequested(const QPoint&)));
2609 
2610    if ((param->minimum == 0.0)
2611          && (param->maximum
2612             == (param->minimum
2613                + param->step)))
2614    {
2615       /* option is basically a bool, so use a checkbox */
2616       QCheckBox *checkBox = new QCheckBox(this);
2617       checkBox->setChecked(param->current == param->maximum ? true : false);
2618       checkBox->setProperty("param", parameter);
2619 
2620       connect(checkBox, SIGNAL(clicked()), this, SLOT(onShaderParamCheckBoxClicked()));
2621 
2622       form->addRow(label, checkBox);
2623    }
2624    else
2625    {
2626       QDoubleSpinBox *doubleSpinBox = NULL;
2627       QSpinBox *spinBox             = NULL;
2628       QHBoxLayout *box              = new QHBoxLayout();
2629       QSlider *slider               = new QSlider(Qt::Horizontal, this);
2630       double value                  = MainWindow::lerp(
2631             param->minimum, param->maximum, 0, 100, param->current);
2632       double intpart                = 0;
2633       bool stepIsFractional         = modf(param->step, &intpart);
2634 
2635       slider->setRange(0, 100);
2636       slider->setSingleStep(1);
2637       slider->setValue(value);
2638       slider->setProperty("param", parameter);
2639 
2640       connect(slider, SIGNAL(valueChanged(int)),
2641             this, SLOT(onShaderParamSliderValueChanged(int)));
2642 
2643       box->addWidget(slider);
2644 
2645       if (stepIsFractional)
2646       {
2647          doubleSpinBox = new QDoubleSpinBox(this);
2648          doubleSpinBox->setRange(param->minimum, param->maximum);
2649          doubleSpinBox->setSingleStep(param->step);
2650          doubleSpinBox->setValue(param->current);
2651          doubleSpinBox->setProperty("slider", QVariant::fromValue(slider));
2652          slider->setProperty("doubleSpinBox", QVariant::fromValue(doubleSpinBox));
2653 
2654          connect(doubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onShaderParamDoubleSpinBoxValueChanged(double)));
2655 
2656          box->addWidget(doubleSpinBox);
2657       }
2658       else
2659       {
2660          spinBox = new QSpinBox(this);
2661          spinBox->setRange(param->minimum, param->maximum);
2662          spinBox->setSingleStep(param->step);
2663          spinBox->setValue(param->current);
2664          spinBox->setProperty("slider", QVariant::fromValue(slider));
2665          slider->setProperty("spinBox", QVariant::fromValue(spinBox));
2666 
2667          connect(spinBox, SIGNAL(valueChanged(int)), this, SLOT(onShaderParamSpinBoxValueChanged(int)));
2668 
2669          box->addWidget(spinBox);
2670       }
2671 
2672       form->addRow(label, box);
2673    }
2674 }
2675 
onShaderParamCheckBoxClicked()2676 void ShaderParamsDialog::onShaderParamCheckBoxClicked()
2677 {
2678    QVariant paramVariant;
2679    QCheckBox *checkBox               = qobject_cast<QCheckBox*>(sender());
2680    struct video_shader *menu_shader  = NULL;
2681    struct video_shader *video_shader = NULL;
2682 
2683    getShaders(&menu_shader, &video_shader);
2684 
2685    if (!checkBox)
2686       return;
2687 
2688    if (menu_shader && menu_shader->passes == 0)
2689       return;
2690 
2691    paramVariant = checkBox->property("param");
2692 
2693    if (paramVariant.isValid())
2694    {
2695       QString parameter = paramVariant.toString();
2696 
2697       if (menu_shader)
2698       {
2699          int i;
2700          struct video_shader_parameter *param = NULL;
2701 
2702          for (i = 0; i < static_cast<int>(menu_shader->num_parameters); i++)
2703          {
2704             QString id = menu_shader->parameters[i].id;
2705 
2706             if (id == parameter)
2707                param = &menu_shader->parameters[i];
2708          }
2709 
2710          if (param)
2711             param->current = (checkBox->isChecked() ? param->maximum : param->minimum);
2712       }
2713 
2714       if (video_shader)
2715       {
2716          int i;
2717          struct video_shader_parameter *param = NULL;
2718 
2719          for (i = 0; i < static_cast<int>(video_shader->num_parameters); i++)
2720          {
2721             QString id = video_shader->parameters[i].id;
2722 
2723             if (id == parameter)
2724                param = &video_shader->parameters[i];
2725          }
2726 
2727          if (param)
2728             param->current = (checkBox->isChecked() ? param->maximum : param->minimum);
2729       }
2730 
2731       video_shader->modified = true;
2732    }
2733 }
2734 
onShaderParamSliderValueChanged(int)2735 void ShaderParamsDialog::onShaderParamSliderValueChanged(int)
2736 {
2737    QVariant spinBoxVariant;
2738    QVariant paramVariant;
2739    QSlider *slider = qobject_cast<QSlider*>(sender());
2740    struct video_shader *menu_shader  = NULL;
2741    struct video_shader *video_shader = NULL;
2742    double                   newValue = 0.0;
2743 
2744    getShaders(&menu_shader, &video_shader);
2745 
2746    if (!slider)
2747       return;
2748 
2749    spinBoxVariant = slider->property("spinBox");
2750    paramVariant   = slider->property("param");
2751 
2752    if (paramVariant.isValid())
2753    {
2754       QString parameter = paramVariant.toString();
2755 
2756       if (menu_shader)
2757       {
2758          int i;
2759          struct video_shader_parameter *param = NULL;
2760 
2761          for (i = 0; i < static_cast<int>(menu_shader->num_parameters); i++)
2762          {
2763             QString id = menu_shader->parameters[i].id;
2764 
2765             if (id == parameter)
2766                param = &menu_shader->parameters[i];
2767          }
2768 
2769          if (param)
2770          {
2771             newValue = MainWindow::lerp(0, 100, param->minimum, param->maximum, slider->value());
2772             newValue = round(newValue / param->step) * param->step;
2773             param->current = newValue;
2774          }
2775       }
2776 
2777       if (video_shader)
2778       {
2779          int i;
2780          struct video_shader_parameter *param = NULL;
2781 
2782          for (i = 0; i < static_cast<int>(video_shader->num_parameters); i++)
2783          {
2784             QString id = video_shader->parameters[i].id;
2785 
2786             if (id == parameter)
2787                param = &video_shader->parameters[i];
2788          }
2789 
2790          if (param)
2791          {
2792             newValue = MainWindow::lerp(0, 100, param->minimum, param->maximum, slider->value());
2793             newValue = round(newValue / param->step) * param->step;
2794             param->current = newValue;
2795          }
2796 
2797          video_shader->modified = true;
2798       }
2799 
2800    }
2801 
2802    if (spinBoxVariant.isValid())
2803    {
2804       QSpinBox *spinBox = spinBoxVariant.value<QSpinBox*>();
2805 
2806       if (!spinBox)
2807          return;
2808 
2809       spinBox->blockSignals(true);
2810       spinBox->setValue(newValue);
2811       spinBox->blockSignals(false);
2812    }
2813    else
2814    {
2815       QVariant doubleSpinBoxVariant = slider->property("doubleSpinBox");
2816       QDoubleSpinBox *doubleSpinBox = doubleSpinBoxVariant.value<QDoubleSpinBox*>();
2817 
2818       if (!doubleSpinBox)
2819          return;
2820 
2821       doubleSpinBox->blockSignals(true);
2822       doubleSpinBox->setValue(newValue);
2823       doubleSpinBox->blockSignals(false);
2824    }
2825 }
2826 
onShaderParamSpinBoxValueChanged(int value)2827 void ShaderParamsDialog::onShaderParamSpinBoxValueChanged(int value)
2828 {
2829    QVariant sliderVariant;
2830    QVariant paramVariant;
2831    QSpinBox                 *spinBox = qobject_cast<QSpinBox*>(sender());
2832    QSlider                   *slider = NULL;
2833    struct video_shader  *menu_shader = NULL;
2834    struct video_shader *video_shader = NULL;
2835 
2836    getShaders(&menu_shader, &video_shader);
2837 
2838    if (!spinBox)
2839       return;
2840 
2841    sliderVariant = spinBox->property("slider");
2842 
2843    if (!sliderVariant.isValid())
2844       return;
2845 
2846    slider = sliderVariant.value<QSlider*>();
2847 
2848    if (!slider)
2849       return;
2850 
2851    paramVariant = slider->property("param");
2852 
2853    if (paramVariant.isValid())
2854    {
2855       QString parameter = paramVariant.toString();
2856       double   newValue = 0.0;
2857 
2858       if (menu_shader)
2859       {
2860          int i;
2861          struct video_shader_parameter *param = NULL;
2862 
2863          for (i = 0; i < static_cast<int>(menu_shader->num_parameters); i++)
2864          {
2865             QString id = menu_shader->parameters[i].id;
2866 
2867             if (id == parameter)
2868                param = &menu_shader->parameters[i];
2869          }
2870 
2871          if (param)
2872          {
2873             param->current = value;
2874             newValue       = MainWindow::lerp(
2875                   param->minimum, param->maximum, 0, 100, param->current);
2876             slider->blockSignals(true);
2877             slider->setValue(newValue);
2878             slider->blockSignals(false);
2879          }
2880       }
2881 
2882       if (video_shader)
2883       {
2884          int i;
2885          struct video_shader_parameter *param = NULL;
2886 
2887          for (i = 0; i < static_cast<int>(video_shader->num_parameters); i++)
2888          {
2889             QString id = video_shader->parameters[i].id;
2890 
2891             if (id == parameter)
2892                param = &video_shader->parameters[i];
2893          }
2894 
2895          if (param)
2896          {
2897             param->current = value;
2898             newValue       = MainWindow::lerp(
2899                   param->minimum, param->maximum, 0, 100, param->current);
2900             slider->blockSignals(true);
2901             slider->setValue(newValue);
2902             slider->blockSignals(false);
2903          }
2904 
2905          video_shader->modified = true;
2906       }
2907    }
2908 }
2909 
onShaderParamDoubleSpinBoxValueChanged(double value)2910 void ShaderParamsDialog::onShaderParamDoubleSpinBoxValueChanged(double value)
2911 {
2912    QVariant sliderVariant;
2913    QVariant paramVariant;
2914    QSlider                   *slider = NULL;
2915    QDoubleSpinBox     *doubleSpinBox = qobject_cast<QDoubleSpinBox*>(sender());
2916    struct video_shader  *menu_shader = NULL;
2917    struct video_shader *video_shader = NULL;
2918 
2919    getShaders(&menu_shader, &video_shader);
2920 
2921    if (!doubleSpinBox)
2922       return;
2923 
2924    sliderVariant = doubleSpinBox->property("slider");
2925 
2926    if (!sliderVariant.isValid())
2927       return;
2928 
2929    slider = sliderVariant.value<QSlider*>();
2930 
2931    if (!slider)
2932       return;
2933 
2934    paramVariant = slider->property("param");
2935 
2936    if (paramVariant.isValid())
2937    {
2938       QString parameter = paramVariant.toString();
2939       double newValue   = 0.0;
2940 
2941       if (menu_shader)
2942       {
2943          int i;
2944          struct video_shader_parameter *param = NULL;
2945 
2946          for (i = 0; i < static_cast<int>(menu_shader->num_parameters); i++)
2947          {
2948             QString id = menu_shader->parameters[i].id;
2949 
2950             if (id == parameter)
2951                param = &menu_shader->parameters[i];
2952          }
2953 
2954          if (param)
2955          {
2956             param->current = value;
2957             newValue = MainWindow::lerp(
2958                   param->minimum, param->maximum, 0, 100, param->current);
2959             slider->blockSignals(true);
2960             slider->setValue(newValue);
2961             slider->blockSignals(false);
2962          }
2963       }
2964 
2965       if (video_shader)
2966       {
2967          int i;
2968          struct video_shader_parameter *param = NULL;
2969 
2970          for (i = 0; i < static_cast<int>(video_shader->num_parameters); i++)
2971          {
2972             QString id = video_shader->parameters[i].id;
2973 
2974             if (id == parameter)
2975                param = &video_shader->parameters[i];
2976          }
2977 
2978          if (param)
2979          {
2980             param->current = value;
2981             newValue       = MainWindow::lerp(
2982                   param->minimum, param->maximum, 0, 100, param->current);
2983             slider->blockSignals(true);
2984             slider->setValue(newValue);
2985             slider->blockSignals(false);
2986          }
2987 
2988          video_shader->modified = true;
2989       }
2990    }
2991 }
2992 #endif
2993 #endif
2994