mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Split GTK integration into a singleton
This commit is contained in:
parent
bb016e1489
commit
ada22ee6cc
21 changed files with 1800 additions and 1629 deletions
|
@ -821,10 +821,15 @@ PRIVATE
|
||||||
platform/linux/linux_gdk_helper.h
|
platform/linux/linux_gdk_helper.h
|
||||||
platform/linux/linux_gsd_media_keys.cpp
|
platform/linux/linux_gsd_media_keys.cpp
|
||||||
platform/linux/linux_gsd_media_keys.h
|
platform/linux/linux_gsd_media_keys.h
|
||||||
platform/linux/linux_libs.cpp
|
platform/linux/linux_gtk_file_dialog.cpp
|
||||||
platform/linux/linux_libs.h
|
platform/linux/linux_gtk_file_dialog.h
|
||||||
|
platform/linux/linux_gtk_integration_p.h
|
||||||
|
platform/linux/linux_gtk_integration.cpp
|
||||||
|
platform/linux/linux_gtk_integration.h
|
||||||
platform/linux/linux_notification_service_watcher.cpp
|
platform/linux/linux_notification_service_watcher.cpp
|
||||||
platform/linux/linux_notification_service_watcher.h
|
platform/linux/linux_notification_service_watcher.h
|
||||||
|
platform/linux/linux_open_with_dialog.cpp
|
||||||
|
platform/linux/linux_open_with_dialog.h
|
||||||
platform/linux/linux_wayland_integration.cpp
|
platform/linux/linux_wayland_integration.cpp
|
||||||
platform/linux/linux_wayland_integration.h
|
platform/linux/linux_wayland_integration.h
|
||||||
platform/linux/linux_xlib_helper.cpp
|
platform/linux/linux_xlib_helper.cpp
|
||||||
|
@ -1134,6 +1139,26 @@ if (LINUX AND DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION)
|
||||||
nice_target_sources(Telegram ${src_loc} PRIVATE platform/linux/linux_wayland_integration_dummy.cpp)
|
nice_target_sources(Telegram ${src_loc} PRIVATE platform/linux/linux_wayland_integration_dummy.cpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (LINUX AND TDESKTOP_DISABLE_GTK_INTEGRATION)
|
||||||
|
remove_target_sources(Telegram ${src_loc}
|
||||||
|
platform/linux/linux_gdk_helper.cpp
|
||||||
|
platform/linux/linux_gdk_helper.h
|
||||||
|
platform/linux/linux_gtk_file_dialog.cpp
|
||||||
|
platform/linux/linux_gtk_file_dialog.h
|
||||||
|
platform/linux/linux_gtk_integration_p.h
|
||||||
|
platform/linux/linux_gtk_integration.cpp
|
||||||
|
platform/linux/linux_open_with_dialog.cpp
|
||||||
|
platform/linux/linux_open_with_dialog.h
|
||||||
|
platform/linux/linux_xlib_helper.cpp
|
||||||
|
platform/linux/linux_xlib_helper.h
|
||||||
|
)
|
||||||
|
|
||||||
|
nice_target_sources(Telegram ${src_loc}
|
||||||
|
PRIVATE
|
||||||
|
platform/linux/linux_gtk_integration_dummy.cpp
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (NOT DESKTOP_APP_USE_PACKAGED)
|
if (NOT DESKTOP_APP_USE_PACKAGED)
|
||||||
nice_target_sources(Telegram ${src_loc} PRIVATE platform/mac/mac_iconv_helper.c)
|
nice_target_sources(Telegram ${src_loc} PRIVATE platform/mac/mac_iconv_helper.c)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -7,14 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "platform/linux/file_utilities_linux.h"
|
#include "platform/linux/file_utilities_linux.h"
|
||||||
|
|
||||||
#include "platform/linux/linux_libs.h"
|
#include "platform/linux/linux_gtk_integration.h"
|
||||||
#include "platform/linux/linux_gdk_helper.h"
|
|
||||||
#include "platform/linux/linux_desktop_environment.h"
|
|
||||||
#include "platform/linux/specific_linux.h"
|
#include "platform/linux/specific_linux.h"
|
||||||
#include "storage/localstorage.h"
|
|
||||||
#include "base/qt_adapters.h"
|
|
||||||
#include "window/window_controller.h"
|
|
||||||
#include "core/application.h"
|
|
||||||
|
|
||||||
#include <QtGui/QDesktopServices>
|
#include <QtGui/QDesktopServices>
|
||||||
|
|
||||||
|
@ -24,121 +18,10 @@ extern "C" {
|
||||||
#define signals public
|
#define signals public
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|
||||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
using Platform::internal::GtkIntegration;
|
||||||
#include <private/qguiapplication_p.h>
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#undef signals
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#include <gdk/gdk.h>
|
|
||||||
#define signals public
|
|
||||||
} // extern "C"
|
|
||||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
|
|
||||||
namespace Platform {
|
namespace Platform {
|
||||||
namespace File {
|
namespace File {
|
||||||
namespace {
|
|
||||||
|
|
||||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
bool ShowOpenWithSupported() {
|
|
||||||
return Platform::internal::GdkHelperLoaded()
|
|
||||||
&& (Libs::gtk_app_chooser_dialog_new != nullptr)
|
|
||||||
&& (Libs::gtk_app_chooser_get_app_info != nullptr)
|
|
||||||
&& (Libs::gtk_app_chooser_get_type != nullptr)
|
|
||||||
&& (Libs::gtk_widget_get_window != nullptr)
|
|
||||||
&& (Libs::gtk_widget_realize != nullptr)
|
|
||||||
&& (Libs::gtk_widget_show != nullptr)
|
|
||||||
&& (Libs::gtk_widget_destroy != nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
class OpenWithDialog : public QWindow {
|
|
||||||
public:
|
|
||||||
OpenWithDialog(const QString &filepath);
|
|
||||||
~OpenWithDialog();
|
|
||||||
|
|
||||||
bool exec();
|
|
||||||
|
|
||||||
private:
|
|
||||||
static void handleResponse(OpenWithDialog *dialog, int responseId);
|
|
||||||
|
|
||||||
GFile *_gfileInstance = nullptr;
|
|
||||||
GtkWidget *_gtkWidget = nullptr;
|
|
||||||
QEventLoop _loop;
|
|
||||||
std::optional<bool> _result = std::nullopt;
|
|
||||||
};
|
|
||||||
|
|
||||||
OpenWithDialog::OpenWithDialog(const QString &filepath)
|
|
||||||
: _gfileInstance(g_file_new_for_path(filepath.toUtf8()))
|
|
||||||
, _gtkWidget(Libs::gtk_app_chooser_dialog_new(
|
|
||||||
nullptr,
|
|
||||||
GTK_DIALOG_MODAL,
|
|
||||||
_gfileInstance)) {
|
|
||||||
g_signal_connect_swapped(
|
|
||||||
_gtkWidget,
|
|
||||||
"response",
|
|
||||||
G_CALLBACK(handleResponse),
|
|
||||||
this);
|
|
||||||
}
|
|
||||||
|
|
||||||
OpenWithDialog::~OpenWithDialog() {
|
|
||||||
Libs::gtk_widget_destroy(_gtkWidget);
|
|
||||||
g_object_unref(_gfileInstance);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OpenWithDialog::exec() {
|
|
||||||
Libs::gtk_widget_realize(_gtkWidget);
|
|
||||||
|
|
||||||
if (const auto activeWindow = Core::App().activeWindow()) {
|
|
||||||
Platform::internal::XSetTransientForHint(
|
|
||||||
Libs::gtk_widget_get_window(_gtkWidget),
|
|
||||||
activeWindow->widget().get()->windowHandle()->winId());
|
|
||||||
}
|
|
||||||
|
|
||||||
QGuiApplicationPrivate::showModalWindow(this);
|
|
||||||
Libs::gtk_widget_show(_gtkWidget);
|
|
||||||
|
|
||||||
if (!_result.has_value()) {
|
|
||||||
_loop.exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
QGuiApplicationPrivate::hideModalWindow(this);
|
|
||||||
return *_result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OpenWithDialog::handleResponse(OpenWithDialog *dialog, int responseId) {
|
|
||||||
GAppInfo *chosenAppInfo = nullptr;
|
|
||||||
dialog->_result = true;
|
|
||||||
|
|
||||||
switch (responseId) {
|
|
||||||
case GTK_RESPONSE_OK:
|
|
||||||
chosenAppInfo = Libs::gtk_app_chooser_get_app_info(
|
|
||||||
Libs::gtk_app_chooser_cast(dialog->_gtkWidget));
|
|
||||||
|
|
||||||
if (chosenAppInfo) {
|
|
||||||
GList *uris = nullptr;
|
|
||||||
uris = g_list_prepend(uris, g_file_get_uri(dialog->_gfileInstance));
|
|
||||||
g_app_info_launch_uris(chosenAppInfo, uris, nullptr, nullptr);
|
|
||||||
g_list_free(uris);
|
|
||||||
g_object_unref(chosenAppInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case GTK_RESPONSE_CANCEL:
|
|
||||||
case GTK_RESPONSE_DELETE_EVENT:
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
dialog->_result = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog->_loop.quit();
|
|
||||||
}
|
|
||||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
QByteArray EscapeShell(const QByteArray &content) {
|
QByteArray EscapeShell(const QByteArray &content) {
|
||||||
|
@ -183,18 +66,16 @@ void UnsafeOpenEmailLink(const QString &email) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UnsafeShowOpenWith(const QString &filepath) {
|
bool UnsafeShowOpenWith(const QString &filepath) {
|
||||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
if (InFlatpak() || InSnap()) {
|
||||||
if (InFlatpak()
|
|
||||||
|| InSnap()
|
|
||||||
|| !ShowOpenWithSupported()) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto absolutePath = QFileInfo(filepath).absoluteFilePath();
|
if (const auto integration = GtkIntegration::Instance()) {
|
||||||
return OpenWithDialog(absolutePath).exec();
|
const auto absolutePath = QFileInfo(filepath).absoluteFilePath();
|
||||||
#else // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
return integration->showOpenWithDialog(absolutePath);
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
#endif // TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnsafeLaunch(const QString &filepath) {
|
void UnsafeLaunch(const QString &filepath) {
|
||||||
|
@ -213,136 +94,6 @@ void UnsafeLaunch(const QString &filepath) {
|
||||||
} // namespace File
|
} // namespace File
|
||||||
|
|
||||||
namespace FileDialog {
|
namespace FileDialog {
|
||||||
namespace {
|
|
||||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
|
|
||||||
// GTK file chooser image preview: thanks to Chromium
|
|
||||||
|
|
||||||
// The size of the preview we display for selected image files. We set height
|
|
||||||
// larger than width because generally there is more free space vertically
|
|
||||||
// than horiztonally (setting the preview image will alway expand the width of
|
|
||||||
// the dialog, but usually not the height). The image's aspect ratio will always
|
|
||||||
// be preserved.
|
|
||||||
constexpr auto kPreviewWidth = 256;
|
|
||||||
constexpr auto kPreviewHeight = 512;
|
|
||||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
|
|
||||||
using Type = ::FileDialog::internal::Type;
|
|
||||||
|
|
||||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
bool UseNative(Type type = Type::ReadFile) {
|
|
||||||
// use gtk file dialog on gtk-based desktop environments
|
|
||||||
// or if QT_QPA_PLATFORMTHEME=(gtk2|gtk3)
|
|
||||||
// or if portals are used and operation is to open folder
|
|
||||||
// and portal doesn't support folder choosing
|
|
||||||
const auto sandboxedOrCustomPortal = InFlatpak()
|
|
||||||
|| InSnap()
|
|
||||||
|| UseXDGDesktopPortal();
|
|
||||||
|
|
||||||
const auto neededForPortal = (type == Type::ReadFolder)
|
|
||||||
&& !CanOpenDirectoryWithPortal();
|
|
||||||
|
|
||||||
const auto neededNonForced = DesktopEnvironment::IsGtkBased()
|
|
||||||
|| (sandboxedOrCustomPortal && neededForPortal);
|
|
||||||
|
|
||||||
const auto excludeNonForced = sandboxedOrCustomPortal && !neededForPortal;
|
|
||||||
|
|
||||||
return IsGtkIntegrationForced()
|
|
||||||
|| (neededNonForced && !excludeNonForced);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NativeSupported() {
|
|
||||||
return Platform::internal::GdkHelperLoaded()
|
|
||||||
&& (Libs::gtk_widget_hide_on_delete != nullptr)
|
|
||||||
&& (Libs::gtk_clipboard_store != nullptr)
|
|
||||||
&& (Libs::gtk_clipboard_get != nullptr)
|
|
||||||
&& (Libs::gtk_widget_destroy != nullptr)
|
|
||||||
&& (Libs::gtk_dialog_get_type != nullptr)
|
|
||||||
&& (Libs::gtk_dialog_run != nullptr)
|
|
||||||
&& (Libs::gtk_widget_realize != nullptr)
|
|
||||||
&& (Libs::gdk_window_set_modal_hint != nullptr)
|
|
||||||
&& (Libs::gtk_widget_show != nullptr)
|
|
||||||
&& (Libs::gdk_window_focus != nullptr)
|
|
||||||
&& (Libs::gtk_widget_hide != nullptr)
|
|
||||||
&& (Libs::gtk_widget_hide_on_delete != nullptr)
|
|
||||||
&& (Libs::gtk_file_chooser_dialog_new != nullptr)
|
|
||||||
&& (Libs::gtk_file_chooser_get_type != nullptr)
|
|
||||||
&& (Libs::gtk_file_chooser_set_current_folder != nullptr)
|
|
||||||
&& (Libs::gtk_file_chooser_get_current_folder != nullptr)
|
|
||||||
&& (Libs::gtk_file_chooser_set_current_name != nullptr)
|
|
||||||
&& (Libs::gtk_file_chooser_select_filename != nullptr)
|
|
||||||
&& (Libs::gtk_file_chooser_get_filenames != nullptr)
|
|
||||||
&& (Libs::gtk_file_chooser_set_filter != nullptr)
|
|
||||||
&& (Libs::gtk_file_chooser_get_filter != nullptr)
|
|
||||||
&& (Libs::gtk_window_get_type != nullptr)
|
|
||||||
&& (Libs::gtk_window_set_title != nullptr)
|
|
||||||
&& (Libs::gtk_file_chooser_set_local_only != nullptr)
|
|
||||||
&& (Libs::gtk_file_chooser_set_action != nullptr)
|
|
||||||
&& (Libs::gtk_file_chooser_set_select_multiple != nullptr)
|
|
||||||
&& (Libs::gtk_file_chooser_set_do_overwrite_confirmation != nullptr)
|
|
||||||
&& (Libs::gtk_file_chooser_remove_filter != nullptr)
|
|
||||||
&& (Libs::gtk_file_filter_set_name != nullptr)
|
|
||||||
&& (Libs::gtk_file_filter_add_pattern != nullptr)
|
|
||||||
&& (Libs::gtk_file_chooser_add_filter != nullptr)
|
|
||||||
&& (Libs::gtk_file_filter_new != nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PreviewSupported() {
|
|
||||||
return NativeSupported()
|
|
||||||
&& (Libs::gdk_pixbuf_new_from_file_at_size != nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GetNative(
|
|
||||||
QPointer<QWidget> parent,
|
|
||||||
QStringList &files,
|
|
||||||
QByteArray &remoteContent,
|
|
||||||
const QString &caption,
|
|
||||||
const QString &filter,
|
|
||||||
Type type,
|
|
||||||
QString startFile) {
|
|
||||||
internal::GtkFileDialog dialog(parent, caption, QString(), filter);
|
|
||||||
|
|
||||||
dialog.setModal(true);
|
|
||||||
if (type == Type::ReadFile || type == Type::ReadFiles) {
|
|
||||||
dialog.setFileMode((type == Type::ReadFiles) ? QFileDialog::ExistingFiles : QFileDialog::ExistingFile);
|
|
||||||
dialog.setAcceptMode(QFileDialog::AcceptOpen);
|
|
||||||
} else if (type == Type::ReadFolder) {
|
|
||||||
dialog.setAcceptMode(QFileDialog::AcceptOpen);
|
|
||||||
dialog.setFileMode(QFileDialog::Directory);
|
|
||||||
dialog.setOption(QFileDialog::ShowDirsOnly);
|
|
||||||
} else {
|
|
||||||
dialog.setFileMode(QFileDialog::AnyFile);
|
|
||||||
dialog.setAcceptMode(QFileDialog::AcceptSave);
|
|
||||||
}
|
|
||||||
if (startFile.isEmpty() || startFile.at(0) != '/') {
|
|
||||||
startFile = cDialogLastPath() + '/' + startFile;
|
|
||||||
}
|
|
||||||
dialog.selectFile(startFile);
|
|
||||||
|
|
||||||
int res = dialog.exec();
|
|
||||||
|
|
||||||
QString path = dialog.directory().absolutePath();
|
|
||||||
if (path != cDialogLastPath()) {
|
|
||||||
cSetDialogLastPath(path);
|
|
||||||
Local::writeSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res == QDialog::Accepted) {
|
|
||||||
if (type == Type::ReadFiles) {
|
|
||||||
files = dialog.selectedFiles();
|
|
||||||
} else {
|
|
||||||
files = dialog.selectedFiles().mid(0, 1);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
files = QStringList();
|
|
||||||
remoteContent = QByteArray();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
bool Get(
|
bool Get(
|
||||||
QPointer<QWidget> parent,
|
QPointer<QWidget> parent,
|
||||||
|
@ -350,23 +101,24 @@ bool Get(
|
||||||
QByteArray &remoteContent,
|
QByteArray &remoteContent,
|
||||||
const QString &caption,
|
const QString &caption,
|
||||||
const QString &filter,
|
const QString &filter,
|
||||||
Type type,
|
::FileDialog::internal::Type type,
|
||||||
QString startFile) {
|
QString startFile) {
|
||||||
if (parent) {
|
if (parent) {
|
||||||
parent = parent->window();
|
parent = parent->window();
|
||||||
}
|
}
|
||||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
if (const auto integration = GtkIntegration::Instance()) {
|
||||||
if (UseNative(type) && NativeSupported()) {
|
if (integration->fileDialogSupported()
|
||||||
return GetNative(
|
&& integration->useFileDialog(type)) {
|
||||||
parent,
|
return integration->getFileDialog(
|
||||||
files,
|
parent,
|
||||||
remoteContent,
|
files,
|
||||||
caption,
|
remoteContent,
|
||||||
filter,
|
caption,
|
||||||
type,
|
filter,
|
||||||
startFile);
|
type,
|
||||||
|
startFile);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
return ::FileDialog::internal::GetDefault(
|
return ::FileDialog::internal::GetDefault(
|
||||||
parent,
|
parent,
|
||||||
files,
|
files,
|
||||||
|
@ -377,448 +129,5 @@ bool Get(
|
||||||
startFile);
|
startFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
namespace internal {
|
|
||||||
|
|
||||||
QGtkDialog::QGtkDialog(GtkWidget *gtkWidget) : gtkWidget(gtkWidget) {
|
|
||||||
g_signal_connect_swapped(G_OBJECT(gtkWidget), "response", G_CALLBACK(onResponse), this);
|
|
||||||
g_signal_connect(G_OBJECT(gtkWidget), "delete-event", G_CALLBACK(Libs::gtk_widget_hide_on_delete), nullptr);
|
|
||||||
if (PreviewSupported()) {
|
|
||||||
_preview = Libs::gtk_image_new();
|
|
||||||
g_signal_connect_swapped(G_OBJECT(gtkWidget), "update-preview", G_CALLBACK(onUpdatePreview), this);
|
|
||||||
Libs::gtk_file_chooser_set_preview_widget(Libs::gtk_file_chooser_cast(gtkWidget), _preview);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QGtkDialog::~QGtkDialog() {
|
|
||||||
Libs::gtk_clipboard_store(Libs::gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
|
|
||||||
Libs::gtk_widget_destroy(gtkWidget);
|
|
||||||
}
|
|
||||||
|
|
||||||
GtkDialog *QGtkDialog::gtkDialog() const {
|
|
||||||
return Libs::gtk_dialog_cast(gtkWidget);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QGtkDialog::exec() {
|
|
||||||
if (modality() == Qt::ApplicationModal) {
|
|
||||||
// block input to the whole app, including other GTK dialogs
|
|
||||||
Libs::gtk_dialog_run(gtkDialog());
|
|
||||||
} else {
|
|
||||||
// block input to the window, allow input to other GTK dialogs
|
|
||||||
QEventLoop loop;
|
|
||||||
connect(this, SIGNAL(accept()), &loop, SLOT(quit()));
|
|
||||||
connect(this, SIGNAL(reject()), &loop, SLOT(quit()));
|
|
||||||
loop.exec();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QGtkDialog::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) {
|
|
||||||
connect(parent, &QWindow::destroyed, this, &QGtkDialog::onParentWindowDestroyed,
|
|
||||||
Qt::UniqueConnection);
|
|
||||||
setParent(parent);
|
|
||||||
setFlags(flags);
|
|
||||||
setModality(modality);
|
|
||||||
|
|
||||||
Libs::gtk_widget_realize(gtkWidget); // creates X window
|
|
||||||
|
|
||||||
if (parent) {
|
|
||||||
Platform::internal::XSetTransientForHint(Libs::gtk_widget_get_window(gtkWidget), parent->winId());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modality != Qt::NonModal) {
|
|
||||||
Libs::gdk_window_set_modal_hint(Libs::gtk_widget_get_window(gtkWidget), true);
|
|
||||||
QGuiApplicationPrivate::showModalWindow(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
Libs::gtk_widget_show(gtkWidget);
|
|
||||||
Libs::gdk_window_focus(Libs::gtk_widget_get_window(gtkWidget), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QGtkDialog::hide() {
|
|
||||||
QGuiApplicationPrivate::hideModalWindow(this);
|
|
||||||
Libs::gtk_widget_hide(gtkWidget);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QGtkDialog::onResponse(QGtkDialog *dialog, int response) {
|
|
||||||
if (response == GTK_RESPONSE_OK)
|
|
||||||
emit dialog->accept();
|
|
||||||
else
|
|
||||||
emit dialog->reject();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QGtkDialog::onUpdatePreview(QGtkDialog* dialog) {
|
|
||||||
auto filename = Libs::gtk_file_chooser_get_preview_filename(Libs::gtk_file_chooser_cast(dialog->gtkWidget));
|
|
||||||
if (!filename) {
|
|
||||||
Libs::gtk_file_chooser_set_preview_widget_active(Libs::gtk_file_chooser_cast(dialog->gtkWidget), false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't attempt to open anything which isn't a regular file. If a named pipe,
|
|
||||||
// this may hang. See https://crbug.com/534754.
|
|
||||||
struct stat stat_buf;
|
|
||||||
if (stat(filename, &stat_buf) != 0 || !S_ISREG(stat_buf.st_mode)) {
|
|
||||||
g_free(filename);
|
|
||||||
Libs::gtk_file_chooser_set_preview_widget_active(Libs::gtk_file_chooser_cast(dialog->gtkWidget), false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This will preserve the image's aspect ratio.
|
|
||||||
auto pixbuf = Libs::gdk_pixbuf_new_from_file_at_size(filename, kPreviewWidth, kPreviewHeight, nullptr);
|
|
||||||
g_free(filename);
|
|
||||||
if (pixbuf) {
|
|
||||||
Libs::gtk_image_set_from_pixbuf(Libs::gtk_image_cast(dialog->_preview), pixbuf);
|
|
||||||
g_object_unref(pixbuf);
|
|
||||||
}
|
|
||||||
Libs::gtk_file_chooser_set_preview_widget_active(Libs::gtk_file_chooser_cast(dialog->gtkWidget), pixbuf ? true : false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QGtkDialog::onParentWindowDestroyed() {
|
|
||||||
// The Gtk*DialogHelper classes own this object. Make sure the parent doesn't delete it.
|
|
||||||
setParent(nullptr);
|
|
||||||
}
|
|
||||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
const char *filterRegExp =
|
|
||||||
"^(.*)\\(([a-zA-Z0-9_.,*? +;#\\-\\[\\]@\\{\\}/!<>\\$%&=^~:\\|]*)\\)$";
|
|
||||||
|
|
||||||
QStringList makeFilterList(const QString &filter) {
|
|
||||||
QString f(filter);
|
|
||||||
|
|
||||||
if (f.isEmpty())
|
|
||||||
return QStringList();
|
|
||||||
|
|
||||||
QString sep(QLatin1String(";;"));
|
|
||||||
int i = f.indexOf(sep, 0);
|
|
||||||
if (i == -1) {
|
|
||||||
if (f.indexOf(QLatin1Char('\n'), 0) != -1) {
|
|
||||||
sep = QLatin1Char('\n');
|
|
||||||
i = f.indexOf(sep, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return f.split(sep);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Makes a list of filters from a normal filter string "Image Files (*.png *.jpg)"
|
|
||||||
QStringList cleanFilterList(const QString &filter) {
|
|
||||||
QRegExp regexp(QString::fromLatin1(filterRegExp));
|
|
||||||
Q_ASSERT(regexp.isValid());
|
|
||||||
QString f = filter;
|
|
||||||
int i = regexp.indexIn(f);
|
|
||||||
if (i >= 0)
|
|
||||||
f = regexp.cap(2);
|
|
||||||
return f.split(QLatin1Char(' '), base::QStringSkipEmptyParts);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
GtkFileDialog::GtkFileDialog(QWidget *parent, const QString &caption, const QString &directory, const QString &filter) : QDialog(parent)
|
|
||||||
, _windowTitle(caption)
|
|
||||||
, _initialDirectory(directory) {
|
|
||||||
auto filters = makeFilterList(filter);
|
|
||||||
const int numFilters = filters.count();
|
|
||||||
_nameFilters.reserve(numFilters);
|
|
||||||
for (int i = 0; i < numFilters; ++i) {
|
|
||||||
_nameFilters << filters[i].simplified();
|
|
||||||
}
|
|
||||||
|
|
||||||
d.reset(new QGtkDialog(Libs::gtk_file_chooser_dialog_new("", nullptr,
|
|
||||||
GTK_FILE_CHOOSER_ACTION_OPEN,
|
|
||||||
// https://developer.gnome.org/gtk3/stable/GtkFileChooserDialog.html#gtk-file-chooser-dialog-new
|
|
||||||
// first_button_text doesn't need explicit conversion to char*, while all others are vardict
|
|
||||||
tr::lng_cancel(tr::now).toUtf8(), GTK_RESPONSE_CANCEL,
|
|
||||||
tr::lng_box_ok(tr::now).toUtf8().constData(), GTK_RESPONSE_OK, nullptr)));
|
|
||||||
connect(d.data(), SIGNAL(accept()), this, SLOT(onAccepted()));
|
|
||||||
connect(d.data(), SIGNAL(reject()), this, SLOT(onRejected()));
|
|
||||||
|
|
||||||
g_signal_connect(Libs::gtk_file_chooser_cast(d->gtkDialog()), "selection-changed", G_CALLBACK(onSelectionChanged), this);
|
|
||||||
g_signal_connect_swapped(Libs::gtk_file_chooser_cast(d->gtkDialog()), "current-folder-changed", G_CALLBACK(onCurrentFolderChanged), this);
|
|
||||||
}
|
|
||||||
|
|
||||||
GtkFileDialog::~GtkFileDialog() {
|
|
||||||
}
|
|
||||||
|
|
||||||
void GtkFileDialog::showHelper(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) {
|
|
||||||
_dir.clear();
|
|
||||||
_selection.clear();
|
|
||||||
|
|
||||||
applyOptions();
|
|
||||||
return d->show(flags, modality, parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GtkFileDialog::setVisible(bool visible) {
|
|
||||||
if (visible) {
|
|
||||||
if (testAttribute(Qt::WA_WState_ExplicitShowHide) && !testAttribute(Qt::WA_WState_Hidden)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else if (testAttribute(Qt::WA_WState_ExplicitShowHide) && testAttribute(Qt::WA_WState_Hidden)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (visible) {
|
|
||||||
showHelper(windowFlags(), windowModality(), parentWidget() ? parentWidget()->windowHandle() : nullptr);
|
|
||||||
} else {
|
|
||||||
hideHelper();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set WA_DontShowOnScreen so that QDialog::setVisible(visible) below
|
|
||||||
// updates the state correctly, but skips showing the non-native version:
|
|
||||||
setAttribute(Qt::WA_DontShowOnScreen);
|
|
||||||
|
|
||||||
QDialog::setVisible(visible);
|
|
||||||
}
|
|
||||||
|
|
||||||
int GtkFileDialog::exec() {
|
|
||||||
d->setModality(windowModality());
|
|
||||||
|
|
||||||
bool deleteOnClose = testAttribute(Qt::WA_DeleteOnClose);
|
|
||||||
setAttribute(Qt::WA_DeleteOnClose, false);
|
|
||||||
|
|
||||||
bool wasShowModal = testAttribute(Qt::WA_ShowModal);
|
|
||||||
setAttribute(Qt::WA_ShowModal, true);
|
|
||||||
setResult(0);
|
|
||||||
|
|
||||||
show();
|
|
||||||
|
|
||||||
QPointer<QDialog> guard = this;
|
|
||||||
d->exec();
|
|
||||||
if (guard.isNull())
|
|
||||||
return QDialog::Rejected;
|
|
||||||
|
|
||||||
setAttribute(Qt::WA_ShowModal, wasShowModal);
|
|
||||||
|
|
||||||
return result();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GtkFileDialog::hideHelper() {
|
|
||||||
// After GtkFileChooserDialog has been hidden, gtk_file_chooser_get_current_folder()
|
|
||||||
// & gtk_file_chooser_get_filenames() will return bogus values -> cache the actual
|
|
||||||
// values before hiding the dialog
|
|
||||||
_dir = directory().absolutePath();
|
|
||||||
_selection = selectedFiles();
|
|
||||||
|
|
||||||
d->hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GtkFileDialog::defaultNameFilterDisables() const {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GtkFileDialog::setDirectory(const QString &directory) {
|
|
||||||
GtkDialog *gtkDialog = d->gtkDialog();
|
|
||||||
Libs::gtk_file_chooser_set_current_folder(Libs::gtk_file_chooser_cast(gtkDialog), directory.toUtf8());
|
|
||||||
}
|
|
||||||
|
|
||||||
QDir GtkFileDialog::directory() const {
|
|
||||||
// While GtkFileChooserDialog is hidden, gtk_file_chooser_get_current_folder()
|
|
||||||
// returns a bogus value -> return the cached value before hiding
|
|
||||||
if (!_dir.isEmpty())
|
|
||||||
return _dir;
|
|
||||||
|
|
||||||
QString ret;
|
|
||||||
GtkDialog *gtkDialog = d->gtkDialog();
|
|
||||||
gchar *folder = Libs::gtk_file_chooser_get_current_folder(Libs::gtk_file_chooser_cast(gtkDialog));
|
|
||||||
if (folder) {
|
|
||||||
ret = QString::fromUtf8(folder);
|
|
||||||
g_free(folder);
|
|
||||||
}
|
|
||||||
return QDir(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GtkFileDialog::selectFile(const QString &filename) {
|
|
||||||
_initialFiles.clear();
|
|
||||||
_initialFiles.append(filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList GtkFileDialog::selectedFiles() const {
|
|
||||||
// While GtkFileChooserDialog is hidden, gtk_file_chooser_get_filenames()
|
|
||||||
// returns a bogus value -> return the cached value before hiding
|
|
||||||
if (!_selection.isEmpty())
|
|
||||||
return _selection;
|
|
||||||
|
|
||||||
QStringList selection;
|
|
||||||
GtkDialog *gtkDialog = d->gtkDialog();
|
|
||||||
GSList *filenames = Libs::gtk_file_chooser_get_filenames(Libs::gtk_file_chooser_cast(gtkDialog));
|
|
||||||
for (GSList *it = filenames; it; it = it->next)
|
|
||||||
selection += QString::fromUtf8((const char*)it->data);
|
|
||||||
g_slist_free(filenames);
|
|
||||||
return selection;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GtkFileDialog::setFilter() {
|
|
||||||
applyOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GtkFileDialog::selectNameFilter(const QString &filter) {
|
|
||||||
GtkFileFilter *gtkFilter = _filters.value(filter);
|
|
||||||
if (gtkFilter) {
|
|
||||||
GtkDialog *gtkDialog = d->gtkDialog();
|
|
||||||
Libs::gtk_file_chooser_set_filter(Libs::gtk_file_chooser_cast(gtkDialog), gtkFilter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString GtkFileDialog::selectedNameFilter() const {
|
|
||||||
GtkDialog *gtkDialog = d->gtkDialog();
|
|
||||||
GtkFileFilter *gtkFilter = Libs::gtk_file_chooser_get_filter(Libs::gtk_file_chooser_cast(gtkDialog));
|
|
||||||
return _filterNames.value(gtkFilter);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GtkFileDialog::onAccepted() {
|
|
||||||
emit accept();
|
|
||||||
|
|
||||||
// QString filter = selectedNameFilter();
|
|
||||||
// if (filter.isEmpty())
|
|
||||||
// emit filterSelected(filter);
|
|
||||||
|
|
||||||
// QList<QUrl> files = selectedFiles();
|
|
||||||
// emit filesSelected(files);
|
|
||||||
// if (files.count() == 1)
|
|
||||||
// emit fileSelected(files.first());
|
|
||||||
}
|
|
||||||
|
|
||||||
void GtkFileDialog::onRejected() {
|
|
||||||
emit reject();
|
|
||||||
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
void GtkFileDialog::onSelectionChanged(GtkDialog *gtkDialog, GtkFileDialog *helper) {
|
|
||||||
// QString selection;
|
|
||||||
// gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(gtkDialog));
|
|
||||||
// if (filename) {
|
|
||||||
// selection = QString::fromUtf8(filename);
|
|
||||||
// g_free(filename);
|
|
||||||
// }
|
|
||||||
// emit helper->currentChanged(QUrl::fromLocalFile(selection));
|
|
||||||
}
|
|
||||||
|
|
||||||
void GtkFileDialog::onCurrentFolderChanged(GtkFileDialog *dialog) {
|
|
||||||
// emit dialog->directoryEntered(dialog->directory());
|
|
||||||
}
|
|
||||||
|
|
||||||
GtkFileChooserAction gtkFileChooserAction(QFileDialog::FileMode fileMode, QFileDialog::AcceptMode acceptMode) {
|
|
||||||
switch (fileMode) {
|
|
||||||
case QFileDialog::AnyFile:
|
|
||||||
case QFileDialog::ExistingFile:
|
|
||||||
case QFileDialog::ExistingFiles:
|
|
||||||
if (acceptMode == QFileDialog::AcceptOpen)
|
|
||||||
return GTK_FILE_CHOOSER_ACTION_OPEN;
|
|
||||||
else
|
|
||||||
return GTK_FILE_CHOOSER_ACTION_SAVE;
|
|
||||||
case QFileDialog::Directory:
|
|
||||||
default:
|
|
||||||
if (acceptMode == QFileDialog::AcceptOpen)
|
|
||||||
return GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
|
|
||||||
else
|
|
||||||
return GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CustomButtonsSupported() {
|
|
||||||
return (Libs::gtk_dialog_get_widget_for_response != nullptr)
|
|
||||||
&& (Libs::gtk_button_set_label != nullptr)
|
|
||||||
&& (Libs::gtk_button_get_type != nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GtkFileDialog::applyOptions() {
|
|
||||||
GtkDialog *gtkDialog = d->gtkDialog();
|
|
||||||
|
|
||||||
Libs::gtk_window_set_title(Libs::gtk_window_cast(gtkDialog), _windowTitle.toUtf8());
|
|
||||||
Libs::gtk_file_chooser_set_local_only(Libs::gtk_file_chooser_cast(gtkDialog), true);
|
|
||||||
|
|
||||||
const GtkFileChooserAction action = gtkFileChooserAction(_fileMode, _acceptMode);
|
|
||||||
Libs::gtk_file_chooser_set_action(Libs::gtk_file_chooser_cast(gtkDialog), action);
|
|
||||||
|
|
||||||
const bool selectMultiple = (_fileMode == QFileDialog::ExistingFiles);
|
|
||||||
Libs::gtk_file_chooser_set_select_multiple(Libs::gtk_file_chooser_cast(gtkDialog), selectMultiple);
|
|
||||||
|
|
||||||
const bool confirmOverwrite = !_options.testFlag(QFileDialog::DontConfirmOverwrite);
|
|
||||||
Libs::gtk_file_chooser_set_do_overwrite_confirmation(Libs::gtk_file_chooser_cast(gtkDialog), confirmOverwrite);
|
|
||||||
|
|
||||||
if (!_nameFilters.isEmpty())
|
|
||||||
setNameFilters(_nameFilters);
|
|
||||||
|
|
||||||
if (!_initialDirectory.isEmpty())
|
|
||||||
setDirectory(_initialDirectory);
|
|
||||||
|
|
||||||
for_const (const auto &filename, _initialFiles) {
|
|
||||||
if (_acceptMode == QFileDialog::AcceptSave) {
|
|
||||||
QFileInfo fi(filename);
|
|
||||||
Libs::gtk_file_chooser_set_current_folder(Libs::gtk_file_chooser_cast(gtkDialog), fi.path().toUtf8());
|
|
||||||
Libs::gtk_file_chooser_set_current_name(Libs::gtk_file_chooser_cast(gtkDialog), fi.fileName().toUtf8());
|
|
||||||
} else if (filename.endsWith('/')) {
|
|
||||||
Libs::gtk_file_chooser_set_current_folder(Libs::gtk_file_chooser_cast(gtkDialog), filename.toUtf8());
|
|
||||||
} else {
|
|
||||||
Libs::gtk_file_chooser_select_filename(Libs::gtk_file_chooser_cast(gtkDialog), filename.toUtf8());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString initialNameFilter = _nameFilters.isEmpty() ? QString() : _nameFilters.front();
|
|
||||||
if (!initialNameFilter.isEmpty())
|
|
||||||
selectNameFilter(initialNameFilter);
|
|
||||||
|
|
||||||
if (CustomButtonsSupported()) {
|
|
||||||
GtkWidget *acceptButton = Libs::gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_OK);
|
|
||||||
if (acceptButton) {
|
|
||||||
/*if (opts->isLabelExplicitlySet(QFileDialogOptions::Accept))
|
|
||||||
Libs::gtk_button_set_label(Libs::gtk_button_cast(acceptButton), opts->labelText(QFileDialogOptions::Accept).toUtf8());
|
|
||||||
else*/ if (_acceptMode == QFileDialog::AcceptOpen)
|
|
||||||
Libs::gtk_button_set_label(Libs::gtk_button_cast(acceptButton), tr::lng_open_link(tr::now).toUtf8());
|
|
||||||
else
|
|
||||||
Libs::gtk_button_set_label(Libs::gtk_button_cast(acceptButton), tr::lng_settings_save(tr::now).toUtf8());
|
|
||||||
}
|
|
||||||
|
|
||||||
GtkWidget *rejectButton = Libs::gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_CANCEL);
|
|
||||||
if (rejectButton) {
|
|
||||||
/*if (opts->isLabelExplicitlySet(QFileDialogOptions::Reject))
|
|
||||||
Libs::gtk_button_set_label(Libs::gtk_button_cast(rejectButton), opts->labelText(QFileDialogOptions::Reject).toUtf8());
|
|
||||||
else*/
|
|
||||||
Libs::gtk_button_set_label(Libs::gtk_button_cast(rejectButton), tr::lng_cancel(tr::now).toUtf8());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GtkFileDialog::setNameFilters(const QStringList &filters) {
|
|
||||||
GtkDialog *gtkDialog = d->gtkDialog();
|
|
||||||
foreach (GtkFileFilter *filter, _filters)
|
|
||||||
Libs::gtk_file_chooser_remove_filter(Libs::gtk_file_chooser_cast(gtkDialog), filter);
|
|
||||||
|
|
||||||
_filters.clear();
|
|
||||||
_filterNames.clear();
|
|
||||||
|
|
||||||
for_const (auto &filter, filters) {
|
|
||||||
GtkFileFilter *gtkFilter = Libs::gtk_file_filter_new();
|
|
||||||
auto name = filter;//.left(filter.indexOf(QLatin1Char('(')));
|
|
||||||
auto extensions = cleanFilterList(filter);
|
|
||||||
|
|
||||||
Libs::gtk_file_filter_set_name(gtkFilter, name.isEmpty() ? extensions.join(QStringLiteral(", ")).toUtf8() : name.toUtf8());
|
|
||||||
for_const (auto &ext, extensions) {
|
|
||||||
auto caseInsensitiveExt = QString();
|
|
||||||
caseInsensitiveExt.reserve(4 * ext.size());
|
|
||||||
for_const (auto ch, ext) {
|
|
||||||
auto chLower = ch.toLower();
|
|
||||||
auto chUpper = ch.toUpper();
|
|
||||||
if (chLower != chUpper) {
|
|
||||||
caseInsensitiveExt.append('[').append(chLower).append(chUpper).append(']');
|
|
||||||
} else {
|
|
||||||
caseInsensitiveExt.append(ch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Libs::gtk_file_filter_add_pattern(gtkFilter, caseInsensitiveExt.toUtf8());
|
|
||||||
}
|
|
||||||
|
|
||||||
Libs::gtk_file_chooser_add_filter(Libs::gtk_file_chooser_cast(gtkDialog), gtkFilter);
|
|
||||||
|
|
||||||
_filters.insert(filter, gtkFilter);
|
|
||||||
_filterNames.insert(gtkFilter, filter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace internal
|
|
||||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
} // namespace FileDialog
|
} // namespace FileDialog
|
||||||
} // namespace Platform
|
} // namespace Platform
|
||||||
|
|
|
@ -9,15 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "platform/platform_file_utilities.h"
|
#include "platform/platform_file_utilities.h"
|
||||||
|
|
||||||
#include <QtGui/QWindow>
|
|
||||||
#include <QtWidgets/QFileDialog>
|
|
||||||
|
|
||||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
typedef struct _GtkWidget GtkWidget;
|
|
||||||
typedef struct _GtkDialog GtkDialog;
|
|
||||||
typedef struct _GtkFileFilter GtkFileFilter;
|
|
||||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
|
|
||||||
namespace Platform {
|
namespace Platform {
|
||||||
namespace File {
|
namespace File {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
@ -45,114 +36,5 @@ inline void InitLastPath() {
|
||||||
::FileDialog::internal::InitLastPathDefault();
|
::FileDialog::internal::InitLastPathDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace internal {
|
|
||||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
|
|
||||||
// This is a patched copy of qgtk2 theme plugin.
|
|
||||||
// We need to use our own gtk file dialog instead of
|
|
||||||
// styling Qt file dialog, because Qt only works with gtk2.
|
|
||||||
// We need to be able to work with gtk2 and gtk3, because
|
|
||||||
// we use gtk3 to work with appindicator3.
|
|
||||||
class QGtkDialog : public QWindow {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
QGtkDialog(GtkWidget *gtkWidget);
|
|
||||||
~QGtkDialog();
|
|
||||||
|
|
||||||
GtkDialog *gtkDialog() const;
|
|
||||||
|
|
||||||
void exec();
|
|
||||||
void show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent);
|
|
||||||
void hide();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void accept();
|
|
||||||
void reject();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
static void onResponse(QGtkDialog *dialog, int response);
|
|
||||||
static void onUpdatePreview(QGtkDialog *dialog);
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void onParentWindowDestroyed();
|
|
||||||
|
|
||||||
private:
|
|
||||||
GtkWidget *gtkWidget;
|
|
||||||
GtkWidget *_preview = nullptr;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
class GtkFileDialog : public QDialog {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
GtkFileDialog(QWidget *parent = Q_NULLPTR,
|
|
||||||
const QString &caption = QString(),
|
|
||||||
const QString &directory = QString(),
|
|
||||||
const QString &filter = QString());
|
|
||||||
~GtkFileDialog();
|
|
||||||
|
|
||||||
void setVisible(bool visible) override;
|
|
||||||
|
|
||||||
void setWindowTitle(const QString &windowTitle) {
|
|
||||||
_windowTitle = windowTitle;
|
|
||||||
}
|
|
||||||
void setAcceptMode(QFileDialog::AcceptMode acceptMode) {
|
|
||||||
_acceptMode = acceptMode;
|
|
||||||
}
|
|
||||||
void setFileMode(QFileDialog::FileMode fileMode) {
|
|
||||||
_fileMode = fileMode;
|
|
||||||
}
|
|
||||||
void setOption(QFileDialog::Option option, bool on = true) {
|
|
||||||
if (on) {
|
|
||||||
_options |= option;
|
|
||||||
} else {
|
|
||||||
_options &= ~option;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int exec() override;
|
|
||||||
|
|
||||||
bool defaultNameFilterDisables() const;
|
|
||||||
void setDirectory(const QString &directory);
|
|
||||||
QDir directory() const;
|
|
||||||
void selectFile(const QString &filename);
|
|
||||||
QStringList selectedFiles() const;
|
|
||||||
void setFilter();
|
|
||||||
void selectNameFilter(const QString &filter);
|
|
||||||
QString selectedNameFilter() const;
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void onAccepted();
|
|
||||||
void onRejected();
|
|
||||||
|
|
||||||
private:
|
|
||||||
static void onSelectionChanged(GtkDialog *dialog, GtkFileDialog *helper);
|
|
||||||
static void onCurrentFolderChanged(GtkFileDialog *helper);
|
|
||||||
void applyOptions();
|
|
||||||
void setNameFilters(const QStringList &filters);
|
|
||||||
|
|
||||||
void showHelper(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent);
|
|
||||||
void hideHelper();
|
|
||||||
|
|
||||||
// Options
|
|
||||||
QFileDialog::Options _options;
|
|
||||||
QString _windowTitle = "Choose file";
|
|
||||||
QString _initialDirectory;
|
|
||||||
QStringList _initialFiles;
|
|
||||||
QStringList _nameFilters;
|
|
||||||
QFileDialog::AcceptMode _acceptMode = QFileDialog::AcceptOpen;
|
|
||||||
QFileDialog::FileMode _fileMode = QFileDialog::ExistingFile;
|
|
||||||
|
|
||||||
QString _dir;
|
|
||||||
QStringList _selection;
|
|
||||||
QHash<QString, GtkFileFilter*> _filters;
|
|
||||||
QHash<GtkFileFilter*, QString> _filterNames;
|
|
||||||
QScopedPointer<QGtkDialog> d;
|
|
||||||
};
|
|
||||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
|
|
||||||
} // namespace internal
|
|
||||||
} // namespace FileDialog
|
} // namespace FileDialog
|
||||||
} // namespace Platform
|
} // namespace Platform
|
||||||
|
|
|
@ -5,10 +5,9 @@ the official desktop application for the Telegram messaging service.
|
||||||
For license and copyright information please follow this link:
|
For license and copyright information please follow this link:
|
||||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
#include "platform/linux/linux_gdk_helper.h"
|
#include "platform/linux/linux_gdk_helper.h"
|
||||||
|
|
||||||
#include "platform/linux/linux_libs.h"
|
#include "platform/linux/linux_gtk_integration_p.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#undef signals
|
#undef signals
|
||||||
|
@ -19,6 +18,8 @@ extern "C" {
|
||||||
namespace Platform {
|
namespace Platform {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
|
using namespace Platform::Gtk;
|
||||||
|
|
||||||
enum class GtkLoaded {
|
enum class GtkLoaded {
|
||||||
GtkNone,
|
GtkNone,
|
||||||
Gtk2,
|
Gtk2,
|
||||||
|
@ -44,7 +45,7 @@ f_gdk_x11_window_get_type gdk_x11_window_get_type = nullptr;
|
||||||
// To be able to compile with gtk-2.0 headers as well
|
// To be able to compile with gtk-2.0 headers as well
|
||||||
template <typename Object>
|
template <typename Object>
|
||||||
inline bool gdk_is_x11_window_check(Object *obj) {
|
inline bool gdk_is_x11_window_check(Object *obj) {
|
||||||
return Libs::g_type_cit_helper(obj, gdk_x11_window_get_type());
|
return g_type_cit_helper(obj, gdk_x11_window_get_type());
|
||||||
}
|
}
|
||||||
|
|
||||||
using f_gdk_window_get_display = GdkDisplay*(*)(GdkWindow *window);
|
using f_gdk_window_get_display = GdkDisplay*(*)(GdkWindow *window);
|
||||||
|
@ -57,20 +58,20 @@ using f_gdk_x11_window_get_xid = Window(*)(GdkWindow *window);
|
||||||
f_gdk_x11_window_get_xid gdk_x11_window_get_xid = nullptr;
|
f_gdk_x11_window_get_xid gdk_x11_window_get_xid = nullptr;
|
||||||
|
|
||||||
bool GdkHelperLoadGtk2(QLibrary &lib) {
|
bool GdkHelperLoadGtk2(QLibrary &lib) {
|
||||||
#if defined DESKTOP_APP_USE_PACKAGED && !defined DESKTOP_APP_USE_PACKAGED_LAZY
|
#ifdef LINK_TO_GTK
|
||||||
return false;
|
return false;
|
||||||
#else // DESKTOP_APP_USE_PACKAGED && !DESKTOP_APP_USE_PACKAGED_LAZY
|
#else // LINK_TO_GTK
|
||||||
if (!LOAD_SYMBOL(lib, "gdk_x11_drawable_get_xdisplay", gdk_x11_drawable_get_xdisplay)) return false;
|
if (!LOAD_GTK_SYMBOL(lib, "gdk_x11_drawable_get_xdisplay", gdk_x11_drawable_get_xdisplay)) return false;
|
||||||
if (!LOAD_SYMBOL(lib, "gdk_x11_drawable_get_xid", gdk_x11_drawable_get_xid)) return false;
|
if (!LOAD_GTK_SYMBOL(lib, "gdk_x11_drawable_get_xid", gdk_x11_drawable_get_xid)) return false;
|
||||||
return true;
|
return true;
|
||||||
#endif // !DESKTOP_APP_USE_PACKAGED || DESKTOP_APP_USE_PACKAGED_LAZY
|
#endif // !LINK_TO_GTK
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GdkHelperLoadGtk3(QLibrary &lib) {
|
bool GdkHelperLoadGtk3(QLibrary &lib) {
|
||||||
if (!LOAD_SYMBOL(lib, "gdk_x11_window_get_type", gdk_x11_window_get_type)) return false;
|
if (!LOAD_GTK_SYMBOL(lib, "gdk_x11_window_get_type", gdk_x11_window_get_type)) return false;
|
||||||
if (!LOAD_SYMBOL(lib, "gdk_window_get_display", gdk_window_get_display)) return false;
|
if (!LOAD_GTK_SYMBOL(lib, "gdk_window_get_display", gdk_window_get_display)) return false;
|
||||||
if (!LOAD_SYMBOL(lib, "gdk_x11_display_get_xdisplay", gdk_x11_display_get_xdisplay)) return false;
|
if (!LOAD_GTK_SYMBOL(lib, "gdk_x11_display_get_xdisplay", gdk_x11_display_get_xdisplay)) return false;
|
||||||
if (!LOAD_SYMBOL(lib, "gdk_x11_window_get_xid", gdk_x11_window_get_xid)) return false;
|
if (!LOAD_GTK_SYMBOL(lib, "gdk_x11_window_get_xid", gdk_x11_window_get_xid)) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,4 +104,3 @@ void XSetTransientForHint(GdkWindow *window, quintptr winId) {
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace Platform
|
} // namespace Platform
|
||||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
|
|
|
@ -7,11 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QtCore/QObject>
|
|
||||||
|
|
||||||
class QLibrary;
|
class QLibrary;
|
||||||
|
|
||||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#undef signals
|
#undef signals
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
@ -28,4 +25,3 @@ void XSetTransientForHint(GdkWindow *window, quintptr winId);
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace Platform
|
} // namespace Platform
|
||||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
|
|
714
Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp
Normal file
714
Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp
Normal file
|
@ -0,0 +1,714 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#include "platform/linux/linux_gtk_file_dialog.h"
|
||||||
|
|
||||||
|
#include "platform/linux/linux_gtk_integration_p.h"
|
||||||
|
#include "platform/linux/linux_gdk_helper.h"
|
||||||
|
#include "platform/linux/linux_desktop_environment.h"
|
||||||
|
#include "platform/linux/specific_linux.h"
|
||||||
|
#include "lang/lang_keys.h"
|
||||||
|
#include "storage/localstorage.h"
|
||||||
|
#include "base/qt_adapters.h"
|
||||||
|
|
||||||
|
#include <QtGui/QWindow>
|
||||||
|
#include <QtWidgets/QFileDialog>
|
||||||
|
#include <private/qguiapplication_p.h>
|
||||||
|
|
||||||
|
namespace Platform {
|
||||||
|
namespace FileDialog {
|
||||||
|
namespace Gtk {
|
||||||
|
|
||||||
|
using namespace Platform::Gtk;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// GTK file chooser image preview: thanks to Chromium
|
||||||
|
|
||||||
|
// The size of the preview we display for selected image files. We set height
|
||||||
|
// larger than width because generally there is more free space vertically
|
||||||
|
// than horiztonally (setting the preview image will alway expand the width of
|
||||||
|
// the dialog, but usually not the height). The image's aspect ratio will always
|
||||||
|
// be preserved.
|
||||||
|
constexpr auto kPreviewWidth = 256;
|
||||||
|
constexpr auto kPreviewHeight = 512;
|
||||||
|
|
||||||
|
const char *filterRegExp =
|
||||||
|
"^(.*)\\(([a-zA-Z0-9_.,*? +;#\\-\\[\\]@\\{\\}/!<>\\$%&=^~:\\|]*)\\)$";
|
||||||
|
|
||||||
|
QStringList makeFilterList(const QString &filter) {
|
||||||
|
QString f(filter);
|
||||||
|
|
||||||
|
if (f.isEmpty())
|
||||||
|
return QStringList();
|
||||||
|
|
||||||
|
QString sep(QLatin1String(";;"));
|
||||||
|
int i = f.indexOf(sep, 0);
|
||||||
|
if (i == -1) {
|
||||||
|
if (f.indexOf(QLatin1Char('\n'), 0) != -1) {
|
||||||
|
sep = QLatin1Char('\n');
|
||||||
|
i = f.indexOf(sep, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.split(sep);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Makes a list of filters from a normal filter string "Image Files (*.png *.jpg)"
|
||||||
|
QStringList cleanFilterList(const QString &filter) {
|
||||||
|
QRegExp regexp(QString::fromLatin1(filterRegExp));
|
||||||
|
Assert(regexp.isValid());
|
||||||
|
QString f = filter;
|
||||||
|
int i = regexp.indexIn(f);
|
||||||
|
if (i >= 0)
|
||||||
|
f = regexp.cap(2);
|
||||||
|
return f.split(QLatin1Char(' '), base::QStringSkipEmptyParts);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PreviewSupported() {
|
||||||
|
return (gdk_pixbuf_new_from_file_at_size != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CustomButtonsSupported() {
|
||||||
|
return (gtk_dialog_get_widget_for_response != nullptr)
|
||||||
|
&& (gtk_button_set_label != nullptr)
|
||||||
|
&& (gtk_button_get_type != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a patched copy of qgtk2 theme plugin.
|
||||||
|
// We need to use our own gtk file dialog instead of
|
||||||
|
// styling Qt file dialog, because Qt only works with gtk2.
|
||||||
|
// We need to be able to work with gtk2 and gtk3, because
|
||||||
|
// we use gtk3 to work with appindicator3.
|
||||||
|
class QGtkDialog : public QWindow {
|
||||||
|
public:
|
||||||
|
QGtkDialog(GtkWidget *gtkWidget);
|
||||||
|
~QGtkDialog();
|
||||||
|
|
||||||
|
GtkDialog *gtkDialog() const;
|
||||||
|
|
||||||
|
void exec();
|
||||||
|
void show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent);
|
||||||
|
void hide();
|
||||||
|
|
||||||
|
rpl::producer<> accept();
|
||||||
|
rpl::producer<> reject();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static void onResponse(QGtkDialog *dialog, int response);
|
||||||
|
static void onUpdatePreview(QGtkDialog *dialog);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void onParentWindowDestroyed();
|
||||||
|
|
||||||
|
GtkWidget *gtkWidget = nullptr;
|
||||||
|
GtkWidget *_preview = nullptr;
|
||||||
|
|
||||||
|
rpl::event_stream<> _accept;
|
||||||
|
rpl::event_stream<> _reject;
|
||||||
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class GtkFileDialog : public QDialog {
|
||||||
|
public:
|
||||||
|
GtkFileDialog(QWidget *parent = nullptr,
|
||||||
|
const QString &caption = QString(),
|
||||||
|
const QString &directory = QString(),
|
||||||
|
const QString &filter = QString());
|
||||||
|
~GtkFileDialog();
|
||||||
|
|
||||||
|
void setVisible(bool visible) override;
|
||||||
|
|
||||||
|
void setWindowTitle(const QString &windowTitle) {
|
||||||
|
_windowTitle = windowTitle;
|
||||||
|
}
|
||||||
|
void setAcceptMode(QFileDialog::AcceptMode acceptMode) {
|
||||||
|
_acceptMode = acceptMode;
|
||||||
|
}
|
||||||
|
void setFileMode(QFileDialog::FileMode fileMode) {
|
||||||
|
_fileMode = fileMode;
|
||||||
|
}
|
||||||
|
void setOption(QFileDialog::Option option, bool on = true) {
|
||||||
|
if (on) {
|
||||||
|
_options |= option;
|
||||||
|
} else {
|
||||||
|
_options &= ~option;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int exec() override;
|
||||||
|
|
||||||
|
bool defaultNameFilterDisables() const;
|
||||||
|
void setDirectory(const QString &directory);
|
||||||
|
QDir directory() const;
|
||||||
|
void selectFile(const QString &filename);
|
||||||
|
QStringList selectedFiles() const;
|
||||||
|
void setFilter();
|
||||||
|
void selectNameFilter(const QString &filter);
|
||||||
|
QString selectedNameFilter() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void onSelectionChanged(GtkDialog *dialog, GtkFileDialog *helper);
|
||||||
|
static void onCurrentFolderChanged(GtkFileDialog *helper);
|
||||||
|
void applyOptions();
|
||||||
|
void setNameFilters(const QStringList &filters);
|
||||||
|
|
||||||
|
void showHelper(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent);
|
||||||
|
void hideHelper();
|
||||||
|
|
||||||
|
void onAccepted();
|
||||||
|
void onRejected();
|
||||||
|
|
||||||
|
// Options
|
||||||
|
QFileDialog::Options _options;
|
||||||
|
QString _windowTitle = "Choose file";
|
||||||
|
QString _initialDirectory;
|
||||||
|
QStringList _initialFiles;
|
||||||
|
QStringList _nameFilters;
|
||||||
|
QFileDialog::AcceptMode _acceptMode = QFileDialog::AcceptOpen;
|
||||||
|
QFileDialog::FileMode _fileMode = QFileDialog::ExistingFile;
|
||||||
|
|
||||||
|
QString _dir;
|
||||||
|
QStringList _selection;
|
||||||
|
QHash<QString, GtkFileFilter*> _filters;
|
||||||
|
QHash<GtkFileFilter*, QString> _filterNames;
|
||||||
|
QScopedPointer<QGtkDialog> d;
|
||||||
|
|
||||||
|
rpl::lifetime _lifetime;
|
||||||
|
};
|
||||||
|
|
||||||
|
QGtkDialog::QGtkDialog(GtkWidget *gtkWidget) : gtkWidget(gtkWidget) {
|
||||||
|
g_signal_connect_swapped(G_OBJECT(gtkWidget), "response", G_CALLBACK(onResponse), this);
|
||||||
|
g_signal_connect(G_OBJECT(gtkWidget), "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), nullptr);
|
||||||
|
if (PreviewSupported()) {
|
||||||
|
_preview = gtk_image_new();
|
||||||
|
g_signal_connect_swapped(G_OBJECT(gtkWidget), "update-preview", G_CALLBACK(onUpdatePreview), this);
|
||||||
|
gtk_file_chooser_set_preview_widget(gtk_file_chooser_cast(gtkWidget), _preview);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QGtkDialog::~QGtkDialog() {
|
||||||
|
gtk_clipboard_store(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
|
||||||
|
gtk_widget_destroy(gtkWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkDialog *QGtkDialog::gtkDialog() const {
|
||||||
|
return gtk_dialog_cast(gtkWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QGtkDialog::exec() {
|
||||||
|
if (modality() == Qt::ApplicationModal) {
|
||||||
|
// block input to the whole app, including other GTK dialogs
|
||||||
|
gtk_dialog_run(gtkDialog());
|
||||||
|
} else {
|
||||||
|
// block input to the window, allow input to other GTK dialogs
|
||||||
|
QEventLoop loop;
|
||||||
|
|
||||||
|
accept(
|
||||||
|
) | rpl::start_with_next([=, &loop] {
|
||||||
|
loop.quit();
|
||||||
|
}, _lifetime);
|
||||||
|
|
||||||
|
reject(
|
||||||
|
) | rpl::start_with_next([=, &loop] {
|
||||||
|
loop.quit();
|
||||||
|
}, _lifetime);
|
||||||
|
|
||||||
|
loop.exec();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QGtkDialog::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) {
|
||||||
|
connect(parent, &QWindow::destroyed, this, [=] { onParentWindowDestroyed(); },
|
||||||
|
Qt::UniqueConnection);
|
||||||
|
setParent(parent);
|
||||||
|
setFlags(flags);
|
||||||
|
setModality(modality);
|
||||||
|
|
||||||
|
gtk_widget_realize(gtkWidget); // creates X window
|
||||||
|
|
||||||
|
if (parent) {
|
||||||
|
internal::XSetTransientForHint(gtk_widget_get_window(gtkWidget), parent->winId());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modality != Qt::NonModal) {
|
||||||
|
gdk_window_set_modal_hint(gtk_widget_get_window(gtkWidget), true);
|
||||||
|
QGuiApplicationPrivate::showModalWindow(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_widget_show(gtkWidget);
|
||||||
|
gdk_window_focus(gtk_widget_get_window(gtkWidget), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QGtkDialog::hide() {
|
||||||
|
QGuiApplicationPrivate::hideModalWindow(this);
|
||||||
|
gtk_widget_hide(gtkWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<> QGtkDialog::accept() {
|
||||||
|
return _accept.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<> QGtkDialog::reject() {
|
||||||
|
return _reject.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QGtkDialog::onResponse(QGtkDialog *dialog, int response) {
|
||||||
|
if (response == GTK_RESPONSE_OK)
|
||||||
|
dialog->_accept.fire({});
|
||||||
|
else
|
||||||
|
dialog->_reject.fire({});
|
||||||
|
}
|
||||||
|
|
||||||
|
void QGtkDialog::onUpdatePreview(QGtkDialog* dialog) {
|
||||||
|
auto filename = gtk_file_chooser_get_preview_filename(gtk_file_chooser_cast(dialog->gtkWidget));
|
||||||
|
if (!filename) {
|
||||||
|
gtk_file_chooser_set_preview_widget_active(gtk_file_chooser_cast(dialog->gtkWidget), false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't attempt to open anything which isn't a regular file. If a named pipe,
|
||||||
|
// this may hang. See https://crbug.com/534754.
|
||||||
|
struct stat stat_buf;
|
||||||
|
if (stat(filename, &stat_buf) != 0 || !S_ISREG(stat_buf.st_mode)) {
|
||||||
|
g_free(filename);
|
||||||
|
gtk_file_chooser_set_preview_widget_active(gtk_file_chooser_cast(dialog->gtkWidget), false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will preserve the image's aspect ratio.
|
||||||
|
auto pixbuf = gdk_pixbuf_new_from_file_at_size(filename, kPreviewWidth, kPreviewHeight, nullptr);
|
||||||
|
g_free(filename);
|
||||||
|
if (pixbuf) {
|
||||||
|
gtk_image_set_from_pixbuf(gtk_image_cast(dialog->_preview), pixbuf);
|
||||||
|
g_object_unref(pixbuf);
|
||||||
|
}
|
||||||
|
gtk_file_chooser_set_preview_widget_active(gtk_file_chooser_cast(dialog->gtkWidget), pixbuf ? true : false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QGtkDialog::onParentWindowDestroyed() {
|
||||||
|
// The Gtk*DialogHelper classes own this object. Make sure the parent doesn't delete it.
|
||||||
|
setParent(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkFileDialog::GtkFileDialog(QWidget *parent, const QString &caption, const QString &directory, const QString &filter) : QDialog(parent)
|
||||||
|
, _windowTitle(caption)
|
||||||
|
, _initialDirectory(directory) {
|
||||||
|
auto filters = makeFilterList(filter);
|
||||||
|
const int numFilters = filters.count();
|
||||||
|
_nameFilters.reserve(numFilters);
|
||||||
|
for (int i = 0; i < numFilters; ++i) {
|
||||||
|
_nameFilters << filters[i].simplified();
|
||||||
|
}
|
||||||
|
|
||||||
|
d.reset(new QGtkDialog(gtk_file_chooser_dialog_new("", nullptr,
|
||||||
|
GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||||
|
// https://developer.gnome.org/gtk3/stable/GtkFileChooserDialog.html#gtk-file-chooser-dialog-new
|
||||||
|
// first_button_text doesn't need explicit conversion to char*, while all others are vardict
|
||||||
|
tr::lng_cancel(tr::now).toUtf8(), GTK_RESPONSE_CANCEL,
|
||||||
|
tr::lng_box_ok(tr::now).toUtf8().constData(), GTK_RESPONSE_OK, nullptr)));
|
||||||
|
|
||||||
|
d.data()->accept(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
onAccepted();
|
||||||
|
}, _lifetime);
|
||||||
|
|
||||||
|
d.data()->reject(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
onRejected();
|
||||||
|
}, _lifetime);
|
||||||
|
|
||||||
|
g_signal_connect(gtk_file_chooser_cast(d->gtkDialog()), "selection-changed", G_CALLBACK(onSelectionChanged), this);
|
||||||
|
g_signal_connect_swapped(gtk_file_chooser_cast(d->gtkDialog()), "current-folder-changed", G_CALLBACK(onCurrentFolderChanged), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkFileDialog::~GtkFileDialog() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void GtkFileDialog::showHelper(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) {
|
||||||
|
_dir.clear();
|
||||||
|
_selection.clear();
|
||||||
|
|
||||||
|
applyOptions();
|
||||||
|
return d->show(flags, modality, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GtkFileDialog::setVisible(bool visible) {
|
||||||
|
if (visible) {
|
||||||
|
if (testAttribute(Qt::WA_WState_ExplicitShowHide) && !testAttribute(Qt::WA_WState_Hidden)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (testAttribute(Qt::WA_WState_ExplicitShowHide) && testAttribute(Qt::WA_WState_Hidden)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visible) {
|
||||||
|
showHelper(windowFlags(), windowModality(), parentWidget() ? parentWidget()->windowHandle() : nullptr);
|
||||||
|
} else {
|
||||||
|
hideHelper();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set WA_DontShowOnScreen so that QDialog::setVisible(visible) below
|
||||||
|
// updates the state correctly, but skips showing the non-native version:
|
||||||
|
setAttribute(Qt::WA_DontShowOnScreen);
|
||||||
|
|
||||||
|
QDialog::setVisible(visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
int GtkFileDialog::exec() {
|
||||||
|
d->setModality(windowModality());
|
||||||
|
|
||||||
|
bool deleteOnClose = testAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
setAttribute(Qt::WA_DeleteOnClose, false);
|
||||||
|
|
||||||
|
bool wasShowModal = testAttribute(Qt::WA_ShowModal);
|
||||||
|
setAttribute(Qt::WA_ShowModal, true);
|
||||||
|
setResult(0);
|
||||||
|
|
||||||
|
show();
|
||||||
|
|
||||||
|
QPointer<QDialog> guard = this;
|
||||||
|
d->exec();
|
||||||
|
if (guard.isNull())
|
||||||
|
return QDialog::Rejected;
|
||||||
|
|
||||||
|
setAttribute(Qt::WA_ShowModal, wasShowModal);
|
||||||
|
|
||||||
|
return result();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GtkFileDialog::hideHelper() {
|
||||||
|
// After GtkFileChooserDialog has been hidden, gtk_file_chooser_get_current_folder()
|
||||||
|
// & gtk_file_chooser_get_filenames() will return bogus values -> cache the actual
|
||||||
|
// values before hiding the dialog
|
||||||
|
_dir = directory().absolutePath();
|
||||||
|
_selection = selectedFiles();
|
||||||
|
|
||||||
|
d->hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GtkFileDialog::defaultNameFilterDisables() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GtkFileDialog::setDirectory(const QString &directory) {
|
||||||
|
GtkDialog *gtkDialog = d->gtkDialog();
|
||||||
|
gtk_file_chooser_set_current_folder(gtk_file_chooser_cast(gtkDialog), directory.toUtf8());
|
||||||
|
}
|
||||||
|
|
||||||
|
QDir GtkFileDialog::directory() const {
|
||||||
|
// While GtkFileChooserDialog is hidden, gtk_file_chooser_get_current_folder()
|
||||||
|
// returns a bogus value -> return the cached value before hiding
|
||||||
|
if (!_dir.isEmpty())
|
||||||
|
return _dir;
|
||||||
|
|
||||||
|
QString ret;
|
||||||
|
GtkDialog *gtkDialog = d->gtkDialog();
|
||||||
|
gchar *folder = gtk_file_chooser_get_current_folder(gtk_file_chooser_cast(gtkDialog));
|
||||||
|
if (folder) {
|
||||||
|
ret = QString::fromUtf8(folder);
|
||||||
|
g_free(folder);
|
||||||
|
}
|
||||||
|
return QDir(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GtkFileDialog::selectFile(const QString &filename) {
|
||||||
|
_initialFiles.clear();
|
||||||
|
_initialFiles.append(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList GtkFileDialog::selectedFiles() const {
|
||||||
|
// While GtkFileChooserDialog is hidden, gtk_file_chooser_get_filenames()
|
||||||
|
// returns a bogus value -> return the cached value before hiding
|
||||||
|
if (!_selection.isEmpty())
|
||||||
|
return _selection;
|
||||||
|
|
||||||
|
QStringList selection;
|
||||||
|
GtkDialog *gtkDialog = d->gtkDialog();
|
||||||
|
GSList *filenames = gtk_file_chooser_get_filenames(gtk_file_chooser_cast(gtkDialog));
|
||||||
|
for (GSList *it = filenames; it; it = it->next)
|
||||||
|
selection += QString::fromUtf8((const char*)it->data);
|
||||||
|
g_slist_free(filenames);
|
||||||
|
return selection;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GtkFileDialog::setFilter() {
|
||||||
|
applyOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GtkFileDialog::selectNameFilter(const QString &filter) {
|
||||||
|
GtkFileFilter *gtkFilter = _filters.value(filter);
|
||||||
|
if (gtkFilter) {
|
||||||
|
GtkDialog *gtkDialog = d->gtkDialog();
|
||||||
|
gtk_file_chooser_set_filter(gtk_file_chooser_cast(gtkDialog), gtkFilter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString GtkFileDialog::selectedNameFilter() const {
|
||||||
|
GtkDialog *gtkDialog = d->gtkDialog();
|
||||||
|
GtkFileFilter *gtkFilter = gtk_file_chooser_get_filter(gtk_file_chooser_cast(gtkDialog));
|
||||||
|
return _filterNames.value(gtkFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GtkFileDialog::onAccepted() {
|
||||||
|
emit accept();
|
||||||
|
|
||||||
|
// QString filter = selectedNameFilter();
|
||||||
|
// if (filter.isEmpty())
|
||||||
|
// emit filterSelected(filter);
|
||||||
|
|
||||||
|
// QList<QUrl> files = selectedFiles();
|
||||||
|
// emit filesSelected(files);
|
||||||
|
// if (files.count() == 1)
|
||||||
|
// emit fileSelected(files.first());
|
||||||
|
}
|
||||||
|
|
||||||
|
void GtkFileDialog::onRejected() {
|
||||||
|
emit reject();
|
||||||
|
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
void GtkFileDialog::onSelectionChanged(GtkDialog *gtkDialog, GtkFileDialog *helper) {
|
||||||
|
// QString selection;
|
||||||
|
// gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(gtkDialog));
|
||||||
|
// if (filename) {
|
||||||
|
// selection = QString::fromUtf8(filename);
|
||||||
|
// g_free(filename);
|
||||||
|
// }
|
||||||
|
// emit helper->currentChanged(QUrl::fromLocalFile(selection));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GtkFileDialog::onCurrentFolderChanged(GtkFileDialog *dialog) {
|
||||||
|
// emit dialog->directoryEntered(dialog->directory());
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkFileChooserAction gtkFileChooserAction(QFileDialog::FileMode fileMode, QFileDialog::AcceptMode acceptMode) {
|
||||||
|
switch (fileMode) {
|
||||||
|
case QFileDialog::AnyFile:
|
||||||
|
case QFileDialog::ExistingFile:
|
||||||
|
case QFileDialog::ExistingFiles:
|
||||||
|
if (acceptMode == QFileDialog::AcceptOpen)
|
||||||
|
return GTK_FILE_CHOOSER_ACTION_OPEN;
|
||||||
|
else
|
||||||
|
return GTK_FILE_CHOOSER_ACTION_SAVE;
|
||||||
|
case QFileDialog::Directory:
|
||||||
|
default:
|
||||||
|
if (acceptMode == QFileDialog::AcceptOpen)
|
||||||
|
return GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
|
||||||
|
else
|
||||||
|
return GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GtkFileDialog::applyOptions() {
|
||||||
|
GtkDialog *gtkDialog = d->gtkDialog();
|
||||||
|
|
||||||
|
gtk_window_set_title(gtk_window_cast(gtkDialog), _windowTitle.toUtf8());
|
||||||
|
gtk_file_chooser_set_local_only(gtk_file_chooser_cast(gtkDialog), true);
|
||||||
|
|
||||||
|
const GtkFileChooserAction action = gtkFileChooserAction(_fileMode, _acceptMode);
|
||||||
|
gtk_file_chooser_set_action(gtk_file_chooser_cast(gtkDialog), action);
|
||||||
|
|
||||||
|
const bool selectMultiple = (_fileMode == QFileDialog::ExistingFiles);
|
||||||
|
gtk_file_chooser_set_select_multiple(gtk_file_chooser_cast(gtkDialog), selectMultiple);
|
||||||
|
|
||||||
|
const bool confirmOverwrite = !_options.testFlag(QFileDialog::DontConfirmOverwrite);
|
||||||
|
gtk_file_chooser_set_do_overwrite_confirmation(gtk_file_chooser_cast(gtkDialog), confirmOverwrite);
|
||||||
|
|
||||||
|
if (!_nameFilters.isEmpty())
|
||||||
|
setNameFilters(_nameFilters);
|
||||||
|
|
||||||
|
if (!_initialDirectory.isEmpty())
|
||||||
|
setDirectory(_initialDirectory);
|
||||||
|
|
||||||
|
for_const (const auto &filename, _initialFiles) {
|
||||||
|
if (_acceptMode == QFileDialog::AcceptSave) {
|
||||||
|
QFileInfo fi(filename);
|
||||||
|
gtk_file_chooser_set_current_folder(gtk_file_chooser_cast(gtkDialog), fi.path().toUtf8());
|
||||||
|
gtk_file_chooser_set_current_name(gtk_file_chooser_cast(gtkDialog), fi.fileName().toUtf8());
|
||||||
|
} else if (filename.endsWith('/')) {
|
||||||
|
gtk_file_chooser_set_current_folder(gtk_file_chooser_cast(gtkDialog), filename.toUtf8());
|
||||||
|
} else {
|
||||||
|
gtk_file_chooser_select_filename(gtk_file_chooser_cast(gtkDialog), filename.toUtf8());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString initialNameFilter = _nameFilters.isEmpty() ? QString() : _nameFilters.front();
|
||||||
|
if (!initialNameFilter.isEmpty())
|
||||||
|
selectNameFilter(initialNameFilter);
|
||||||
|
|
||||||
|
if (CustomButtonsSupported()) {
|
||||||
|
GtkWidget *acceptButton = gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_OK);
|
||||||
|
if (acceptButton) {
|
||||||
|
/*if (opts->isLabelExplicitlySet(QFileDialogOptions::Accept))
|
||||||
|
gtk_button_set_label(gtk_button_cast(acceptButton), opts->labelText(QFileDialogOptions::Accept).toUtf8());
|
||||||
|
else*/ if (_acceptMode == QFileDialog::AcceptOpen)
|
||||||
|
gtk_button_set_label(gtk_button_cast(acceptButton), tr::lng_open_link(tr::now).toUtf8());
|
||||||
|
else
|
||||||
|
gtk_button_set_label(gtk_button_cast(acceptButton), tr::lng_settings_save(tr::now).toUtf8());
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkWidget *rejectButton = gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_CANCEL);
|
||||||
|
if (rejectButton) {
|
||||||
|
/*if (opts->isLabelExplicitlySet(QFileDialogOptions::Reject))
|
||||||
|
gtk_button_set_label(gtk_button_cast(rejectButton), opts->labelText(QFileDialogOptions::Reject).toUtf8());
|
||||||
|
else*/
|
||||||
|
gtk_button_set_label(gtk_button_cast(rejectButton), tr::lng_cancel(tr::now).toUtf8());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GtkFileDialog::setNameFilters(const QStringList &filters) {
|
||||||
|
GtkDialog *gtkDialog = d->gtkDialog();
|
||||||
|
foreach (GtkFileFilter *filter, _filters)
|
||||||
|
gtk_file_chooser_remove_filter(gtk_file_chooser_cast(gtkDialog), filter);
|
||||||
|
|
||||||
|
_filters.clear();
|
||||||
|
_filterNames.clear();
|
||||||
|
|
||||||
|
for_const (auto &filter, filters) {
|
||||||
|
GtkFileFilter *gtkFilter = gtk_file_filter_new();
|
||||||
|
auto name = filter;//.left(filter.indexOf(QLatin1Char('(')));
|
||||||
|
auto extensions = cleanFilterList(filter);
|
||||||
|
|
||||||
|
gtk_file_filter_set_name(gtkFilter, name.isEmpty() ? extensions.join(QStringLiteral(", ")).toUtf8() : name.toUtf8());
|
||||||
|
for_const (auto &ext, extensions) {
|
||||||
|
auto caseInsensitiveExt = QString();
|
||||||
|
caseInsensitiveExt.reserve(4 * ext.size());
|
||||||
|
for_const (auto ch, ext) {
|
||||||
|
auto chLower = ch.toLower();
|
||||||
|
auto chUpper = ch.toUpper();
|
||||||
|
if (chLower != chUpper) {
|
||||||
|
caseInsensitiveExt.append('[').append(chLower).append(chUpper).append(']');
|
||||||
|
} else {
|
||||||
|
caseInsensitiveExt.append(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_file_filter_add_pattern(gtkFilter, caseInsensitiveExt.toUtf8());
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_file_chooser_add_filter(gtk_file_chooser_cast(gtkDialog), gtkFilter);
|
||||||
|
|
||||||
|
_filters.insert(filter, gtkFilter);
|
||||||
|
_filterNames.insert(gtkFilter, filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool Supported() {
|
||||||
|
return internal::GdkHelperLoaded()
|
||||||
|
&& (gtk_widget_hide_on_delete != nullptr)
|
||||||
|
&& (gtk_clipboard_store != nullptr)
|
||||||
|
&& (gtk_clipboard_get != nullptr)
|
||||||
|
&& (gtk_widget_destroy != nullptr)
|
||||||
|
&& (gtk_dialog_get_type != nullptr)
|
||||||
|
&& (gtk_dialog_run != nullptr)
|
||||||
|
&& (gtk_widget_realize != nullptr)
|
||||||
|
&& (gdk_window_set_modal_hint != nullptr)
|
||||||
|
&& (gtk_widget_show != nullptr)
|
||||||
|
&& (gdk_window_focus != nullptr)
|
||||||
|
&& (gtk_widget_hide != nullptr)
|
||||||
|
&& (gtk_widget_hide_on_delete != nullptr)
|
||||||
|
&& (gtk_file_chooser_dialog_new != nullptr)
|
||||||
|
&& (gtk_file_chooser_get_type != nullptr)
|
||||||
|
&& (gtk_file_chooser_set_current_folder != nullptr)
|
||||||
|
&& (gtk_file_chooser_get_current_folder != nullptr)
|
||||||
|
&& (gtk_file_chooser_set_current_name != nullptr)
|
||||||
|
&& (gtk_file_chooser_select_filename != nullptr)
|
||||||
|
&& (gtk_file_chooser_get_filenames != nullptr)
|
||||||
|
&& (gtk_file_chooser_set_filter != nullptr)
|
||||||
|
&& (gtk_file_chooser_get_filter != nullptr)
|
||||||
|
&& (gtk_window_get_type != nullptr)
|
||||||
|
&& (gtk_window_set_title != nullptr)
|
||||||
|
&& (gtk_file_chooser_set_local_only != nullptr)
|
||||||
|
&& (gtk_file_chooser_set_action != nullptr)
|
||||||
|
&& (gtk_file_chooser_set_select_multiple != nullptr)
|
||||||
|
&& (gtk_file_chooser_set_do_overwrite_confirmation != nullptr)
|
||||||
|
&& (gtk_file_chooser_remove_filter != nullptr)
|
||||||
|
&& (gtk_file_filter_set_name != nullptr)
|
||||||
|
&& (gtk_file_filter_add_pattern != nullptr)
|
||||||
|
&& (gtk_file_chooser_add_filter != nullptr)
|
||||||
|
&& (gtk_file_filter_new != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Use(Type type) {
|
||||||
|
// use gtk file dialog on gtk-based desktop environments
|
||||||
|
// or if QT_QPA_PLATFORMTHEME=(gtk2|gtk3)
|
||||||
|
// or if portals are used and operation is to open folder
|
||||||
|
// and portal doesn't support folder choosing
|
||||||
|
const auto sandboxedOrCustomPortal = InFlatpak()
|
||||||
|
|| InSnap()
|
||||||
|
|| UseXDGDesktopPortal();
|
||||||
|
|
||||||
|
const auto neededForPortal = (type == Type::ReadFolder)
|
||||||
|
&& !CanOpenDirectoryWithPortal();
|
||||||
|
|
||||||
|
const auto neededNonForced = DesktopEnvironment::IsGtkBased()
|
||||||
|
|| (sandboxedOrCustomPortal && neededForPortal);
|
||||||
|
|
||||||
|
const auto excludeNonForced = sandboxedOrCustomPortal && !neededForPortal;
|
||||||
|
|
||||||
|
return IsGtkIntegrationForced()
|
||||||
|
|| (neededNonForced && !excludeNonForced);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Get(
|
||||||
|
QPointer<QWidget> parent,
|
||||||
|
QStringList &files,
|
||||||
|
QByteArray &remoteContent,
|
||||||
|
const QString &caption,
|
||||||
|
const QString &filter,
|
||||||
|
Type type,
|
||||||
|
QString startFile) {
|
||||||
|
GtkFileDialog dialog(parent, caption, QString(), filter);
|
||||||
|
|
||||||
|
dialog.setModal(true);
|
||||||
|
if (type == Type::ReadFile || type == Type::ReadFiles) {
|
||||||
|
dialog.setFileMode((type == Type::ReadFiles) ? QFileDialog::ExistingFiles : QFileDialog::ExistingFile);
|
||||||
|
dialog.setAcceptMode(QFileDialog::AcceptOpen);
|
||||||
|
} else if (type == Type::ReadFolder) {
|
||||||
|
dialog.setAcceptMode(QFileDialog::AcceptOpen);
|
||||||
|
dialog.setFileMode(QFileDialog::Directory);
|
||||||
|
dialog.setOption(QFileDialog::ShowDirsOnly);
|
||||||
|
} else {
|
||||||
|
dialog.setFileMode(QFileDialog::AnyFile);
|
||||||
|
dialog.setAcceptMode(QFileDialog::AcceptSave);
|
||||||
|
}
|
||||||
|
if (startFile.isEmpty() || startFile.at(0) != '/') {
|
||||||
|
startFile = cDialogLastPath() + '/' + startFile;
|
||||||
|
}
|
||||||
|
dialog.selectFile(startFile);
|
||||||
|
|
||||||
|
int res = dialog.exec();
|
||||||
|
|
||||||
|
QString path = dialog.directory().absolutePath();
|
||||||
|
if (path != cDialogLastPath()) {
|
||||||
|
cSetDialogLastPath(path);
|
||||||
|
Local::writeSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res == QDialog::Accepted) {
|
||||||
|
if (type == Type::ReadFiles) {
|
||||||
|
files = dialog.selectedFiles();
|
||||||
|
} else {
|
||||||
|
files = dialog.selectedFiles().mid(0, 1);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
files = QStringList();
|
||||||
|
remoteContent = QByteArray();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Gtk
|
||||||
|
} // namespace FileDialog
|
||||||
|
} // namespace Platform
|
31
Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.h
Normal file
31
Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/file_utilities.h"
|
||||||
|
|
||||||
|
namespace Platform {
|
||||||
|
namespace FileDialog {
|
||||||
|
namespace Gtk {
|
||||||
|
|
||||||
|
using Type = ::FileDialog::internal::Type;
|
||||||
|
|
||||||
|
bool Supported();
|
||||||
|
bool Use(Type type = Type::ReadFile);
|
||||||
|
bool Get(
|
||||||
|
QPointer<QWidget> parent,
|
||||||
|
QStringList &files,
|
||||||
|
QByteArray &remoteContent,
|
||||||
|
const QString &caption,
|
||||||
|
const QString &filter,
|
||||||
|
Type type,
|
||||||
|
QString startFile);
|
||||||
|
|
||||||
|
} // namespace Gtk
|
||||||
|
} // namespace FileDialog
|
||||||
|
} // namespace Platform
|
491
Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp
Normal file
491
Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp
Normal file
|
@ -0,0 +1,491 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#include "platform/linux/linux_gtk_integration.h"
|
||||||
|
|
||||||
|
#include "platform/linux/linux_gtk_integration_p.h"
|
||||||
|
#include "base/platform/base_platform_info.h"
|
||||||
|
#include "platform/linux/linux_xlib_helper.h"
|
||||||
|
#include "platform/linux/linux_gdk_helper.h"
|
||||||
|
#include "platform/linux/linux_gtk_file_dialog.h"
|
||||||
|
#include "platform/linux/linux_open_with_dialog.h"
|
||||||
|
#include "platform/linux/specific_linux.h"
|
||||||
|
#include "core/sandbox.h"
|
||||||
|
#include "core/core_settings.h"
|
||||||
|
#include "core/application.h"
|
||||||
|
#include "main/main_domain.h"
|
||||||
|
#include "mainwindow.h"
|
||||||
|
|
||||||
|
namespace Platform {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
using namespace Platform::Gtk;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
bool GtkTriedToInit = false;
|
||||||
|
bool GtkLoaded = false;
|
||||||
|
|
||||||
|
bool LoadLibrary(QLibrary &lib, const char *name, int version) {
|
||||||
|
#ifdef LINK_TO_GTK
|
||||||
|
return true;
|
||||||
|
#else // LINK_TO_GTK
|
||||||
|
DEBUG_LOG(("Loading '%1' with version %2...").arg(
|
||||||
|
QLatin1String(name)).arg(version));
|
||||||
|
lib.setFileNameAndVersion(QLatin1String(name), version);
|
||||||
|
if (lib.load()) {
|
||||||
|
DEBUG_LOG(("Loaded '%1' with version %2!").arg(
|
||||||
|
QLatin1String(name)).arg(version));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
lib.setFileNameAndVersion(QLatin1String(name), QString());
|
||||||
|
if (lib.load()) {
|
||||||
|
DEBUG_LOG(("Loaded '%1' without version!").arg(QLatin1String(name)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
LOG(("Could not load '%1' with version %2 :(").arg(
|
||||||
|
QLatin1String(name)).arg(version));
|
||||||
|
return false;
|
||||||
|
#endif // !LINK_TO_GTK
|
||||||
|
}
|
||||||
|
|
||||||
|
void GtkMessageHandler(
|
||||||
|
const gchar *log_domain,
|
||||||
|
GLogLevelFlags log_level,
|
||||||
|
const gchar *message,
|
||||||
|
gpointer unused_data) {
|
||||||
|
// Silence false-positive Gtk warnings (we are using Xlib to set
|
||||||
|
// the WM_TRANSIENT_FOR hint).
|
||||||
|
if (message != qstr("GtkDialog mapped without a transient parent. "
|
||||||
|
"This is discouraged.")) {
|
||||||
|
// For other messages, call the default handler.
|
||||||
|
g_log_default_handler(log_domain, log_level, message, unused_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetupGtkBase(QLibrary &lib_gtk) {
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_init_check", gtk_init_check)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_check_version", gtk_check_version)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_settings_get_default", gtk_settings_get_default)) return false;
|
||||||
|
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_widget_show", gtk_widget_show)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_widget_hide", gtk_widget_hide)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_widget_get_window", gtk_widget_get_window)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_widget_realize", gtk_widget_realize)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_widget_hide_on_delete", gtk_widget_hide_on_delete)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_widget_destroy", gtk_widget_destroy)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_clipboard_get", gtk_clipboard_get)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_clipboard_store", gtk_clipboard_store)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_clipboard_wait_for_contents", gtk_clipboard_wait_for_contents)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_clipboard_wait_for_image", gtk_clipboard_wait_for_image)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_selection_data_targets_include_image", gtk_selection_data_targets_include_image)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_selection_data_free", gtk_selection_data_free)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_dialog_new", gtk_file_chooser_dialog_new)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_get_type", gtk_file_chooser_get_type)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_image_get_type", gtk_image_get_type)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_set_current_folder", gtk_file_chooser_set_current_folder)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_get_current_folder", gtk_file_chooser_get_current_folder)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_set_current_name", gtk_file_chooser_set_current_name)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_select_filename", gtk_file_chooser_select_filename)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_get_filenames", gtk_file_chooser_get_filenames)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_set_filter", gtk_file_chooser_set_filter)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_get_filter", gtk_file_chooser_get_filter)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_window_get_type", gtk_window_get_type)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_window_set_title", gtk_window_set_title)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_set_local_only", gtk_file_chooser_set_local_only)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_set_action", gtk_file_chooser_set_action)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_set_select_multiple", gtk_file_chooser_set_select_multiple)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_set_do_overwrite_confirmation", gtk_file_chooser_set_do_overwrite_confirmation)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_remove_filter", gtk_file_chooser_remove_filter)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_filter_set_name", gtk_file_filter_set_name)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_filter_add_pattern", gtk_file_filter_add_pattern)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_add_filter", gtk_file_chooser_add_filter)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_set_preview_widget", gtk_file_chooser_set_preview_widget)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_get_preview_filename", gtk_file_chooser_get_preview_filename)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_chooser_set_preview_widget_active", gtk_file_chooser_set_preview_widget_active)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_file_filter_new", gtk_file_filter_new)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_image_new", gtk_image_new)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_image_set_from_pixbuf", gtk_image_set_from_pixbuf)) return false;
|
||||||
|
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gdk_window_set_modal_hint", gdk_window_set_modal_hint)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gdk_window_focus", gdk_window_focus)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_dialog_get_type", gtk_dialog_get_type)) return false;
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gtk_dialog_run", gtk_dialog_run)) return false;
|
||||||
|
|
||||||
|
if (!LOAD_GTK_SYMBOL(lib_gtk, "gdk_atom_intern", gdk_atom_intern)) return false;
|
||||||
|
|
||||||
|
if (LOAD_GTK_SYMBOL(lib_gtk, "gdk_set_allowed_backends", gdk_set_allowed_backends)) {
|
||||||
|
// We work only with Wayland and X11 GDK backends.
|
||||||
|
// Otherwise we get segfault in Ubuntu 17.04 in gtk_init_check() call.
|
||||||
|
// See https://github.com/telegramdesktop/tdesktop/issues/3176
|
||||||
|
// See https://github.com/telegramdesktop/tdesktop/issues/3162
|
||||||
|
if(IsWayland()) {
|
||||||
|
DEBUG_LOG(("Limit allowed GDK backends to wayland,x11"));
|
||||||
|
gdk_set_allowed_backends("wayland,x11");
|
||||||
|
} else {
|
||||||
|
DEBUG_LOG(("Limit allowed GDK backends to x11,wayland"));
|
||||||
|
gdk_set_allowed_backends("x11,wayland");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// gtk_init will reset the Xlib error handler,
|
||||||
|
// and that causes Qt applications to quit on X errors.
|
||||||
|
// Therefore, we need to manually restore it.
|
||||||
|
XErrorHandlerRestorer handlerRestorer;
|
||||||
|
|
||||||
|
DEBUG_LOG(("Library gtk functions loaded!"));
|
||||||
|
GtkTriedToInit = true;
|
||||||
|
if (!gtk_init_check(0, 0)) {
|
||||||
|
gtk_init_check = nullptr;
|
||||||
|
DEBUG_LOG(("Failed to gtk_init_check(0, 0)!"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DEBUG_LOG(("Checked gtk with gtk_init_check!"));
|
||||||
|
|
||||||
|
// Use our custom log handler.
|
||||||
|
g_log_set_handler("Gtk", G_LOG_LEVEL_MESSAGE, GtkMessageHandler, nullptr);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetImageFromClipboardSupported() {
|
||||||
|
return (gtk_clipboard_get != nullptr)
|
||||||
|
&& (gtk_clipboard_wait_for_contents != nullptr)
|
||||||
|
&& (gtk_clipboard_wait_for_image != nullptr)
|
||||||
|
&& (gtk_selection_data_targets_include_image != nullptr)
|
||||||
|
&& (gtk_selection_data_free != nullptr)
|
||||||
|
&& (gdk_pixbuf_get_pixels != nullptr)
|
||||||
|
&& (gdk_pixbuf_get_width != nullptr)
|
||||||
|
&& (gdk_pixbuf_get_height != nullptr)
|
||||||
|
&& (gdk_pixbuf_get_rowstride != nullptr)
|
||||||
|
&& (gdk_pixbuf_get_has_alpha != nullptr)
|
||||||
|
&& (gdk_atom_intern != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::optional<T> GtkSetting(const QString &propertyName) {
|
||||||
|
const auto integration = GtkIntegration::Instance();
|
||||||
|
if (!integration
|
||||||
|
|| !integration->loaded()
|
||||||
|
|| gtk_settings_get_default == nullptr) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
auto settings = gtk_settings_get_default();
|
||||||
|
T value;
|
||||||
|
g_object_get(settings, propertyName.toUtf8(), &value, nullptr);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IconThemeShouldBeSet() {
|
||||||
|
// change the icon theme only if
|
||||||
|
// it isn't already set by a platformtheme plugin
|
||||||
|
// if QT_QPA_PLATFORMTHEME=(gtk2|gtk3), then force-apply the icon theme
|
||||||
|
static const auto Result =
|
||||||
|
// QGenericUnixTheme
|
||||||
|
(QIcon::themeName() == qstr("hicolor")
|
||||||
|
&& QIcon::fallbackThemeName() == qstr("hicolor"))
|
||||||
|
// QGnomeTheme
|
||||||
|
|| (QIcon::themeName() == qstr("Adwaita")
|
||||||
|
&& QIcon::fallbackThemeName() == qstr("gnome"))
|
||||||
|
// qt5ct
|
||||||
|
|| (QIcon::themeName().isEmpty()
|
||||||
|
&& QIcon::fallbackThemeName().isEmpty())
|
||||||
|
|| IsGtkIntegrationForced();
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CursorSizeShouldBeSet() {
|
||||||
|
// change the cursor size only on Wayland and if it wasn't already set
|
||||||
|
static const auto Result = IsWayland()
|
||||||
|
&& qEnvironmentVariableIsEmpty("XCURSOR_SIZE");
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetIconTheme() {
|
||||||
|
Core::Sandbox::Instance().customEnterFromEventLoop([] {
|
||||||
|
const auto integration = GtkIntegration::Instance();
|
||||||
|
|
||||||
|
if (!integration
|
||||||
|
|| !IconThemeShouldBeSet()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto themeName = integration->getStringSetting(
|
||||||
|
qsl("gtk-icon-theme-name"));
|
||||||
|
|
||||||
|
const auto fallbackThemeName = integration->getStringSetting(
|
||||||
|
qsl("gtk-fallback-icon-theme"));
|
||||||
|
|
||||||
|
if (!themeName.has_value() || !fallbackThemeName.has_value()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_LOG(("Setting GTK icon theme"));
|
||||||
|
|
||||||
|
QIcon::setThemeName(*themeName);
|
||||||
|
QIcon::setFallbackThemeName(*fallbackThemeName);
|
||||||
|
|
||||||
|
DEBUG_LOG(("New icon theme: %1").arg(QIcon::themeName()));
|
||||||
|
DEBUG_LOG(("New fallback icon theme: %1").arg(
|
||||||
|
QIcon::fallbackThemeName()));
|
||||||
|
|
||||||
|
SetApplicationIcon(Window::CreateIcon());
|
||||||
|
if (App::wnd()) {
|
||||||
|
App::wnd()->setWindowIcon(Window::CreateIcon());
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::App().domain().notifyUnreadBadgeChanged();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetCursorSize() {
|
||||||
|
Core::Sandbox::Instance().customEnterFromEventLoop([] {
|
||||||
|
const auto integration = GtkIntegration::Instance();
|
||||||
|
|
||||||
|
if (!integration
|
||||||
|
|| !CursorSizeShouldBeSet()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto newCursorSize = integration->getIntSetting(
|
||||||
|
qsl("gtk-cursor-theme-size"));
|
||||||
|
|
||||||
|
if (!newCursorSize.has_value()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_LOG(("Setting GTK cursor size"));
|
||||||
|
qputenv("XCURSOR_SIZE", QByteArray::number(*newCursorSize));
|
||||||
|
DEBUG_LOG(("New cursor size: %1").arg(*newCursorSize));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void DarkModeChanged() {
|
||||||
|
Core::Sandbox::Instance().customEnterFromEventLoop([] {
|
||||||
|
Core::App().settings().setSystemDarkMode(IsDarkMode());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void DecorationLayoutChanged() {
|
||||||
|
Core::Sandbox::Instance().customEnterFromEventLoop([] {
|
||||||
|
Core::App().settings().setWindowControlsLayout(
|
||||||
|
WindowControlsLayout());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
GtkIntegration::GtkIntegration() {
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkIntegration *GtkIntegration::Instance() {
|
||||||
|
static const auto useGtkIntegration = !qEnvironmentVariableIsSet(
|
||||||
|
kDisableGtkIntegration.utf8());
|
||||||
|
|
||||||
|
if (!useGtkIntegration) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GtkIntegration instance;
|
||||||
|
return &instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GtkIntegration::load() {
|
||||||
|
DEBUG_LOG(("Loading GTK"));
|
||||||
|
|
||||||
|
QLibrary lib_gtk;
|
||||||
|
lib_gtk.setLoadHints(QLibrary::DeepBindHint);
|
||||||
|
|
||||||
|
if (LoadLibrary(lib_gtk, "gtk-3", 0)) {
|
||||||
|
GtkLoaded = SetupGtkBase(lib_gtk);
|
||||||
|
}
|
||||||
|
if (!GtkLoaded
|
||||||
|
&& !GtkTriedToInit
|
||||||
|
&& LoadLibrary(lib_gtk, "gtk-x11-2.0", 0)) {
|
||||||
|
GtkLoaded = SetupGtkBase(lib_gtk);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GtkLoaded) {
|
||||||
|
LOAD_GTK_SYMBOL(lib_gtk, "gdk_pixbuf_new_from_file_at_size", gdk_pixbuf_new_from_file_at_size);
|
||||||
|
LOAD_GTK_SYMBOL(lib_gtk, "gdk_pixbuf_get_has_alpha", gdk_pixbuf_get_has_alpha);
|
||||||
|
LOAD_GTK_SYMBOL(lib_gtk, "gdk_pixbuf_get_pixels", gdk_pixbuf_get_pixels);
|
||||||
|
LOAD_GTK_SYMBOL(lib_gtk, "gdk_pixbuf_get_width", gdk_pixbuf_get_width);
|
||||||
|
LOAD_GTK_SYMBOL(lib_gtk, "gdk_pixbuf_get_height", gdk_pixbuf_get_height);
|
||||||
|
LOAD_GTK_SYMBOL(lib_gtk, "gdk_pixbuf_get_rowstride", gdk_pixbuf_get_rowstride);
|
||||||
|
|
||||||
|
GdkHelperLoad(lib_gtk);
|
||||||
|
|
||||||
|
LOAD_GTK_SYMBOL(lib_gtk, "gtk_dialog_get_widget_for_response", gtk_dialog_get_widget_for_response);
|
||||||
|
LOAD_GTK_SYMBOL(lib_gtk, "gtk_button_set_label", gtk_button_set_label);
|
||||||
|
LOAD_GTK_SYMBOL(lib_gtk, "gtk_button_get_type", gtk_button_get_type);
|
||||||
|
|
||||||
|
LOAD_GTK_SYMBOL(lib_gtk, "gtk_app_chooser_dialog_new", gtk_app_chooser_dialog_new);
|
||||||
|
LOAD_GTK_SYMBOL(lib_gtk, "gtk_app_chooser_get_app_info", gtk_app_chooser_get_app_info);
|
||||||
|
LOAD_GTK_SYMBOL(lib_gtk, "gtk_app_chooser_get_type", gtk_app_chooser_get_type);
|
||||||
|
|
||||||
|
SetIconTheme();
|
||||||
|
SetCursorSize();
|
||||||
|
|
||||||
|
const auto settings = gtk_settings_get_default();
|
||||||
|
|
||||||
|
g_signal_connect(
|
||||||
|
settings,
|
||||||
|
"notify::gtk-icon-theme-name",
|
||||||
|
G_CALLBACK(SetIconTheme),
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
g_signal_connect(
|
||||||
|
settings,
|
||||||
|
"notify::gtk-theme-name",
|
||||||
|
G_CALLBACK(DarkModeChanged),
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
g_signal_connect(
|
||||||
|
settings,
|
||||||
|
"notify::gtk-cursor-theme-size",
|
||||||
|
G_CALLBACK(SetCursorSize),
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
if (checkVersion(3, 0, 0)) {
|
||||||
|
g_signal_connect(
|
||||||
|
settings,
|
||||||
|
"notify::gtk-application-prefer-dark-theme",
|
||||||
|
G_CALLBACK(DarkModeChanged),
|
||||||
|
nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkVersion(3, 12, 0)) {
|
||||||
|
g_signal_connect(
|
||||||
|
settings,
|
||||||
|
"notify::gtk-decoration-layout",
|
||||||
|
G_CALLBACK(DecorationLayoutChanged),
|
||||||
|
nullptr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG(("Could not load gtk-3 or gtk-x11-2.0!"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GtkIntegration::loaded() const {
|
||||||
|
return GtkLoaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GtkIntegration::checkVersion(uint major, uint minor, uint micro) const {
|
||||||
|
return (loaded() && gtk_check_version != nullptr)
|
||||||
|
? !gtk_check_version(major, minor, micro)
|
||||||
|
: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<bool> GtkIntegration::getBoolSetting(
|
||||||
|
const QString &propertyName) const {
|
||||||
|
const auto value = GtkSetting<gboolean>(propertyName);
|
||||||
|
if (!value.has_value()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
DEBUG_LOG(("Getting GTK setting, %1: %2")
|
||||||
|
.arg(propertyName)
|
||||||
|
.arg(Logs::b(*value)));
|
||||||
|
return *value;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<int> GtkIntegration::getIntSetting(
|
||||||
|
const QString &propertyName) const {
|
||||||
|
const auto value = GtkSetting<gint>(propertyName);
|
||||||
|
if (value.has_value()) {
|
||||||
|
DEBUG_LOG(("Getting GTK setting, %1: %2")
|
||||||
|
.arg(propertyName)
|
||||||
|
.arg(*value));
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<QString> GtkIntegration::getStringSetting(
|
||||||
|
const QString &propertyName) const {
|
||||||
|
auto value = GtkSetting<gchararray>(propertyName);
|
||||||
|
if (!value.has_value()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
const auto str = QString::fromUtf8(*value);
|
||||||
|
g_free(*value);
|
||||||
|
DEBUG_LOG(("Getting GTK setting, %1: '%2'").arg(propertyName).arg(str));
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GtkIntegration::fileDialogSupported() const {
|
||||||
|
return FileDialog::Gtk::Supported();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GtkIntegration::useFileDialog(FileDialogType type) const {
|
||||||
|
return FileDialog::Gtk::Use(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GtkIntegration::getFileDialog(
|
||||||
|
QPointer<QWidget> parent,
|
||||||
|
QStringList &files,
|
||||||
|
QByteArray &remoteContent,
|
||||||
|
const QString &caption,
|
||||||
|
const QString &filter,
|
||||||
|
FileDialogType type,
|
||||||
|
QString startFile) const {
|
||||||
|
return FileDialog::Gtk::Get(
|
||||||
|
parent,
|
||||||
|
files,
|
||||||
|
remoteContent,
|
||||||
|
caption,
|
||||||
|
filter,
|
||||||
|
type,
|
||||||
|
startFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GtkIntegration::showOpenWithDialog(const QString &filepath) const {
|
||||||
|
return File::internal::ShowOpenWithDialog(filepath);
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage GtkIntegration::getImageFromClipboard() const {
|
||||||
|
QImage data;
|
||||||
|
|
||||||
|
if (!GetImageFromClipboardSupported()) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
|
||||||
|
if (!clipboard) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto gsel = gtk_clipboard_wait_for_contents(
|
||||||
|
clipboard,
|
||||||
|
gdk_atom_intern("TARGETS", true));
|
||||||
|
|
||||||
|
if (gsel) {
|
||||||
|
if (gtk_selection_data_targets_include_image(gsel, false)) {
|
||||||
|
auto img = gtk_clipboard_wait_for_image(clipboard);
|
||||||
|
|
||||||
|
if (img) {
|
||||||
|
data = QImage(
|
||||||
|
gdk_pixbuf_get_pixels(img),
|
||||||
|
gdk_pixbuf_get_width(img),
|
||||||
|
gdk_pixbuf_get_height(img),
|
||||||
|
gdk_pixbuf_get_rowstride(img),
|
||||||
|
gdk_pixbuf_get_has_alpha(img)
|
||||||
|
? QImage::Format_RGBA8888
|
||||||
|
: QImage::Format_RGB888).copy();
|
||||||
|
|
||||||
|
g_object_unref(img);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_selection_data_free(gsel);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace Platform
|
59
Telegram/SourceFiles/platform/linux/linux_gtk_integration.h
Normal file
59
Telegram/SourceFiles/platform/linux/linux_gtk_integration.h
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/file_utilities.h"
|
||||||
|
|
||||||
|
namespace Platform {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
inline constexpr auto kDisableGtkIntegration = "TDESKTOP_DISABLE_GTK_INTEGRATION"_cs;
|
||||||
|
|
||||||
|
class GtkIntegration {
|
||||||
|
public:
|
||||||
|
static GtkIntegration *Instance();
|
||||||
|
|
||||||
|
void load();
|
||||||
|
[[nodiscard]] bool loaded() const;
|
||||||
|
[[nodiscard]] bool checkVersion(
|
||||||
|
uint major,
|
||||||
|
uint minor,
|
||||||
|
uint micro) const;
|
||||||
|
|
||||||
|
[[nodiscard]] std::optional<bool> getBoolSetting(
|
||||||
|
const QString &propertyName) const;
|
||||||
|
|
||||||
|
[[nodiscard]] std::optional<int> getIntSetting(
|
||||||
|
const QString &propertyName) const;
|
||||||
|
|
||||||
|
[[nodiscard]] std::optional<QString> getStringSetting(
|
||||||
|
const QString &propertyName) const;
|
||||||
|
|
||||||
|
using FileDialogType = ::FileDialog::internal::Type;
|
||||||
|
[[nodiscard]] bool fileDialogSupported() const;
|
||||||
|
[[nodiscard]] bool useFileDialog(
|
||||||
|
FileDialogType type = FileDialogType::ReadFile) const;
|
||||||
|
[[nodiscard]] bool getFileDialog(
|
||||||
|
QPointer<QWidget> parent,
|
||||||
|
QStringList &files,
|
||||||
|
QByteArray &remoteContent,
|
||||||
|
const QString &caption,
|
||||||
|
const QString &filter,
|
||||||
|
FileDialogType type,
|
||||||
|
QString startFile) const;
|
||||||
|
|
||||||
|
[[nodiscard]] bool showOpenWithDialog(const QString &filepath) const;
|
||||||
|
|
||||||
|
[[nodiscard]] QImage getImageFromClipboard() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
GtkIntegration();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace Platform
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#include "platform/linux/linux_gtk_integration.h"
|
||||||
|
|
||||||
|
namespace Platform {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
GtkIntegration::GtkIntegration() {
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkIntegration *GtkIntegration::Instance() {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GtkIntegration::load() {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GtkIntegration::loaded() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GtkIntegration::checkVersion(uint major, uint minor, uint micro) const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<bool> GtkIntegration::getBoolSetting(
|
||||||
|
const QString &propertyName) const {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<int> GtkIntegration::getIntSetting(
|
||||||
|
const QString &propertyName) const {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<QString> GtkIntegration::getStringSetting(
|
||||||
|
const QString &propertyName) const {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GtkIntegration::fileDialogSupported() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GtkIntegration::useFileDialog(FileDialogType type) const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GtkIntegration::getFileDialog(
|
||||||
|
QPointer<QWidget> parent,
|
||||||
|
QStringList &files,
|
||||||
|
QByteArray &remoteContent,
|
||||||
|
const QString &caption,
|
||||||
|
const QString &filter,
|
||||||
|
FileDialogType type,
|
||||||
|
QString startFile) const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GtkIntegration::showOpenWithDialog(const QString &filepath) const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage GtkIntegration::getImageFromClipboard() const {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace Platform
|
158
Telegram/SourceFiles/platform/linux/linux_gtk_integration_p.h
Normal file
158
Telegram/SourceFiles/platform/linux/linux_gtk_integration_p.h
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QtCore/QLibrary>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#undef signals
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
#include <gdk/gdk.h>
|
||||||
|
#define signals public
|
||||||
|
} // extern "C"
|
||||||
|
|
||||||
|
#if defined DESKTOP_APP_USE_PACKAGED && !defined DESKTOP_APP_USE_PACKAGED_LAZY
|
||||||
|
#define LINK_TO_GTK
|
||||||
|
#endif // DESKTOP_APP_USE_PACKAGED && !DESKTOP_APP_USE_PACKAGED_LAZY
|
||||||
|
|
||||||
|
#ifdef LINK_TO_GTK
|
||||||
|
#define LOAD_GTK_SYMBOL(lib, name, func) (func = ::func)
|
||||||
|
#else // LINK_TO_GTK
|
||||||
|
#define LOAD_GTK_SYMBOL Platform::Gtk::LoadSymbol
|
||||||
|
#endif // !LINK_TO_GTK
|
||||||
|
|
||||||
|
// To be able to compile with gtk-2.0 headers as well
|
||||||
|
typedef struct _GtkAppChooser GtkAppChooser;
|
||||||
|
|
||||||
|
namespace Platform {
|
||||||
|
namespace Gtk {
|
||||||
|
|
||||||
|
template <typename Function>
|
||||||
|
bool LoadSymbol(QLibrary &lib, const char *name, Function &func) {
|
||||||
|
func = nullptr;
|
||||||
|
if (!lib.isLoaded()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
func = reinterpret_cast<Function>(lib.resolve(name));
|
||||||
|
if (func) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
LOG(("Error: failed to load '%1' function!").arg(name));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline gboolean (*gtk_init_check)(int *argc, char ***argv) = nullptr;
|
||||||
|
inline const gchar* (*gtk_check_version)(guint required_major, guint required_minor, guint required_micro) = nullptr;
|
||||||
|
inline GtkSettings* (*gtk_settings_get_default)(void) = nullptr;
|
||||||
|
inline void (*gtk_widget_show)(GtkWidget *widget) = nullptr;
|
||||||
|
inline void (*gtk_widget_hide)(GtkWidget *widget) = nullptr;
|
||||||
|
inline GdkWindow* (*gtk_widget_get_window)(GtkWidget *widget) = nullptr;
|
||||||
|
inline void (*gtk_widget_realize)(GtkWidget *widget) = nullptr;
|
||||||
|
inline gboolean (*gtk_widget_hide_on_delete)(GtkWidget *widget) = nullptr;
|
||||||
|
inline void (*gtk_widget_destroy)(GtkWidget *widget) = nullptr;
|
||||||
|
inline GtkClipboard* (*gtk_clipboard_get)(GdkAtom selection) = nullptr;
|
||||||
|
inline void (*gtk_clipboard_store)(GtkClipboard *clipboard) = nullptr;
|
||||||
|
inline GtkSelectionData* (*gtk_clipboard_wait_for_contents)(GtkClipboard *clipboard, GdkAtom target) = nullptr;
|
||||||
|
inline GdkPixbuf* (*gtk_clipboard_wait_for_image)(GtkClipboard *clipboard) = nullptr;
|
||||||
|
inline gboolean (*gtk_selection_data_targets_include_image)(const GtkSelectionData *selection_data, gboolean writable) = nullptr;
|
||||||
|
inline void (*gtk_selection_data_free)(GtkSelectionData *data) = nullptr;
|
||||||
|
inline GtkWidget* (*gtk_file_chooser_dialog_new)(const gchar *title, GtkWindow *parent, GtkFileChooserAction action, const gchar *first_button_text, ...) G_GNUC_NULL_TERMINATED = nullptr;
|
||||||
|
inline gboolean (*gtk_file_chooser_set_current_folder)(GtkFileChooser *chooser, const gchar *filename) = nullptr;
|
||||||
|
inline gchar* (*gtk_file_chooser_get_current_folder)(GtkFileChooser *chooser) = nullptr;
|
||||||
|
inline void (*gtk_file_chooser_set_current_name)(GtkFileChooser *chooser, const gchar *name) = nullptr;
|
||||||
|
inline gboolean (*gtk_file_chooser_select_filename)(GtkFileChooser *chooser, const gchar *filename) = nullptr;
|
||||||
|
inline GSList* (*gtk_file_chooser_get_filenames)(GtkFileChooser *chooser) = nullptr;
|
||||||
|
inline void (*gtk_file_chooser_set_filter)(GtkFileChooser *chooser, GtkFileFilter *filter) = nullptr;
|
||||||
|
inline GtkFileFilter* (*gtk_file_chooser_get_filter)(GtkFileChooser *chooser) = nullptr;
|
||||||
|
inline void (*gtk_window_set_title)(GtkWindow *window, const gchar *title) = nullptr;
|
||||||
|
inline void (*gtk_file_chooser_set_local_only)(GtkFileChooser *chooser, gboolean local_only) = nullptr;
|
||||||
|
inline void (*gtk_file_chooser_set_action)(GtkFileChooser *chooser, GtkFileChooserAction action) = nullptr;
|
||||||
|
inline void (*gtk_file_chooser_set_select_multiple)(GtkFileChooser *chooser, gboolean select_multiple) = nullptr;
|
||||||
|
inline void (*gtk_file_chooser_set_do_overwrite_confirmation)(GtkFileChooser *chooser, gboolean do_overwrite_confirmation) = nullptr;
|
||||||
|
inline GtkWidget* (*gtk_dialog_get_widget_for_response)(GtkDialog *dialog, gint response_id) = nullptr;
|
||||||
|
inline void (*gtk_button_set_label)(GtkButton *button, const gchar *label) = nullptr;
|
||||||
|
inline void (*gtk_file_chooser_remove_filter)(GtkFileChooser *chooser, GtkFileFilter *filter) = nullptr;
|
||||||
|
inline void (*gtk_file_filter_set_name)(GtkFileFilter *filter, const gchar *name) = nullptr;
|
||||||
|
inline void (*gtk_file_filter_add_pattern)(GtkFileFilter *filter, const gchar *pattern) = nullptr;
|
||||||
|
inline void (*gtk_file_chooser_add_filter)(GtkFileChooser *chooser, GtkFileFilter *filter) = nullptr;
|
||||||
|
inline void (*gtk_file_chooser_set_preview_widget)(GtkFileChooser *chooser, GtkWidget *preview_widget) = nullptr;
|
||||||
|
inline gchar* (*gtk_file_chooser_get_preview_filename)(GtkFileChooser *chooser) = nullptr;
|
||||||
|
inline void (*gtk_file_chooser_set_preview_widget_active)(GtkFileChooser *chooser, gboolean active) = nullptr;
|
||||||
|
inline GtkFileFilter* (*gtk_file_filter_new)(void) = nullptr;
|
||||||
|
inline GtkWidget* (*gtk_image_new)(void) = nullptr;
|
||||||
|
inline void (*gtk_image_set_from_pixbuf)(GtkImage *image, GdkPixbuf *pixbuf) = nullptr;
|
||||||
|
inline GtkWidget* (*gtk_app_chooser_dialog_new)(GtkWindow *parent, GtkDialogFlags flags, GFile *file) = nullptr;
|
||||||
|
inline GAppInfo* (*gtk_app_chooser_get_app_info)(GtkAppChooser *self) = nullptr;
|
||||||
|
inline void (*gdk_set_allowed_backends)(const gchar *backends) = nullptr;
|
||||||
|
inline void (*gdk_window_set_modal_hint)(GdkWindow *window, gboolean modal) = nullptr;
|
||||||
|
inline void (*gdk_window_focus)(GdkWindow *window, guint32 timestamp) = nullptr;
|
||||||
|
|
||||||
|
template <typename Result, typename Object>
|
||||||
|
inline Result *g_type_cic_helper(Object *instance, GType iface_type) {
|
||||||
|
return reinterpret_cast<Result*>(g_type_check_instance_cast(reinterpret_cast<GTypeInstance*>(instance), iface_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline GType (*gtk_dialog_get_type)(void) G_GNUC_CONST = nullptr;
|
||||||
|
template <typename Object>
|
||||||
|
inline GtkDialog *gtk_dialog_cast(Object *obj) {
|
||||||
|
return g_type_cic_helper<GtkDialog, Object>(obj, gtk_dialog_get_type());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline GType (*gtk_file_chooser_get_type)(void) G_GNUC_CONST = nullptr;
|
||||||
|
template <typename Object>
|
||||||
|
inline GtkFileChooser *gtk_file_chooser_cast(Object *obj) {
|
||||||
|
return g_type_cic_helper<GtkFileChooser, Object>(obj, gtk_file_chooser_get_type());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline GType (*gtk_image_get_type)(void) G_GNUC_CONST = nullptr;
|
||||||
|
template <typename Object>
|
||||||
|
inline GtkImage *gtk_image_cast(Object *obj) {
|
||||||
|
return g_type_cic_helper<GtkImage, Object>(obj, gtk_image_get_type());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline GType (*gtk_button_get_type)(void) G_GNUC_CONST = nullptr;
|
||||||
|
template <typename Object>
|
||||||
|
inline GtkButton *gtk_button_cast(Object *obj) {
|
||||||
|
return g_type_cic_helper<GtkButton, Object>(obj, gtk_button_get_type());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline GType (*gtk_window_get_type)(void) G_GNUC_CONST = nullptr;
|
||||||
|
template <typename Object>
|
||||||
|
inline GtkWindow *gtk_window_cast(Object *obj) {
|
||||||
|
return g_type_cic_helper<GtkWindow, Object>(obj, gtk_window_get_type());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline GType (*gtk_app_chooser_get_type)(void) G_GNUC_CONST = nullptr;
|
||||||
|
template <typename Object>
|
||||||
|
inline GtkAppChooser *gtk_app_chooser_cast(Object *obj) {
|
||||||
|
return g_type_cic_helper<GtkAppChooser, Object>(obj, gtk_app_chooser_get_type());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Object>
|
||||||
|
inline bool g_type_cit_helper(Object *instance, GType iface_type) {
|
||||||
|
if (!instance) return false;
|
||||||
|
|
||||||
|
auto ginstance = reinterpret_cast<GTypeInstance*>(instance);
|
||||||
|
if (ginstance->g_class && ginstance->g_class->g_type == iface_type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return g_type_check_instance_is_a(ginstance, iface_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline gint (*gtk_dialog_run)(GtkDialog *dialog) = nullptr;
|
||||||
|
inline GdkAtom (*gdk_atom_intern)(const gchar *atom_name, gboolean only_if_exists) = nullptr;
|
||||||
|
inline GdkPixbuf* (*gdk_pixbuf_new_from_file_at_size)(const gchar *filename, int width, int height, GError **error) = nullptr;
|
||||||
|
inline gboolean (*gdk_pixbuf_get_has_alpha)(const GdkPixbuf *pixbuf) = nullptr;
|
||||||
|
inline guchar* (*gdk_pixbuf_get_pixels)(const GdkPixbuf *pixbuf) = nullptr;
|
||||||
|
inline int (*gdk_pixbuf_get_width)(const GdkPixbuf *pixbuf) = nullptr;
|
||||||
|
inline int (*gdk_pixbuf_get_height)(const GdkPixbuf *pixbuf) = nullptr;
|
||||||
|
inline int (*gdk_pixbuf_get_rowstride)(const GdkPixbuf *pixbuf) = nullptr;
|
||||||
|
|
||||||
|
} // namespace Gtk
|
||||||
|
} // namespace Platform
|
|
@ -1,360 +0,0 @@
|
||||||
/*
|
|
||||||
This file is part of Telegram Desktop,
|
|
||||||
the official desktop application for the Telegram messaging service.
|
|
||||||
|
|
||||||
For license and copyright information please follow this link:
|
|
||||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|
||||||
*/
|
|
||||||
#include "platform/linux/linux_libs.h"
|
|
||||||
|
|
||||||
#include "base/platform/base_platform_info.h"
|
|
||||||
#include "platform/linux/linux_xlib_helper.h"
|
|
||||||
#include "platform/linux/linux_gdk_helper.h"
|
|
||||||
#include "platform/linux/specific_linux.h"
|
|
||||||
#include "core/sandbox.h"
|
|
||||||
#include "core/core_settings.h"
|
|
||||||
#include "core/application.h"
|
|
||||||
#include "main/main_domain.h"
|
|
||||||
#include "mainwindow.h"
|
|
||||||
|
|
||||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
using Platform::internal::XErrorHandlerRestorer;
|
|
||||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
|
|
||||||
namespace Platform {
|
|
||||||
namespace Libs {
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
bool gtkTriedToInit = false;
|
|
||||||
bool gtkLoaded = false;
|
|
||||||
|
|
||||||
bool loadLibrary(QLibrary &lib, const char *name, int version) {
|
|
||||||
#if defined DESKTOP_APP_USE_PACKAGED && !defined DESKTOP_APP_USE_PACKAGED_LAZY
|
|
||||||
return true;
|
|
||||||
#else // DESKTOP_APP_USE_PACKAGED && !DESKTOP_APP_USE_PACKAGED_LAZY
|
|
||||||
DEBUG_LOG(("Loading '%1' with version %2...").arg(QLatin1String(name)).arg(version));
|
|
||||||
lib.setFileNameAndVersion(QLatin1String(name), version);
|
|
||||||
if (lib.load()) {
|
|
||||||
DEBUG_LOG(("Loaded '%1' with version %2!").arg(QLatin1String(name)).arg(version));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
lib.setFileNameAndVersion(QLatin1String(name), QString());
|
|
||||||
if (lib.load()) {
|
|
||||||
DEBUG_LOG(("Loaded '%1' without version!").arg(QLatin1String(name)));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
LOG(("Could not load '%1' with version %2 :(").arg(QLatin1String(name)).arg(version));
|
|
||||||
return false;
|
|
||||||
#endif // !DESKTOP_APP_USE_PACKAGED || DESKTOP_APP_USE_PACKAGED_LAZY
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
void gtkMessageHandler(
|
|
||||||
const gchar *log_domain,
|
|
||||||
GLogLevelFlags log_level,
|
|
||||||
const gchar *message,
|
|
||||||
gpointer unused_data) {
|
|
||||||
// Silence false-positive Gtk warnings (we are using Xlib to set
|
|
||||||
// the WM_TRANSIENT_FOR hint).
|
|
||||||
if (message != qstr("GtkDialog mapped without a transient parent. "
|
|
||||||
"This is discouraged.")) {
|
|
||||||
// For other messages, call the default handler.
|
|
||||||
g_log_default_handler(log_domain, log_level, message, unused_data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool setupGtkBase(QLibrary &lib_gtk) {
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_init_check", gtk_init_check)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_check_version", gtk_check_version)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_settings_get_default", gtk_settings_get_default)) return false;
|
|
||||||
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_widget_show", gtk_widget_show)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_widget_hide", gtk_widget_hide)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_widget_get_window", gtk_widget_get_window)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_widget_realize", gtk_widget_realize)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_widget_hide_on_delete", gtk_widget_hide_on_delete)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_widget_destroy", gtk_widget_destroy)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_clipboard_get", gtk_clipboard_get)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_clipboard_store", gtk_clipboard_store)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_clipboard_wait_for_contents", gtk_clipboard_wait_for_contents)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_clipboard_wait_for_image", gtk_clipboard_wait_for_image)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_selection_data_targets_include_image", gtk_selection_data_targets_include_image)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_selection_data_free", gtk_selection_data_free)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_dialog_new", gtk_file_chooser_dialog_new)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_get_type", gtk_file_chooser_get_type)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_image_get_type", gtk_image_get_type)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_set_current_folder", gtk_file_chooser_set_current_folder)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_get_current_folder", gtk_file_chooser_get_current_folder)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_set_current_name", gtk_file_chooser_set_current_name)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_select_filename", gtk_file_chooser_select_filename)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_get_filenames", gtk_file_chooser_get_filenames)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_set_filter", gtk_file_chooser_set_filter)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_get_filter", gtk_file_chooser_get_filter)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_window_get_type", gtk_window_get_type)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_window_set_title", gtk_window_set_title)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_set_local_only", gtk_file_chooser_set_local_only)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_set_action", gtk_file_chooser_set_action)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_set_select_multiple", gtk_file_chooser_set_select_multiple)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_set_do_overwrite_confirmation", gtk_file_chooser_set_do_overwrite_confirmation)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_remove_filter", gtk_file_chooser_remove_filter)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_file_filter_set_name", gtk_file_filter_set_name)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_file_filter_add_pattern", gtk_file_filter_add_pattern)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_add_filter", gtk_file_chooser_add_filter)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_set_preview_widget", gtk_file_chooser_set_preview_widget)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_get_preview_filename", gtk_file_chooser_get_preview_filename)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_set_preview_widget_active", gtk_file_chooser_set_preview_widget_active)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_file_filter_new", gtk_file_filter_new)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_image_new", gtk_image_new)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_image_set_from_pixbuf", gtk_image_set_from_pixbuf)) return false;
|
|
||||||
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gdk_window_set_modal_hint", gdk_window_set_modal_hint)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gdk_window_focus", gdk_window_focus)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_dialog_get_type", gtk_dialog_get_type)) return false;
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gtk_dialog_run", gtk_dialog_run)) return false;
|
|
||||||
|
|
||||||
if (!LOAD_SYMBOL(lib_gtk, "gdk_atom_intern", gdk_atom_intern)) return false;
|
|
||||||
|
|
||||||
if (LOAD_SYMBOL(lib_gtk, "gdk_set_allowed_backends", gdk_set_allowed_backends)) {
|
|
||||||
// We work only with Wayland and X11 GDK backends.
|
|
||||||
// Otherwise we get segfault in Ubuntu 17.04 in gtk_init_check() call.
|
|
||||||
// See https://github.com/telegramdesktop/tdesktop/issues/3176
|
|
||||||
// See https://github.com/telegramdesktop/tdesktop/issues/3162
|
|
||||||
if(IsWayland()) {
|
|
||||||
DEBUG_LOG(("Limit allowed GDK backends to wayland,x11"));
|
|
||||||
gdk_set_allowed_backends("wayland,x11");
|
|
||||||
} else {
|
|
||||||
DEBUG_LOG(("Limit allowed GDK backends to x11,wayland"));
|
|
||||||
gdk_set_allowed_backends("x11,wayland");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// gtk_init will reset the Xlib error handler, and that causes
|
|
||||||
// Qt applications to quit on X errors. Therefore, we need to manually restore it.
|
|
||||||
XErrorHandlerRestorer handlerRestorer;
|
|
||||||
|
|
||||||
DEBUG_LOG(("Library gtk functions loaded!"));
|
|
||||||
gtkTriedToInit = true;
|
|
||||||
if (!gtk_init_check(0, 0)) {
|
|
||||||
gtk_init_check = nullptr;
|
|
||||||
DEBUG_LOG(("Failed to gtk_init_check(0, 0)!"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
DEBUG_LOG(("Checked gtk with gtk_init_check!"));
|
|
||||||
|
|
||||||
// Use our custom log handler.
|
|
||||||
g_log_set_handler("Gtk", G_LOG_LEVEL_MESSAGE, gtkMessageHandler, nullptr);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IconThemeShouldBeSet() {
|
|
||||||
// change the icon theme only if it isn't already set by a platformtheme plugin
|
|
||||||
// if QT_QPA_PLATFORMTHEME=(gtk2|gtk3), then force-apply the icon theme
|
|
||||||
static const auto Result =
|
|
||||||
// QGenericUnixTheme
|
|
||||||
(QIcon::themeName() == qstr("hicolor")
|
|
||||||
&& QIcon::fallbackThemeName() == qstr("hicolor"))
|
|
||||||
// QGnomeTheme
|
|
||||||
|| (QIcon::themeName() == qstr("Adwaita")
|
|
||||||
&& QIcon::fallbackThemeName() == qstr("gnome"))
|
|
||||||
// qt5ct
|
|
||||||
|| (QIcon::themeName().isEmpty()
|
|
||||||
&& QIcon::fallbackThemeName().isEmpty())
|
|
||||||
|| IsGtkIntegrationForced();
|
|
||||||
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CursorSizeShouldBeSet() {
|
|
||||||
// change the cursor size only on Wayland and if it wasn't already set
|
|
||||||
static const auto Result = IsWayland()
|
|
||||||
&& qEnvironmentVariableIsEmpty("XCURSOR_SIZE");
|
|
||||||
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetIconTheme() {
|
|
||||||
Core::Sandbox::Instance().customEnterFromEventLoop([] {
|
|
||||||
if (GtkSettingSupported()
|
|
||||||
&& GtkLoaded()
|
|
||||||
&& IconThemeShouldBeSet()) {
|
|
||||||
DEBUG_LOG(("Setting GTK icon theme"));
|
|
||||||
QIcon::setThemeName(GtkSetting("gtk-icon-theme-name"));
|
|
||||||
QIcon::setFallbackThemeName(GtkSetting("gtk-fallback-icon-theme"));
|
|
||||||
|
|
||||||
DEBUG_LOG(("New icon theme: %1").arg(QIcon::themeName()));
|
|
||||||
DEBUG_LOG(("New fallback icon theme: %1").arg(QIcon::fallbackThemeName()));
|
|
||||||
|
|
||||||
SetApplicationIcon(Window::CreateIcon());
|
|
||||||
if (App::wnd()) {
|
|
||||||
App::wnd()->setWindowIcon(Window::CreateIcon());
|
|
||||||
}
|
|
||||||
|
|
||||||
Core::App().domain().notifyUnreadBadgeChanged();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetCursorSize() {
|
|
||||||
Core::Sandbox::Instance().customEnterFromEventLoop([] {
|
|
||||||
if (GtkSettingSupported()
|
|
||||||
&& GtkLoaded()
|
|
||||||
&& CursorSizeShouldBeSet()) {
|
|
||||||
DEBUG_LOG(("Setting GTK cursor size"));
|
|
||||||
|
|
||||||
const auto newCursorSize = GtkSetting<gint>("gtk-cursor-theme-size");
|
|
||||||
qputenv("XCURSOR_SIZE", QByteArray::number(newCursorSize));
|
|
||||||
|
|
||||||
DEBUG_LOG(("New cursor size: %1").arg(newCursorSize));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void DarkModeChanged() {
|
|
||||||
Core::Sandbox::Instance().customEnterFromEventLoop([] {
|
|
||||||
Core::App().settings().setSystemDarkMode(IsDarkMode());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void DecorationLayoutChanged() {
|
|
||||||
Core::Sandbox::Instance().customEnterFromEventLoop([] {
|
|
||||||
Core::App().settings().setWindowControlsLayout(WindowControlsLayout());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
f_gtk_init_check gtk_init_check = nullptr;
|
|
||||||
f_gtk_check_version gtk_check_version = nullptr;
|
|
||||||
f_gtk_settings_get_default gtk_settings_get_default = nullptr;
|
|
||||||
f_gtk_widget_show gtk_widget_show = nullptr;
|
|
||||||
f_gtk_widget_hide gtk_widget_hide = nullptr;
|
|
||||||
f_gtk_widget_get_window gtk_widget_get_window = nullptr;
|
|
||||||
f_gtk_widget_realize gtk_widget_realize = nullptr;
|
|
||||||
f_gtk_widget_hide_on_delete gtk_widget_hide_on_delete = nullptr;
|
|
||||||
f_gtk_widget_destroy gtk_widget_destroy = nullptr;
|
|
||||||
f_gtk_clipboard_get gtk_clipboard_get = nullptr;
|
|
||||||
f_gtk_clipboard_store gtk_clipboard_store = nullptr;
|
|
||||||
f_gtk_clipboard_wait_for_contents gtk_clipboard_wait_for_contents = nullptr;
|
|
||||||
f_gtk_clipboard_wait_for_image gtk_clipboard_wait_for_image = nullptr;
|
|
||||||
f_gtk_selection_data_targets_include_image gtk_selection_data_targets_include_image = nullptr;
|
|
||||||
f_gtk_selection_data_free gtk_selection_data_free = nullptr;
|
|
||||||
f_gtk_file_chooser_dialog_new gtk_file_chooser_dialog_new = nullptr;
|
|
||||||
f_gtk_file_chooser_get_type gtk_file_chooser_get_type = nullptr;
|
|
||||||
f_gtk_image_get_type gtk_image_get_type = nullptr;
|
|
||||||
f_gtk_file_chooser_set_current_folder gtk_file_chooser_set_current_folder = nullptr;
|
|
||||||
f_gtk_file_chooser_get_current_folder gtk_file_chooser_get_current_folder = nullptr;
|
|
||||||
f_gtk_file_chooser_set_current_name gtk_file_chooser_set_current_name = nullptr;
|
|
||||||
f_gtk_file_chooser_select_filename gtk_file_chooser_select_filename = nullptr;
|
|
||||||
f_gtk_file_chooser_get_filenames gtk_file_chooser_get_filenames = nullptr;
|
|
||||||
f_gtk_file_chooser_set_filter gtk_file_chooser_set_filter = nullptr;
|
|
||||||
f_gtk_file_chooser_get_filter gtk_file_chooser_get_filter = nullptr;
|
|
||||||
f_gtk_window_get_type gtk_window_get_type = nullptr;
|
|
||||||
f_gtk_window_set_title gtk_window_set_title = nullptr;
|
|
||||||
f_gtk_file_chooser_set_local_only gtk_file_chooser_set_local_only = nullptr;
|
|
||||||
f_gtk_file_chooser_set_action gtk_file_chooser_set_action = nullptr;
|
|
||||||
f_gtk_file_chooser_set_select_multiple gtk_file_chooser_set_select_multiple = nullptr;
|
|
||||||
f_gtk_file_chooser_set_do_overwrite_confirmation gtk_file_chooser_set_do_overwrite_confirmation = nullptr;
|
|
||||||
f_gtk_file_chooser_remove_filter gtk_file_chooser_remove_filter = nullptr;
|
|
||||||
f_gtk_file_filter_set_name gtk_file_filter_set_name = nullptr;
|
|
||||||
f_gtk_file_filter_add_pattern gtk_file_filter_add_pattern = nullptr;
|
|
||||||
f_gtk_file_chooser_add_filter gtk_file_chooser_add_filter = nullptr;
|
|
||||||
f_gtk_file_chooser_set_preview_widget gtk_file_chooser_set_preview_widget = nullptr;
|
|
||||||
f_gtk_file_chooser_get_preview_filename gtk_file_chooser_get_preview_filename = nullptr;
|
|
||||||
f_gtk_file_chooser_set_preview_widget_active gtk_file_chooser_set_preview_widget_active = nullptr;
|
|
||||||
f_gtk_file_filter_new gtk_file_filter_new = nullptr;
|
|
||||||
f_gtk_image_new gtk_image_new = nullptr;
|
|
||||||
f_gtk_image_set_from_pixbuf gtk_image_set_from_pixbuf = nullptr;
|
|
||||||
f_gtk_dialog_get_widget_for_response gtk_dialog_get_widget_for_response = nullptr;
|
|
||||||
f_gtk_button_set_label gtk_button_set_label = nullptr;
|
|
||||||
f_gtk_button_get_type gtk_button_get_type = nullptr;
|
|
||||||
f_gtk_app_chooser_dialog_new gtk_app_chooser_dialog_new = nullptr;
|
|
||||||
f_gtk_app_chooser_get_app_info gtk_app_chooser_get_app_info = nullptr;
|
|
||||||
f_gtk_app_chooser_get_type gtk_app_chooser_get_type = nullptr;
|
|
||||||
f_gdk_set_allowed_backends gdk_set_allowed_backends = nullptr;
|
|
||||||
f_gdk_window_set_modal_hint gdk_window_set_modal_hint = nullptr;
|
|
||||||
f_gdk_window_focus gdk_window_focus = nullptr;
|
|
||||||
f_gtk_dialog_get_type gtk_dialog_get_type = nullptr;
|
|
||||||
f_gtk_dialog_run gtk_dialog_run = nullptr;
|
|
||||||
f_gdk_atom_intern gdk_atom_intern = nullptr;
|
|
||||||
f_gdk_pixbuf_new_from_file_at_size gdk_pixbuf_new_from_file_at_size = nullptr;
|
|
||||||
f_gdk_pixbuf_get_has_alpha gdk_pixbuf_get_has_alpha = nullptr;
|
|
||||||
f_gdk_pixbuf_get_pixels gdk_pixbuf_get_pixels = nullptr;
|
|
||||||
f_gdk_pixbuf_get_width gdk_pixbuf_get_width = nullptr;
|
|
||||||
f_gdk_pixbuf_get_height gdk_pixbuf_get_height = nullptr;
|
|
||||||
f_gdk_pixbuf_get_rowstride gdk_pixbuf_get_rowstride = nullptr;
|
|
||||||
|
|
||||||
bool GtkLoaded() {
|
|
||||||
return gtkLoaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
::GtkClipboard *GtkClipboard() {
|
|
||||||
if (gtk_clipboard_get != nullptr) {
|
|
||||||
return gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
|
|
||||||
void start() {
|
|
||||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
if (!UseGtkIntegration()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG_LOG(("Loading libraries"));
|
|
||||||
|
|
||||||
QLibrary lib_gtk;
|
|
||||||
lib_gtk.setLoadHints(QLibrary::DeepBindHint);
|
|
||||||
|
|
||||||
if (loadLibrary(lib_gtk, "gtk-3", 0)) {
|
|
||||||
gtkLoaded = setupGtkBase(lib_gtk);
|
|
||||||
}
|
|
||||||
if (!gtkLoaded && !gtkTriedToInit && loadLibrary(lib_gtk, "gtk-x11-2.0", 0)) {
|
|
||||||
gtkLoaded = setupGtkBase(lib_gtk);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gtkLoaded) {
|
|
||||||
LOAD_SYMBOL(lib_gtk, "gdk_pixbuf_new_from_file_at_size", gdk_pixbuf_new_from_file_at_size);
|
|
||||||
LOAD_SYMBOL(lib_gtk, "gdk_pixbuf_get_has_alpha", gdk_pixbuf_get_has_alpha);
|
|
||||||
LOAD_SYMBOL(lib_gtk, "gdk_pixbuf_get_pixels", gdk_pixbuf_get_pixels);
|
|
||||||
LOAD_SYMBOL(lib_gtk, "gdk_pixbuf_get_width", gdk_pixbuf_get_width);
|
|
||||||
LOAD_SYMBOL(lib_gtk, "gdk_pixbuf_get_height", gdk_pixbuf_get_height);
|
|
||||||
LOAD_SYMBOL(lib_gtk, "gdk_pixbuf_get_rowstride", gdk_pixbuf_get_rowstride);
|
|
||||||
|
|
||||||
internal::GdkHelperLoad(lib_gtk);
|
|
||||||
|
|
||||||
LOAD_SYMBOL(lib_gtk, "gtk_dialog_get_widget_for_response", gtk_dialog_get_widget_for_response);
|
|
||||||
LOAD_SYMBOL(lib_gtk, "gtk_button_set_label", gtk_button_set_label);
|
|
||||||
LOAD_SYMBOL(lib_gtk, "gtk_button_get_type", gtk_button_get_type);
|
|
||||||
|
|
||||||
LOAD_SYMBOL(lib_gtk, "gtk_app_chooser_dialog_new", gtk_app_chooser_dialog_new);
|
|
||||||
LOAD_SYMBOL(lib_gtk, "gtk_app_chooser_get_app_info", gtk_app_chooser_get_app_info);
|
|
||||||
LOAD_SYMBOL(lib_gtk, "gtk_app_chooser_get_type", gtk_app_chooser_get_type);
|
|
||||||
|
|
||||||
SetIconTheme();
|
|
||||||
SetCursorSize();
|
|
||||||
|
|
||||||
const auto settings = gtk_settings_get_default();
|
|
||||||
g_signal_connect(settings, "notify::gtk-icon-theme-name", G_CALLBACK(SetIconTheme), nullptr);
|
|
||||||
g_signal_connect(settings, "notify::gtk-theme-name", G_CALLBACK(DarkModeChanged), nullptr);
|
|
||||||
g_signal_connect(settings, "notify::gtk-cursor-theme-size", G_CALLBACK(SetCursorSize), nullptr);
|
|
||||||
|
|
||||||
if (!gtk_check_version(3, 0, 0)) {
|
|
||||||
g_signal_connect(settings, "notify::gtk-application-prefer-dark-theme", G_CALLBACK(DarkModeChanged), nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!gtk_check_version(3, 12, 0)) {
|
|
||||||
g_signal_connect(settings, "notify::gtk-decoration-layout", G_CALLBACK(DecorationLayoutChanged), nullptr);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LOG(("Could not load gtk-3 or gtk-x11-2.0!"));
|
|
||||||
}
|
|
||||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Libs
|
|
||||||
} // namespace Platform
|
|
|
@ -1,303 +0,0 @@
|
||||||
/*
|
|
||||||
This file is part of Telegram Desktop,
|
|
||||||
the official desktop application for the Telegram messaging service.
|
|
||||||
|
|
||||||
For license and copyright information please follow this link:
|
|
||||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QtCore/QLibrary>
|
|
||||||
|
|
||||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#undef signals
|
|
||||||
#include <gtk/gtk.h>
|
|
||||||
#include <gdk/gdk.h>
|
|
||||||
#define signals public
|
|
||||||
} // extern "C"
|
|
||||||
|
|
||||||
// present starting with gtk 3.0, we can build with gtk2 headers
|
|
||||||
typedef struct _GtkAppChooser GtkAppChooser;
|
|
||||||
|
|
||||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
|
|
||||||
#if defined DESKTOP_APP_USE_PACKAGED && !defined DESKTOP_APP_USE_PACKAGED_LAZY
|
|
||||||
#define LOAD_SYMBOL(lib, name, func) (func = ::func)
|
|
||||||
#else // DESKTOP_APP_USE_PACKAGED && !DESKTOP_APP_USE_PACKAGED_LAZY
|
|
||||||
#define LOAD_SYMBOL Platform::Libs::load
|
|
||||||
#endif // !DESKTOP_APP_USE_PACKAGED || DESKTOP_APP_USE_PACKAGED_LAZY
|
|
||||||
|
|
||||||
namespace Platform {
|
|
||||||
namespace Libs {
|
|
||||||
|
|
||||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
bool GtkLoaded();
|
|
||||||
::GtkClipboard *GtkClipboard();
|
|
||||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
|
|
||||||
void start();
|
|
||||||
|
|
||||||
template <typename Function>
|
|
||||||
bool load(QLibrary &lib, const char *name, Function &func) {
|
|
||||||
func = nullptr;
|
|
||||||
if (!lib.isLoaded()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
func = reinterpret_cast<Function>(lib.resolve(name));
|
|
||||||
if (func) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
LOG(("Error: failed to load '%1' function!").arg(name));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
typedef gboolean (*f_gtk_init_check)(int *argc, char ***argv);
|
|
||||||
extern f_gtk_init_check gtk_init_check;
|
|
||||||
|
|
||||||
typedef const gchar* (*f_gtk_check_version)(guint required_major, guint required_minor, guint required_micro);
|
|
||||||
extern f_gtk_check_version gtk_check_version;
|
|
||||||
|
|
||||||
typedef GtkSettings* (*f_gtk_settings_get_default)(void);
|
|
||||||
extern f_gtk_settings_get_default gtk_settings_get_default;
|
|
||||||
|
|
||||||
typedef void (*f_gtk_widget_show)(GtkWidget *widget);
|
|
||||||
extern f_gtk_widget_show gtk_widget_show;
|
|
||||||
|
|
||||||
typedef void (*f_gtk_widget_hide)(GtkWidget *widget);
|
|
||||||
extern f_gtk_widget_hide gtk_widget_hide;
|
|
||||||
|
|
||||||
typedef GdkWindow* (*f_gtk_widget_get_window)(GtkWidget *widget);
|
|
||||||
extern f_gtk_widget_get_window gtk_widget_get_window;
|
|
||||||
|
|
||||||
typedef void (*f_gtk_widget_realize)(GtkWidget *widget);
|
|
||||||
extern f_gtk_widget_realize gtk_widget_realize;
|
|
||||||
|
|
||||||
typedef gboolean (*f_gtk_widget_hide_on_delete)(GtkWidget *widget);
|
|
||||||
extern f_gtk_widget_hide_on_delete gtk_widget_hide_on_delete;
|
|
||||||
|
|
||||||
typedef void (*f_gtk_widget_destroy)(GtkWidget *widget);
|
|
||||||
extern f_gtk_widget_destroy gtk_widget_destroy;
|
|
||||||
|
|
||||||
typedef ::GtkClipboard* (*f_gtk_clipboard_get)(GdkAtom selection);
|
|
||||||
extern f_gtk_clipboard_get gtk_clipboard_get;
|
|
||||||
|
|
||||||
typedef void (*f_gtk_clipboard_store)(::GtkClipboard *clipboard);
|
|
||||||
extern f_gtk_clipboard_store gtk_clipboard_store;
|
|
||||||
|
|
||||||
typedef GtkSelectionData* (*f_gtk_clipboard_wait_for_contents)(::GtkClipboard *clipboard, GdkAtom target);
|
|
||||||
extern f_gtk_clipboard_wait_for_contents gtk_clipboard_wait_for_contents;
|
|
||||||
|
|
||||||
typedef GdkPixbuf* (*f_gtk_clipboard_wait_for_image)(::GtkClipboard *clipboard);
|
|
||||||
extern f_gtk_clipboard_wait_for_image gtk_clipboard_wait_for_image;
|
|
||||||
|
|
||||||
typedef gboolean (*f_gtk_selection_data_targets_include_image)(const GtkSelectionData *selection_data, gboolean writable);
|
|
||||||
extern f_gtk_selection_data_targets_include_image gtk_selection_data_targets_include_image;
|
|
||||||
|
|
||||||
typedef void (*f_gtk_selection_data_free)(GtkSelectionData *data);
|
|
||||||
extern f_gtk_selection_data_free gtk_selection_data_free;
|
|
||||||
|
|
||||||
typedef GtkWidget* (*f_gtk_file_chooser_dialog_new)(const gchar *title, GtkWindow *parent, GtkFileChooserAction action, const gchar *first_button_text, ...) G_GNUC_NULL_TERMINATED;
|
|
||||||
extern f_gtk_file_chooser_dialog_new gtk_file_chooser_dialog_new;
|
|
||||||
|
|
||||||
typedef gboolean (*f_gtk_file_chooser_set_current_folder)(GtkFileChooser *chooser, const gchar *filename);
|
|
||||||
extern f_gtk_file_chooser_set_current_folder gtk_file_chooser_set_current_folder;
|
|
||||||
|
|
||||||
typedef gchar* (*f_gtk_file_chooser_get_current_folder)(GtkFileChooser *chooser);
|
|
||||||
extern f_gtk_file_chooser_get_current_folder gtk_file_chooser_get_current_folder;
|
|
||||||
|
|
||||||
typedef void (*f_gtk_file_chooser_set_current_name)(GtkFileChooser *chooser, const gchar *name);
|
|
||||||
extern f_gtk_file_chooser_set_current_name gtk_file_chooser_set_current_name;
|
|
||||||
|
|
||||||
typedef gboolean (*f_gtk_file_chooser_select_filename)(GtkFileChooser *chooser, const gchar *filename);
|
|
||||||
extern f_gtk_file_chooser_select_filename gtk_file_chooser_select_filename;
|
|
||||||
|
|
||||||
typedef GSList* (*f_gtk_file_chooser_get_filenames)(GtkFileChooser *chooser);
|
|
||||||
extern f_gtk_file_chooser_get_filenames gtk_file_chooser_get_filenames;
|
|
||||||
|
|
||||||
typedef void (*f_gtk_file_chooser_set_filter)(GtkFileChooser *chooser, GtkFileFilter *filter);
|
|
||||||
extern f_gtk_file_chooser_set_filter gtk_file_chooser_set_filter;
|
|
||||||
|
|
||||||
typedef GtkFileFilter* (*f_gtk_file_chooser_get_filter)(GtkFileChooser *chooser);
|
|
||||||
extern f_gtk_file_chooser_get_filter gtk_file_chooser_get_filter;
|
|
||||||
|
|
||||||
typedef void (*f_gtk_window_set_title)(GtkWindow *window, const gchar *title);
|
|
||||||
extern f_gtk_window_set_title gtk_window_set_title;
|
|
||||||
|
|
||||||
typedef void (*f_gtk_file_chooser_set_local_only)(GtkFileChooser *chooser, gboolean local_only);
|
|
||||||
extern f_gtk_file_chooser_set_local_only gtk_file_chooser_set_local_only;
|
|
||||||
|
|
||||||
typedef void (*f_gtk_file_chooser_set_action)(GtkFileChooser *chooser, GtkFileChooserAction action);
|
|
||||||
extern f_gtk_file_chooser_set_action gtk_file_chooser_set_action;
|
|
||||||
|
|
||||||
typedef void (*f_gtk_file_chooser_set_select_multiple)(GtkFileChooser *chooser, gboolean select_multiple);
|
|
||||||
extern f_gtk_file_chooser_set_select_multiple gtk_file_chooser_set_select_multiple;
|
|
||||||
|
|
||||||
typedef void (*f_gtk_file_chooser_set_do_overwrite_confirmation)(GtkFileChooser *chooser, gboolean do_overwrite_confirmation);
|
|
||||||
extern f_gtk_file_chooser_set_do_overwrite_confirmation gtk_file_chooser_set_do_overwrite_confirmation;
|
|
||||||
|
|
||||||
typedef GtkWidget* (*f_gtk_dialog_get_widget_for_response)(GtkDialog *dialog, gint response_id);
|
|
||||||
extern f_gtk_dialog_get_widget_for_response gtk_dialog_get_widget_for_response;
|
|
||||||
|
|
||||||
typedef void (*f_gtk_button_set_label)(GtkButton *button, const gchar *label);
|
|
||||||
extern f_gtk_button_set_label gtk_button_set_label;
|
|
||||||
|
|
||||||
typedef void (*f_gtk_file_chooser_remove_filter)(GtkFileChooser *chooser, GtkFileFilter *filter);
|
|
||||||
extern f_gtk_file_chooser_remove_filter gtk_file_chooser_remove_filter;
|
|
||||||
|
|
||||||
typedef void (*f_gtk_file_filter_set_name)(GtkFileFilter *filter, const gchar *name);
|
|
||||||
extern f_gtk_file_filter_set_name gtk_file_filter_set_name;
|
|
||||||
|
|
||||||
typedef void (*f_gtk_file_filter_add_pattern)(GtkFileFilter *filter, const gchar *pattern);
|
|
||||||
extern f_gtk_file_filter_add_pattern gtk_file_filter_add_pattern;
|
|
||||||
|
|
||||||
typedef void (*f_gtk_file_chooser_add_filter)(GtkFileChooser *chooser, GtkFileFilter *filter);
|
|
||||||
extern f_gtk_file_chooser_add_filter gtk_file_chooser_add_filter;
|
|
||||||
|
|
||||||
typedef void (*f_gtk_file_chooser_set_preview_widget)(GtkFileChooser *chooser, GtkWidget *preview_widget);
|
|
||||||
extern f_gtk_file_chooser_set_preview_widget gtk_file_chooser_set_preview_widget;
|
|
||||||
|
|
||||||
typedef gchar* (*f_gtk_file_chooser_get_preview_filename)(GtkFileChooser *chooser);
|
|
||||||
extern f_gtk_file_chooser_get_preview_filename gtk_file_chooser_get_preview_filename;
|
|
||||||
|
|
||||||
typedef void (*f_gtk_file_chooser_set_preview_widget_active)(GtkFileChooser *chooser, gboolean active);
|
|
||||||
extern f_gtk_file_chooser_set_preview_widget_active gtk_file_chooser_set_preview_widget_active;
|
|
||||||
|
|
||||||
typedef GtkFileFilter* (*f_gtk_file_filter_new)(void);
|
|
||||||
extern f_gtk_file_filter_new gtk_file_filter_new;
|
|
||||||
|
|
||||||
typedef GtkWidget* (*f_gtk_image_new)(void);
|
|
||||||
extern f_gtk_image_new gtk_image_new;
|
|
||||||
|
|
||||||
typedef void (*f_gtk_image_set_from_pixbuf)(GtkImage *image, GdkPixbuf *pixbuf);
|
|
||||||
extern f_gtk_image_set_from_pixbuf gtk_image_set_from_pixbuf;
|
|
||||||
|
|
||||||
typedef GtkWidget* (*f_gtk_app_chooser_dialog_new)(GtkWindow *parent, GtkDialogFlags flags, GFile *file);
|
|
||||||
extern f_gtk_app_chooser_dialog_new gtk_app_chooser_dialog_new;
|
|
||||||
|
|
||||||
typedef GAppInfo* (*f_gtk_app_chooser_get_app_info)(GtkAppChooser *self);
|
|
||||||
extern f_gtk_app_chooser_get_app_info gtk_app_chooser_get_app_info;
|
|
||||||
|
|
||||||
typedef void (*f_gdk_set_allowed_backends)(const gchar *backends);
|
|
||||||
extern f_gdk_set_allowed_backends gdk_set_allowed_backends;
|
|
||||||
|
|
||||||
typedef void (*f_gdk_window_set_modal_hint)(GdkWindow *window, gboolean modal);
|
|
||||||
extern f_gdk_window_set_modal_hint gdk_window_set_modal_hint;
|
|
||||||
|
|
||||||
typedef void (*f_gdk_window_focus)(GdkWindow *window, guint32 timestamp);
|
|
||||||
extern f_gdk_window_focus gdk_window_focus;
|
|
||||||
|
|
||||||
template <typename Result, typename Object>
|
|
||||||
inline Result *g_type_cic_helper(Object *instance, GType iface_type) {
|
|
||||||
return reinterpret_cast<Result*>(g_type_check_instance_cast(reinterpret_cast<GTypeInstance*>(instance), iface_type));
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef GType (*f_gtk_dialog_get_type)(void) G_GNUC_CONST;
|
|
||||||
extern f_gtk_dialog_get_type gtk_dialog_get_type;
|
|
||||||
|
|
||||||
template <typename Object>
|
|
||||||
inline GtkDialog *gtk_dialog_cast(Object *obj) {
|
|
||||||
return g_type_cic_helper<GtkDialog, Object>(obj, gtk_dialog_get_type());
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef GType (*f_gtk_file_chooser_get_type)(void) G_GNUC_CONST;
|
|
||||||
extern f_gtk_file_chooser_get_type gtk_file_chooser_get_type;
|
|
||||||
|
|
||||||
template <typename Object>
|
|
||||||
inline GtkFileChooser *gtk_file_chooser_cast(Object *obj) {
|
|
||||||
return g_type_cic_helper<GtkFileChooser, Object>(obj, gtk_file_chooser_get_type());
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef GType (*f_gtk_image_get_type)(void) G_GNUC_CONST;
|
|
||||||
extern f_gtk_image_get_type gtk_image_get_type;
|
|
||||||
|
|
||||||
template <typename Object>
|
|
||||||
inline GtkImage *gtk_image_cast(Object *obj) {
|
|
||||||
return g_type_cic_helper<GtkImage, Object>(obj, gtk_image_get_type());
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef GType (*f_gtk_button_get_type)(void) G_GNUC_CONST;
|
|
||||||
extern f_gtk_button_get_type gtk_button_get_type;
|
|
||||||
|
|
||||||
template <typename Object>
|
|
||||||
inline GtkButton *gtk_button_cast(Object *obj) {
|
|
||||||
return g_type_cic_helper<GtkButton, Object>(obj, gtk_button_get_type());
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef GType (*f_gtk_window_get_type)(void) G_GNUC_CONST;
|
|
||||||
extern f_gtk_window_get_type gtk_window_get_type;
|
|
||||||
|
|
||||||
template <typename Object>
|
|
||||||
inline GtkWindow *gtk_window_cast(Object *obj) {
|
|
||||||
return g_type_cic_helper<GtkWindow, Object>(obj, gtk_window_get_type());
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef GType (*f_gtk_app_chooser_get_type)(void) G_GNUC_CONST;
|
|
||||||
extern f_gtk_app_chooser_get_type gtk_app_chooser_get_type;
|
|
||||||
|
|
||||||
template <typename Object>
|
|
||||||
inline GtkAppChooser *gtk_app_chooser_cast(Object *obj) {
|
|
||||||
return g_type_cic_helper<GtkAppChooser, Object>(obj, gtk_app_chooser_get_type());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Object>
|
|
||||||
inline bool g_type_cit_helper(Object *instance, GType iface_type) {
|
|
||||||
if (!instance) return false;
|
|
||||||
|
|
||||||
auto ginstance = reinterpret_cast<GTypeInstance*>(instance);
|
|
||||||
if (ginstance->g_class && ginstance->g_class->g_type == iface_type) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return g_type_check_instance_is_a(ginstance, iface_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef gint (*f_gtk_dialog_run)(GtkDialog *dialog);
|
|
||||||
extern f_gtk_dialog_run gtk_dialog_run;
|
|
||||||
|
|
||||||
typedef GdkAtom (*f_gdk_atom_intern)(const gchar *atom_name, gboolean only_if_exists);
|
|
||||||
extern f_gdk_atom_intern gdk_atom_intern;
|
|
||||||
|
|
||||||
typedef GdkPixbuf* (*f_gdk_pixbuf_new_from_file_at_size)(const gchar *filename, int width, int height, GError **error);
|
|
||||||
extern f_gdk_pixbuf_new_from_file_at_size gdk_pixbuf_new_from_file_at_size;
|
|
||||||
|
|
||||||
typedef gboolean (*f_gdk_pixbuf_get_has_alpha)(const GdkPixbuf *pixbuf);
|
|
||||||
extern f_gdk_pixbuf_get_has_alpha gdk_pixbuf_get_has_alpha;
|
|
||||||
|
|
||||||
typedef guchar* (*f_gdk_pixbuf_get_pixels)(const GdkPixbuf *pixbuf);
|
|
||||||
extern f_gdk_pixbuf_get_pixels gdk_pixbuf_get_pixels;
|
|
||||||
|
|
||||||
typedef int (*f_gdk_pixbuf_get_width)(const GdkPixbuf *pixbuf);
|
|
||||||
extern f_gdk_pixbuf_get_width gdk_pixbuf_get_width;
|
|
||||||
|
|
||||||
typedef int (*f_gdk_pixbuf_get_height)(const GdkPixbuf *pixbuf);
|
|
||||||
extern f_gdk_pixbuf_get_height gdk_pixbuf_get_height;
|
|
||||||
|
|
||||||
typedef int (*f_gdk_pixbuf_get_rowstride)(const GdkPixbuf *pixbuf);
|
|
||||||
extern f_gdk_pixbuf_get_rowstride gdk_pixbuf_get_rowstride;
|
|
||||||
|
|
||||||
inline bool GtkSettingSupported() {
|
|
||||||
return gtk_settings_get_default != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
inline T GtkSetting(const gchar *propertyName) {
|
|
||||||
GtkSettings *settings = gtk_settings_get_default();
|
|
||||||
T value;
|
|
||||||
g_object_get(settings, propertyName, &value, nullptr);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline QString GtkSetting(const gchar *propertyName) {
|
|
||||||
gchararray value = GtkSetting<gchararray>(propertyName);
|
|
||||||
QString str = QString::fromUtf8(value);
|
|
||||||
g_free(value);
|
|
||||||
DEBUG_LOG(("Getting GTK setting, %1: '%2'").arg(propertyName).arg(str));
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
|
|
||||||
} // namespace Libs
|
|
||||||
} // namespace Platform
|
|
132
Telegram/SourceFiles/platform/linux/linux_open_with_dialog.cpp
Normal file
132
Telegram/SourceFiles/platform/linux/linux_open_with_dialog.cpp
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#include "platform/linux/linux_open_with_dialog.h"
|
||||||
|
|
||||||
|
#include "platform/linux/linux_gtk_integration_p.h"
|
||||||
|
#include "platform/linux/linux_gdk_helper.h"
|
||||||
|
#include "window/window_controller.h"
|
||||||
|
#include "core/application.h"
|
||||||
|
|
||||||
|
#include <private/qguiapplication_p.h>
|
||||||
|
|
||||||
|
namespace Platform {
|
||||||
|
namespace File {
|
||||||
|
namespace internal {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using namespace Platform::Gtk;
|
||||||
|
|
||||||
|
bool OpenWithDialogSupported() {
|
||||||
|
return Platform::internal::GdkHelperLoaded()
|
||||||
|
&& (gtk_app_chooser_dialog_new != nullptr)
|
||||||
|
&& (gtk_app_chooser_get_app_info != nullptr)
|
||||||
|
&& (gtk_app_chooser_get_type != nullptr)
|
||||||
|
&& (gtk_widget_get_window != nullptr)
|
||||||
|
&& (gtk_widget_realize != nullptr)
|
||||||
|
&& (gtk_widget_show != nullptr)
|
||||||
|
&& (gtk_widget_destroy != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
class OpenWithDialog : public QWindow {
|
||||||
|
public:
|
||||||
|
OpenWithDialog(const QString &filepath);
|
||||||
|
~OpenWithDialog();
|
||||||
|
|
||||||
|
bool exec();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void handleResponse(OpenWithDialog *dialog, int responseId);
|
||||||
|
|
||||||
|
GFile *_gfileInstance = nullptr;
|
||||||
|
GtkWidget *_gtkWidget = nullptr;
|
||||||
|
QEventLoop _loop;
|
||||||
|
std::optional<bool> _result;
|
||||||
|
};
|
||||||
|
|
||||||
|
OpenWithDialog::OpenWithDialog(const QString &filepath)
|
||||||
|
: _gfileInstance(g_file_new_for_path(filepath.toUtf8()))
|
||||||
|
, _gtkWidget(gtk_app_chooser_dialog_new(
|
||||||
|
nullptr,
|
||||||
|
GTK_DIALOG_MODAL,
|
||||||
|
_gfileInstance)) {
|
||||||
|
g_signal_connect_swapped(
|
||||||
|
_gtkWidget,
|
||||||
|
"response",
|
||||||
|
G_CALLBACK(handleResponse),
|
||||||
|
this);
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenWithDialog::~OpenWithDialog() {
|
||||||
|
gtk_widget_destroy(_gtkWidget);
|
||||||
|
g_object_unref(_gfileInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenWithDialog::exec() {
|
||||||
|
gtk_widget_realize(_gtkWidget);
|
||||||
|
|
||||||
|
if (const auto activeWindow = Core::App().activeWindow()) {
|
||||||
|
Platform::internal::XSetTransientForHint(
|
||||||
|
gtk_widget_get_window(_gtkWidget),
|
||||||
|
activeWindow->widget().get()->windowHandle()->winId());
|
||||||
|
}
|
||||||
|
|
||||||
|
QGuiApplicationPrivate::showModalWindow(this);
|
||||||
|
gtk_widget_show(_gtkWidget);
|
||||||
|
|
||||||
|
if (!_result.has_value()) {
|
||||||
|
_loop.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
QGuiApplicationPrivate::hideModalWindow(this);
|
||||||
|
return *_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenWithDialog::handleResponse(OpenWithDialog *dialog, int responseId) {
|
||||||
|
GAppInfo *chosenAppInfo = nullptr;
|
||||||
|
dialog->_result = true;
|
||||||
|
|
||||||
|
switch (responseId) {
|
||||||
|
case GTK_RESPONSE_OK:
|
||||||
|
chosenAppInfo = gtk_app_chooser_get_app_info(
|
||||||
|
gtk_app_chooser_cast(dialog->_gtkWidget));
|
||||||
|
|
||||||
|
if (chosenAppInfo) {
|
||||||
|
GList *uris = nullptr;
|
||||||
|
uris = g_list_prepend(uris, g_file_get_uri(dialog->_gfileInstance));
|
||||||
|
g_app_info_launch_uris(chosenAppInfo, uris, nullptr, nullptr);
|
||||||
|
g_list_free(uris);
|
||||||
|
g_object_unref(chosenAppInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GTK_RESPONSE_CANCEL:
|
||||||
|
case GTK_RESPONSE_DELETE_EVENT:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
dialog->_result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog->_loop.quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool ShowOpenWithDialog(const QString &filepath) {
|
||||||
|
if (!OpenWithDialogSupported()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OpenWithDialog(filepath).exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace File
|
||||||
|
} // namespace Platform
|
18
Telegram/SourceFiles/platform/linux/linux_open_with_dialog.h
Normal file
18
Telegram/SourceFiles/platform/linux/linux_open_with_dialog.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Platform {
|
||||||
|
namespace File {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
bool ShowOpenWithDialog(const QString &filepath);
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace File
|
||||||
|
} // namespace Platform
|
|
@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "platform/linux/linux_xlib_helper.h"
|
#include "platform/linux/linux_xlib_helper.h"
|
||||||
|
|
||||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
}
|
}
|
||||||
|
@ -37,4 +36,3 @@ XErrorHandlerRestorer::~XErrorHandlerRestorer() = default;
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace Platform
|
} // namespace Platform
|
||||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
namespace Platform {
|
namespace Platform {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
|
@ -23,4 +22,3 @@ private:
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace Platform
|
} // namespace Platform
|
||||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
|
|
|
@ -522,6 +522,11 @@ void ForceDisabled(QAction *action, bool disabled) {
|
||||||
|
|
||||||
MainWindow::MainWindow(not_null<Window::Controller*> controller)
|
MainWindow::MainWindow(not_null<Window::Controller*> controller)
|
||||||
: Window::MainWindow(controller) {
|
: Window::MainWindow(controller) {
|
||||||
|
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||||
|
qDBusRegisterMetaType<ToolTip>();
|
||||||
|
qDBusRegisterMetaType<IconPixmap>();
|
||||||
|
qDBusRegisterMetaType<IconPixmapList>();
|
||||||
|
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::initHook() {
|
void MainWindow::initHook() {
|
||||||
|
@ -911,14 +916,6 @@ void MainWindow::updateWaylandDecorationColors() {
|
||||||
windowHandle()->resize(windowHandle()->size());
|
windowHandle()->resize(windowHandle()->size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::LibsLoaded() {
|
|
||||||
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
|
||||||
qDBusRegisterMetaType<ToolTip>();
|
|
||||||
qDBusRegisterMetaType<IconPixmap>();
|
|
||||||
qDBusRegisterMetaType<IconPixmapList>();
|
|
||||||
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::initTrayMenuHook() {
|
void MainWindow::initTrayMenuHook() {
|
||||||
_trayIconMenuXEmbed.emplace(nullptr, trayIconMenu);
|
_trayIconMenuXEmbed.emplace(nullptr, trayIconMenu);
|
||||||
_trayIconMenuXEmbed->deleteOnHide(false);
|
_trayIconMenuXEmbed->deleteOnHide(false);
|
||||||
|
|
|
@ -46,8 +46,6 @@ public:
|
||||||
|
|
||||||
bool isActiveForTrayMenu() override;
|
bool isActiveForTrayMenu() override;
|
||||||
|
|
||||||
static void LibsLoaded();
|
|
||||||
|
|
||||||
~MainWindow();
|
~MainWindow();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "platform/linux/specific_linux.h"
|
#include "platform/linux/specific_linux.h"
|
||||||
|
|
||||||
#include "platform/linux/linux_libs.h"
|
#include "platform/linux/linux_gtk_integration.h"
|
||||||
#include "base/platform/base_platform_info.h"
|
#include "base/platform/base_platform_info.h"
|
||||||
#include "base/platform/linux/base_xcb_utilities_linux.h"
|
#include "base/platform/linux/base_xcb_utilities_linux.h"
|
||||||
#include "base/qt_adapters.h"
|
#include "base/qt_adapters.h"
|
||||||
|
@ -68,11 +68,11 @@ extern "C" {
|
||||||
using namespace Platform;
|
using namespace Platform;
|
||||||
using Platform::File::internal::EscapeShell;
|
using Platform::File::internal::EscapeShell;
|
||||||
using Platform::internal::WaylandIntegration;
|
using Platform::internal::WaylandIntegration;
|
||||||
|
using Platform::internal::GtkIntegration;
|
||||||
|
|
||||||
namespace Platform {
|
namespace Platform {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr auto kDisableGtkIntegration = "TDESKTOP_DISABLE_GTK_INTEGRATION"_cs;
|
|
||||||
constexpr auto kIgnoreGtkIncompatibility = "TDESKTOP_I_KNOW_ABOUT_GTK_INCOMPATIBILITY"_cs;
|
constexpr auto kIgnoreGtkIncompatibility = "TDESKTOP_I_KNOW_ABOUT_GTK_INCOMPATIBILITY"_cs;
|
||||||
|
|
||||||
constexpr auto kDesktopFile = ":/misc/telegramdesktop.desktop"_cs;
|
constexpr auto kDesktopFile = ":/misc/telegramdesktop.desktop"_cs;
|
||||||
|
@ -332,21 +332,6 @@ bool GenerateDesktopFile(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
bool GetImageFromClipboardSupported() {
|
|
||||||
return (Libs::gtk_clipboard_wait_for_contents != nullptr)
|
|
||||||
&& (Libs::gtk_clipboard_wait_for_image != nullptr)
|
|
||||||
&& (Libs::gtk_selection_data_targets_include_image != nullptr)
|
|
||||||
&& (Libs::gtk_selection_data_free != nullptr)
|
|
||||||
&& (Libs::gdk_pixbuf_get_pixels != nullptr)
|
|
||||||
&& (Libs::gdk_pixbuf_get_width != nullptr)
|
|
||||||
&& (Libs::gdk_pixbuf_get_height != nullptr)
|
|
||||||
&& (Libs::gdk_pixbuf_get_rowstride != nullptr)
|
|
||||||
&& (Libs::gdk_pixbuf_get_has_alpha != nullptr)
|
|
||||||
&& (Libs::gdk_atom_intern != nullptr);
|
|
||||||
}
|
|
||||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
|
|
||||||
uint XCBMoveResizeFromEdges(Qt::Edges edges) {
|
uint XCBMoveResizeFromEdges(Qt::Edges edges) {
|
||||||
if (edges == (Qt::TopEdge | Qt::LeftEdge))
|
if (edges == (Qt::TopEdge | Qt::LeftEdge))
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -564,28 +549,17 @@ bool IsStaticBinary() {
|
||||||
#endif // !DESKTOP_APP_USE_PACKAGED
|
#endif // !DESKTOP_APP_USE_PACKAGED
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UseGtkIntegration() {
|
|
||||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
static const auto Result = !qEnvironmentVariableIsSet(
|
|
||||||
kDisableGtkIntegration.utf8());
|
|
||||||
|
|
||||||
return Result;
|
|
||||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsGtkIntegrationForced() {
|
bool IsGtkIntegrationForced() {
|
||||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
static const auto Result = [&] {
|
static const auto Result = [&] {
|
||||||
|
if (!GtkIntegration::Instance()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return PlatformThemes.contains(qstr("gtk3"), Qt::CaseInsensitive)
|
return PlatformThemes.contains(qstr("gtk3"), Qt::CaseInsensitive)
|
||||||
|| PlatformThemes.contains(qstr("gtk2"), Qt::CaseInsensitive);
|
|| PlatformThemes.contains(qstr("gtk2"), Qt::CaseInsensitive);
|
||||||
}();
|
}();
|
||||||
|
|
||||||
return Result;
|
return Result;
|
||||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AreQtPluginsBundled() {
|
bool AreQtPluginsBundled() {
|
||||||
|
@ -723,64 +697,38 @@ QString GetIconName() {
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage GetImageFromClipboard() {
|
QImage GetImageFromClipboard() {
|
||||||
QImage data;
|
if (const auto integration = GtkIntegration::Instance()) {
|
||||||
|
return integration->getImageFromClipboard();
|
||||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
if (!GetImageFromClipboardSupported() || !Libs::GtkClipboard()) {
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto gsel = Libs::gtk_clipboard_wait_for_contents(
|
return {};
|
||||||
Libs::GtkClipboard(),
|
|
||||||
Libs::gdk_atom_intern("TARGETS", true));
|
|
||||||
|
|
||||||
if (gsel) {
|
|
||||||
if (Libs::gtk_selection_data_targets_include_image(gsel, false)) {
|
|
||||||
auto img = Libs::gtk_clipboard_wait_for_image(
|
|
||||||
Libs::GtkClipboard());
|
|
||||||
|
|
||||||
if (img) {
|
|
||||||
data = QImage(
|
|
||||||
Libs::gdk_pixbuf_get_pixels(img),
|
|
||||||
Libs::gdk_pixbuf_get_width(img),
|
|
||||||
Libs::gdk_pixbuf_get_height(img),
|
|
||||||
Libs::gdk_pixbuf_get_rowstride(img),
|
|
||||||
Libs::gdk_pixbuf_get_has_alpha(img)
|
|
||||||
? QImage::Format_RGBA8888
|
|
||||||
: QImage::Format_RGB888).copy();
|
|
||||||
|
|
||||||
g_object_unref(img);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Libs::gtk_selection_data_free(gsel);
|
|
||||||
}
|
|
||||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<bool> IsDarkMode() {
|
std::optional<bool> IsDarkMode() {
|
||||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
const auto integration = GtkIntegration::Instance();
|
||||||
if (Libs::GtkSettingSupported() && Libs::GtkLoaded()) {
|
if (!integration) {
|
||||||
if (Libs::gtk_check_version != nullptr
|
return std::nullopt;
|
||||||
&& !Libs::gtk_check_version(3, 0, 0)
|
|
||||||
&& Libs::GtkSetting<gboolean>(
|
|
||||||
"gtk-application-prefer-dark-theme")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto themeName = Libs::GtkSetting("gtk-theme-name").toLower();
|
|
||||||
|
|
||||||
if (themeName.contains(qsl("-dark"))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
|
|
||||||
return std::nullopt;
|
if (integration->checkVersion(3, 0, 0)) {
|
||||||
|
const auto preferDarkTheme = integration->getBoolSetting(
|
||||||
|
qsl("gtk-application-prefer-dark-theme"));
|
||||||
|
|
||||||
|
if (!preferDarkTheme.has_value()) {
|
||||||
|
return std::nullopt;
|
||||||
|
} else if (*preferDarkTheme) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto themeName = integration->getStringSetting(qsl("gtk-theme-name"));
|
||||||
|
if (!themeName.has_value()) {
|
||||||
|
return std::nullopt;
|
||||||
|
} else if (themeName->toLower().contains(qsl("-dark"))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AutostartSupported() {
|
bool AutostartSupported() {
|
||||||
|
@ -848,38 +796,44 @@ bool WindowsNeedShadow() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Window::ControlsLayout WindowControlsLayout() {
|
Window::ControlsLayout WindowControlsLayout() {
|
||||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
const auto gtkResult = []() -> std::optional<Window::ControlsLayout> {
|
||||||
if (Libs::GtkSettingSupported()
|
const auto integration = GtkIntegration::Instance();
|
||||||
&& Libs::GtkLoaded()
|
if (!integration || !integration->checkVersion(3, 12, 0)) {
|
||||||
&& Libs::gtk_check_version != nullptr
|
return std::nullopt;
|
||||||
&& !Libs::gtk_check_version(3, 12, 0)) {
|
}
|
||||||
const auto decorationLayout = Libs::GtkSetting(
|
|
||||||
"gtk-decoration-layout").split(':');
|
const auto decorationLayoutSetting = integration->getStringSetting(
|
||||||
|
qsl("gtk-decoration-layout"));
|
||||||
|
|
||||||
|
if (!decorationLayoutSetting.has_value()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto decorationLayout = decorationLayoutSetting->split(':');
|
||||||
|
|
||||||
std::vector<Window::Control> controlsLeft;
|
std::vector<Window::Control> controlsLeft;
|
||||||
ranges::transform(
|
ranges::transform(
|
||||||
decorationLayout[0].split(','),
|
decorationLayout[0].split(','),
|
||||||
ranges::back_inserter(controlsLeft),
|
ranges::back_inserter(controlsLeft),
|
||||||
GtkKeywordToWindowControl
|
GtkKeywordToWindowControl);
|
||||||
);
|
|
||||||
|
|
||||||
std::vector<Window::Control> controlsRight;
|
std::vector<Window::Control> controlsRight;
|
||||||
if (decorationLayout.size() > 1) {
|
if (decorationLayout.size() > 1) {
|
||||||
ranges::transform(
|
ranges::transform(
|
||||||
decorationLayout[1].split(','),
|
decorationLayout[1].split(','),
|
||||||
ranges::back_inserter(controlsRight),
|
ranges::back_inserter(controlsRight),
|
||||||
GtkKeywordToWindowControl
|
GtkKeywordToWindowControl);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Window::ControlsLayout{
|
return Window::ControlsLayout{
|
||||||
.left = controlsLeft,
|
.left = controlsLeft,
|
||||||
.right = controlsRight
|
.right = controlsRight
|
||||||
};
|
};
|
||||||
}
|
}();
|
||||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
|
||||||
|
|
||||||
if (DesktopEnvironment::IsUnity()) {
|
if (gtkResult.has_value()) {
|
||||||
|
return *gtkResult;
|
||||||
|
} else if (DesktopEnvironment::IsUnity()) {
|
||||||
return Window::ControlsLayout{
|
return Window::ControlsLayout{
|
||||||
.left = {
|
.left = {
|
||||||
Window::Control::Close,
|
Window::Control::Close,
|
||||||
|
@ -983,7 +937,7 @@ void start() {
|
||||||
|
|
||||||
// if gtk integration and qgtk3/qgtk2 platformtheme (or qgtk2 style)
|
// if gtk integration and qgtk3/qgtk2 platformtheme (or qgtk2 style)
|
||||||
// is used at the same time, the app will crash
|
// is used at the same time, the app will crash
|
||||||
if (UseGtkIntegration()
|
if (GtkIntegration::Instance()
|
||||||
&& !IsStaticBinary()
|
&& !IsStaticBinary()
|
||||||
&& !qEnvironmentVariableIsSet(
|
&& !qEnvironmentVariableIsSet(
|
||||||
kIgnoreGtkIncompatibility.utf8())) {
|
kIgnoreGtkIncompatibility.utf8())) {
|
||||||
|
@ -1004,7 +958,7 @@ void start() {
|
||||||
"Keep in mind that this will lead to clipboard issues "
|
"Keep in mind that this will lead to clipboard issues "
|
||||||
"and tdesktop will be unable to get settings from GTK "
|
"and tdesktop will be unable to get settings from GTK "
|
||||||
"(such as decoration layout, dark mode & more).",
|
"(such as decoration layout, dark mode & more).",
|
||||||
kDisableGtkIntegration.utf8().constData());
|
internal::kDisableGtkIntegration.utf8().constData());
|
||||||
|
|
||||||
qunsetenv("QT_QPA_PLATFORMTHEME");
|
qunsetenv("QT_QPA_PLATFORMTHEME");
|
||||||
qunsetenv("QT_STYLE_OVERRIDE");
|
qunsetenv("QT_STYLE_OVERRIDE");
|
||||||
|
@ -1015,7 +969,7 @@ void start() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!UseGtkIntegration()) {
|
if (!GtkIntegration::Instance()) {
|
||||||
g_warning(
|
g_warning(
|
||||||
"GTK integration was disabled on build or in runtime. "
|
"GTK integration was disabled on build or in runtime. "
|
||||||
"This will lead to clipboard issues and a lack of some features "
|
"This will lead to clipboard issues and a lack of some features "
|
||||||
|
@ -1240,8 +1194,9 @@ void start() {
|
||||||
DEBUG_LOG(("Icon theme: %1").arg(QIcon::themeName()));
|
DEBUG_LOG(("Icon theme: %1").arg(QIcon::themeName()));
|
||||||
DEBUG_LOG(("Fallback icon theme: %1").arg(QIcon::fallbackThemeName()));
|
DEBUG_LOG(("Fallback icon theme: %1").arg(QIcon::fallbackThemeName()));
|
||||||
|
|
||||||
Libs::start();
|
if (const auto integration = GtkIntegration::Instance()) {
|
||||||
MainWindow::LibsLoaded();
|
return integration->load();
|
||||||
|
}
|
||||||
|
|
||||||
// wait for interface announce to know if native window frame is supported
|
// wait for interface announce to know if native window frame is supported
|
||||||
if (const auto waylandIntegration = WaylandIntegration::Instance()) {
|
if (const auto waylandIntegration = WaylandIntegration::Instance()) {
|
||||||
|
|
|
@ -21,7 +21,6 @@ bool InFlatpak();
|
||||||
bool InSnap();
|
bool InSnap();
|
||||||
bool IsStaticBinary();
|
bool IsStaticBinary();
|
||||||
bool AreQtPluginsBundled();
|
bool AreQtPluginsBundled();
|
||||||
bool UseGtkIntegration();
|
|
||||||
bool IsGtkIntegrationForced();
|
bool IsGtkIntegrationForced();
|
||||||
bool UseXDGDesktopPortal();
|
bool UseXDGDesktopPortal();
|
||||||
bool CanOpenDirectoryWithPortal();
|
bool CanOpenDirectoryWithPortal();
|
||||||
|
|
Loading…
Add table
Reference in a new issue