summaryrefslogtreecommitdiff
path: root/Source/Core/DolphinQt/QtUtils/QtUtils.cpp
blob: 1283b17687c4d56708ea6a507a76c98e51f49aaa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// Copyright 2024 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "DolphinQt/QtUtils/QtUtils.h"

#include <QDateTimeEdit>
#include <QDesktopServices>
#include <QDir>
#include <QHBoxLayout>
#include <QLabel>
#include <QProcess>
#include <QScreen>

#if defined(QT_DBUS_LIB)
#include <QDBusConnectionInterface>
#include <QDBusInterface>
#endif

#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"

namespace
{

void ShowFolderOfFile(std::string_view file_path)
{
  // Remove everything after the last separator in the game's path, resulting in the parent
  // directory path with a trailing separator. Keeping the trailing separator prevents Windows from
  // mistakenly opening a .bat or .exe file in the grandparent folder when that file has the same
  // base name as the folder (See https://bugs.dolphin-emu.org/issues/12411).
  std::string parent_directory_path;
  SplitPath(file_path, &parent_directory_path, nullptr, nullptr);
  if (parent_directory_path.empty())
  {
    return;
  }

  const QUrl url = QUrl::fromLocalFile(QString::fromStdString(parent_directory_path));
  QDesktopServices::openUrl(url);
}

}  // namespace

namespace QtUtils
{

void ShowFourDigitYear(QDateTimeEdit* widget)
{
  if (!widget->displayFormat().contains(QStringLiteral("yyyy")))
  {
    // Always show the full year, no matter what the locale specifies. Otherwise, two-digit years
    // will always be interpreted as in the 21st century.
    widget->setDisplayFormat(
        widget->displayFormat().replace(QStringLiteral("yy"), QStringLiteral("yyyy")));
  }
}

QWidget* CreateIconWarning(QWidget* parent, QStyle::StandardPixmap standard_pixmap, QLabel* label)
{
  const auto size = QFontMetrics(parent->font()).height() * 5 / 4;

  auto* const icon = new QLabel{};
  icon->setPixmap(parent->style()->standardIcon(standard_pixmap).pixmap(size, size));

  auto* const widget = new QWidget;
  auto* const layout = new QHBoxLayout{widget};
  layout->addWidget(icon);
  layout->addWidget(label, 1);
  return widget;
}

void AdjustSizeWithinScreen(QWidget* widget)
{
  const auto screen_size = widget->screen()->availableSize();

  const auto adj_screen_size = screen_size * 9 / 10;

  widget->resize(widget->sizeHint().boundedTo(adj_screen_size));

  CenterOnParentWindow(widget);
}

void CenterOnParentWindow(QWidget* const widget)
{
  // Find the top-level window.
  const QWidget* const parent_widget{widget->parentWidget()};
  if (!parent_widget)
    return;
  const QWidget* const window{parent_widget->window()};

  // Calculate position based on the widgets' size and position.
  const QRect window_geometry{window->geometry()};
  const QSize window_size{window_geometry.size()};
  const QPoint window_pos{window_geometry.topLeft()};
  const QRect geometry{widget->geometry()};
  const QSize size{geometry.size()};
  const QPoint offset{(window_size.width() - size.width()) / 2,
                      (window_size.height() - size.height()) / 2};
  const QPoint pos{window_pos + offset};

  widget->setGeometry(QRect(pos, size));
}

void ShowFileInFolder(std::string_view file_path)
{
#if defined(QT_DBUS_LIB)
  QDBusInterface dbus{QString::fromLatin1("org.freedesktop.DBus"),
                      QString::fromLatin1("/org/freedesktop/DBus"),
                      QString::fromLatin1("org.freedesktop.DBus")};

  dbus.call(QString::fromLatin1("StartServiceByName"),
            QString::fromLatin1("org.freedesktop.FileManager1"), 0u);

  QDBusInterface iface{QString::fromLatin1("org.freedesktop.FileManager1"),
                       QString::fromLatin1("/org/freedesktop/FileManager1"),
                       QString::fromLatin1("org.freedesktop.FileManager1")};
  if (iface.isValid())
  {
    QStringList urls;
    urls << QUrl::fromLocalFile(QString::fromUtf8(file_path)).toString();

    const QDBusReply<void> reply = iface.call(QString::fromLatin1("ShowItems"), urls, QString{});
    if (reply.isValid())
      return;

    const auto& err = reply.error();
    WARN_LOG_FMT(COMMON, "DBus call failed: {} - {}", err.name().toStdString(),
                 err.message().toStdString());
  }
  else
  {
    WARN_LOG_FMT(COMMON, "Invalid DBus interface: {}", iface.lastError().message().toStdString());
  }

#elif defined(_WIN32)
  if (QProcess{}.startDetached(QString::fromLatin1("explorer.exe"),
                               {QString::fromLatin1("/select"), QString::fromLatin1(","),
                                QDir::toNativeSeparators(QString::fromUtf8(file_path))}))
  {
    return;
  }
#elif defined(__APPLE__)
  if (QProcess{}.startDetached(QString::fromLatin1("open"),
                               {QString::fromLatin1("-R"), QString::fromUtf8(file_path)}))
  {
    return;
  }
#endif

  // Fall back on failure or unsupported platforms.
  ShowFolderOfFile(file_path);
}

}  // namespace QtUtils