1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qgtk3dialoghelpers.h"
5#include "qgtk3theme.h"
6
7#include <qeventloop.h>
8#include <qwindow.h>
9#include <qcolor.h>
10#include <qdebug.h>
11#include <qfont.h>
12#include <qfileinfo.h>
13
14#include <private/qguiapplication_p.h>
15#include <private/qgenericunixservices_p.h>
16#include <qpa/qplatformintegration.h>
17#include <qpa/qplatformfontdatabase.h>
18
19#undef signals
20#include <gtk/gtk.h>
21#include <gdk/gdk.h>
22#include <pango/pango.h>
23
24#if QT_CONFIG(xlib) && defined(GDK_WINDOWING_X11)
25#include <gdk/gdkx.h>
26#endif
27
28#ifdef GDK_WINDOWING_WAYLAND
29#include <gdk/gdkwayland.h>
30#endif
31
32// The size of the preview we display for selected image files. We set height
33// larger than width because generally there is more free space vertically
34// than horizontally (setting the preview image will always expand the width of
35// the dialog, but usually not the height). The image's aspect ratio will always
36// be preserved.
37#define PREVIEW_WIDTH 256
38#define PREVIEW_HEIGHT 512
39
40QT_BEGIN_NAMESPACE
41
42using namespace Qt::StringLiterals;
43
44class QGtk3Dialog
45{
46public:
47 QGtk3Dialog(GtkWidget *gtkWidget, QPlatformDialogHelper *helper);
48 ~QGtk3Dialog();
49
50 GtkDialog *gtkDialog() const;
51
52 void exec();
53 bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent);
54 void hide();
55
56protected:
57 static void onResponse(QPlatformDialogHelper *helper, int response);
58
59private:
60 GtkWidget *gtkWidget;
61 QPlatformDialogHelper *helper;
62 Qt::WindowModality modality;
63};
64
65QGtk3Dialog::QGtk3Dialog(GtkWidget *gtkWidget, QPlatformDialogHelper *helper)
66 : gtkWidget(gtkWidget)
67 , helper(helper)
68{
69 g_signal_connect_swapped(G_OBJECT(gtkWidget), "response", G_CALLBACK(onResponse), helper);
70 g_signal_connect(G_OBJECT(gtkWidget), "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
71}
72
73QGtk3Dialog::~QGtk3Dialog()
74{
75 gtk_clipboard_store(clipboard: gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
76 gtk_widget_destroy(widget: gtkWidget);
77}
78
79GtkDialog *QGtk3Dialog::gtkDialog() const
80{
81 return GTK_DIALOG(gtkWidget);
82}
83
84void QGtk3Dialog::exec()
85{
86 if (modality == Qt::ApplicationModal) {
87 // block input to the whole app, including other GTK dialogs
88 gtk_dialog_run(dialog: gtkDialog());
89 } else {
90 // block input to the window, allow input to other GTK dialogs
91 QEventLoop loop;
92 loop.connect(asender: helper, SIGNAL(accept()), SLOT(quit()));
93 loop.connect(asender: helper, SIGNAL(reject()), SLOT(quit()));
94 loop.exec();
95 }
96}
97
98bool QGtk3Dialog::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent)
99{
100 Q_UNUSED(flags);
101 this->modality = modality;
102
103 gtk_widget_realize(widget: gtkWidget); // creates X window
104
105 GdkWindow *gdkWindow = gtk_widget_get_window(widget: gtkWidget);
106 if (parent) {
107 if (false) {
108#if defined(GDK_WINDOWING_WAYLAND) && GTK_CHECK_VERSION(3, 22, 0)
109 } else if (GDK_IS_WAYLAND_WINDOW(gdkWindow)) {
110 const auto unixServices = dynamic_cast<QGenericUnixServices *>(
111 QGuiApplicationPrivate::platformIntegration()->services());
112 if (unixServices) {
113 const auto handle = unixServices->portalWindowIdentifier(window: parent);
114 if (handle.startsWith(s: "wayland:"_L1)) {
115 auto handleBa = handle.sliced(pos: 8).toUtf8();
116 gdk_wayland_window_set_transient_for_exported(window: gdkWindow, parent_handle_str: handleBa.data());
117 }
118 }
119#endif
120#if QT_CONFIG(xlib) && defined(GDK_WINDOWING_X11)
121 } else if (GDK_IS_X11_WINDOW(gdkWindow)) {
122 GdkDisplay *gdkDisplay = gdk_window_get_display(window: gdkWindow);
123 XSetTransientForHint(gdk_x11_display_get_xdisplay(display: gdkDisplay),
124 gdk_x11_window_get_xid(window: gdkWindow),
125 parent->winId());
126#endif
127 }
128 }
129
130 if (modality != Qt::NonModal) {
131 gdk_window_set_modal_hint(window: gdkWindow, modal: true);
132 }
133
134 gtk_widget_show(widget: gtkWidget);
135 gdk_window_focus(window: gdkWindow, GDK_CURRENT_TIME);
136 return true;
137}
138
139void QGtk3Dialog::hide()
140{
141 gtk_widget_hide(widget: gtkWidget);
142}
143
144void QGtk3Dialog::onResponse(QPlatformDialogHelper *helper, int response)
145{
146 if (response == GTK_RESPONSE_OK)
147 emit helper->accept();
148 else
149 emit helper->reject();
150}
151
152QGtk3ColorDialogHelper::QGtk3ColorDialogHelper()
153{
154 d.reset(other: new QGtk3Dialog(gtk_color_chooser_dialog_new(title: "", parent: nullptr), this));
155 g_signal_connect_swapped(d->gtkDialog(), "notify::rgba", G_CALLBACK(onColorChanged), this);
156}
157
158QGtk3ColorDialogHelper::~QGtk3ColorDialogHelper()
159{
160}
161
162bool QGtk3ColorDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent)
163{
164 applyOptions();
165 return d->show(flags, modality, parent);
166}
167
168void QGtk3ColorDialogHelper::exec()
169{
170 d->exec();
171}
172
173void QGtk3ColorDialogHelper::hide()
174{
175 d->hide();
176}
177
178void QGtk3ColorDialogHelper::setCurrentColor(const QColor &color)
179{
180 GtkDialog *gtkDialog = d->gtkDialog();
181 if (color.alpha() < 255)
182 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(gtkDialog), use_alpha: true);
183 GdkRGBA gdkColor;
184 gdkColor.red = color.redF();
185 gdkColor.green = color.greenF();
186 gdkColor.blue = color.blueF();
187 gdkColor.alpha = color.alphaF();
188 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(gtkDialog), color: &gdkColor);
189}
190
191QColor QGtk3ColorDialogHelper::currentColor() const
192{
193 GtkDialog *gtkDialog = d->gtkDialog();
194 GdkRGBA gdkColor;
195 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(gtkDialog), color: &gdkColor);
196 return QColor::fromRgbF(r: gdkColor.red, g: gdkColor.green, b: gdkColor.blue, a: gdkColor.alpha);
197}
198
199void QGtk3ColorDialogHelper::onColorChanged(QGtk3ColorDialogHelper *dialog)
200{
201 emit dialog->currentColorChanged(color: dialog->currentColor());
202}
203
204void QGtk3ColorDialogHelper::applyOptions()
205{
206 GtkDialog *gtkDialog = d->gtkDialog();
207 gtk_window_set_title(GTK_WINDOW(gtkDialog), qUtf8Printable(options()->windowTitle()));
208
209 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(gtkDialog), use_alpha: options()->testOption(option: QColorDialogOptions::ShowAlphaChannel));
210}
211
212QGtk3FileDialogHelper::QGtk3FileDialogHelper()
213{
214 d.reset(other: new QGtk3Dialog(gtk_file_chooser_dialog_new(title: "", parent: nullptr,
215 action: GTK_FILE_CHOOSER_ACTION_OPEN,
216 qUtf8Printable(QGtk3Theme::defaultStandardButtonText(QPlatformDialogHelper::Cancel)), GTK_RESPONSE_CANCEL,
217 qUtf8Printable(QGtk3Theme::defaultStandardButtonText(QPlatformDialogHelper::Ok)), GTK_RESPONSE_OK,
218 NULL), this));
219
220 g_signal_connect(GTK_FILE_CHOOSER(d->gtkDialog()), "selection-changed", G_CALLBACK(onSelectionChanged), this);
221 g_signal_connect_swapped(GTK_FILE_CHOOSER(d->gtkDialog()), "current-folder-changed", G_CALLBACK(onCurrentFolderChanged), this);
222 g_signal_connect_swapped(GTK_FILE_CHOOSER(d->gtkDialog()), "notify::filter", G_CALLBACK(onFilterChanged), this);
223
224 previewWidget = gtk_image_new();
225 g_signal_connect(G_OBJECT(d->gtkDialog()), "update-preview", G_CALLBACK(onUpdatePreview), this);
226 gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(d->gtkDialog()), preview_widget: previewWidget);
227}
228
229QGtk3FileDialogHelper::~QGtk3FileDialogHelper()
230{
231}
232
233bool QGtk3FileDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent)
234{
235 _dir.clear();
236 _selection.clear();
237
238 applyOptions();
239 return d->show(flags, modality, parent);
240}
241
242void QGtk3FileDialogHelper::exec()
243{
244 d->exec();
245}
246
247void QGtk3FileDialogHelper::hide()
248{
249 // After GtkFileChooserDialog has been hidden, gtk_file_chooser_get_current_folder()
250 // & gtk_file_chooser_get_filenames() will return bogus values -> cache the actual
251 // values before hiding the dialog
252 _dir = directory();
253 _selection = selectedFiles();
254
255 d->hide();
256}
257
258bool QGtk3FileDialogHelper::defaultNameFilterDisables() const
259{
260 return false;
261}
262
263void QGtk3FileDialogHelper::setDirectory(const QUrl &directory)
264{
265 GtkDialog *gtkDialog = d->gtkDialog();
266 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(gtkDialog), qUtf8Printable(directory.toLocalFile()));
267}
268
269QUrl QGtk3FileDialogHelper::directory() const
270{
271 // While GtkFileChooserDialog is hidden, gtk_file_chooser_get_current_folder()
272 // returns a bogus value -> return the cached value before hiding
273 if (!_dir.isEmpty())
274 return _dir;
275
276 QString ret;
277 GtkDialog *gtkDialog = d->gtkDialog();
278 gchar *folder = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(gtkDialog));
279 if (folder) {
280 ret = QString::fromUtf8(utf8: folder);
281 g_free(mem: folder);
282 }
283 return QUrl::fromLocalFile(localfile: ret);
284}
285
286void QGtk3FileDialogHelper::selectFile(const QUrl &filename)
287{
288 setFileChooserAction();
289 selectFileInternal(filename);
290}
291
292void QGtk3FileDialogHelper::selectFileInternal(const QUrl &filename)
293{
294 GtkDialog *gtkDialog = d->gtkDialog();
295 if (options()->acceptMode() == QFileDialogOptions::AcceptSave) {
296 QFileInfo fi(filename.toLocalFile());
297 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(gtkDialog), qUtf8Printable(fi.path()));
298 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(gtkDialog), qUtf8Printable(fi.fileName()));
299 } else {
300 gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(gtkDialog), qUtf8Printable(filename.toLocalFile()));
301 }
302}
303
304QList<QUrl> QGtk3FileDialogHelper::selectedFiles() const
305{
306 // While GtkFileChooserDialog is hidden, gtk_file_chooser_get_filenames()
307 // returns a bogus value -> return the cached value before hiding
308 if (!_selection.isEmpty())
309 return _selection;
310
311 QList<QUrl> selection;
312 GtkDialog *gtkDialog = d->gtkDialog();
313 GSList *filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(gtkDialog));
314 for (GSList *it = filenames; it; it = it->next)
315 selection += QUrl::fromLocalFile(localfile: QString::fromUtf8(utf8: (const char*)it->data));
316 g_slist_free(list: filenames);
317 return selection;
318}
319
320void QGtk3FileDialogHelper::setFilter()
321{
322 applyOptions();
323}
324
325void QGtk3FileDialogHelper::selectNameFilter(const QString &filter)
326{
327 GtkFileFilter *gtkFilter = _filters.value(key: filter);
328 if (gtkFilter) {
329 GtkDialog *gtkDialog = d->gtkDialog();
330 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(gtkDialog), filter: gtkFilter);
331 }
332}
333
334QString QGtk3FileDialogHelper::selectedNameFilter() const
335{
336 GtkDialog *gtkDialog = d->gtkDialog();
337 GtkFileFilter *gtkFilter = gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(gtkDialog));
338 return _filterNames.value(key: gtkFilter);
339}
340
341void QGtk3FileDialogHelper::onSelectionChanged(GtkDialog *gtkDialog, QGtk3FileDialogHelper *helper)
342{
343 QString selection;
344 gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(gtkDialog));
345 if (filename) {
346 selection = QString::fromUtf8(utf8: filename);
347 g_free(mem: filename);
348 }
349 emit helper->currentChanged(path: QUrl::fromLocalFile(localfile: selection));
350}
351
352void QGtk3FileDialogHelper::onCurrentFolderChanged(QGtk3FileDialogHelper *dialog)
353{
354 emit dialog->directoryEntered(directory: dialog->directory());
355}
356
357void QGtk3FileDialogHelper::onFilterChanged(QGtk3FileDialogHelper *dialog)
358{
359 emit dialog->filterSelected(filter: dialog->selectedNameFilter());
360}
361
362void QGtk3FileDialogHelper::onUpdatePreview(GtkDialog *gtkDialog, QGtk3FileDialogHelper *helper)
363{
364 gchar *filename = gtk_file_chooser_get_preview_filename(GTK_FILE_CHOOSER(gtkDialog));
365 if (!filename) {
366 gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(gtkDialog), active: false);
367 return;
368 }
369
370 // Don't attempt to open anything which isn't a regular file. If a named pipe,
371 // this may hang.
372 QFileInfo fileinfo(filename);
373 if (!fileinfo.exists() || !fileinfo.isFile()) {
374 g_free(mem: filename);
375 gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(gtkDialog), active: false);
376 return;
377 }
378
379 // This will preserve the image's aspect ratio.
380 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file_at_size(filename, PREVIEW_WIDTH, PREVIEW_HEIGHT, error: 0);
381 g_free(mem: filename);
382 if (pixbuf) {
383 gtk_image_set_from_pixbuf(GTK_IMAGE(helper->previewWidget), pixbuf);
384 g_object_unref(object: pixbuf);
385 }
386 gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(gtkDialog), active: pixbuf ? true : false);
387}
388
389static GtkFileChooserAction gtkFileChooserAction(const QSharedPointer<QFileDialogOptions> &options)
390{
391 switch (options->fileMode()) {
392 case QFileDialogOptions::AnyFile:
393 case QFileDialogOptions::ExistingFile:
394 case QFileDialogOptions::ExistingFiles:
395 if (options->acceptMode() == QFileDialogOptions::AcceptOpen)
396 return GTK_FILE_CHOOSER_ACTION_OPEN;
397 else
398 return GTK_FILE_CHOOSER_ACTION_SAVE;
399 case QFileDialogOptions::Directory:
400 case QFileDialogOptions::DirectoryOnly:
401 default:
402 if (options->acceptMode() == QFileDialogOptions::AcceptOpen)
403 return GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
404 else
405 return GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER;
406 }
407}
408
409void QGtk3FileDialogHelper::setFileChooserAction()
410{
411 GtkDialog *gtkDialog = d->gtkDialog();
412
413 const GtkFileChooserAction action = gtkFileChooserAction(options: options());
414 gtk_file_chooser_set_action(GTK_FILE_CHOOSER(gtkDialog), action);
415}
416
417void QGtk3FileDialogHelper::applyOptions()
418{
419 GtkDialog *gtkDialog = d->gtkDialog();
420 const QSharedPointer<QFileDialogOptions> &opts = options();
421
422 gtk_window_set_title(GTK_WINDOW(gtkDialog), qUtf8Printable(opts->windowTitle()));
423 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(gtkDialog), local_only: true);
424
425 setFileChooserAction();
426
427 const bool selectMultiple = opts->fileMode() == QFileDialogOptions::ExistingFiles;
428 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(gtkDialog), select_multiple: selectMultiple);
429
430 const bool confirmOverwrite = !opts->testOption(option: QFileDialogOptions::DontConfirmOverwrite);
431 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(gtkDialog), do_overwrite_confirmation: confirmOverwrite);
432
433 const bool readOnly = opts->testOption(option: QFileDialogOptions::ReadOnly);
434 gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER(gtkDialog), create_folders: !readOnly);
435
436 const QStringList nameFilters = opts->nameFilters();
437 if (!nameFilters.isEmpty())
438 setNameFilters(nameFilters);
439
440 if (opts->initialDirectory().isLocalFile())
441 setDirectory(opts->initialDirectory());
442
443 foreach (const QUrl &filename, opts->initiallySelectedFiles())
444 selectFileInternal(filename);
445
446 const QString initialNameFilter = opts->initiallySelectedNameFilter();
447 if (!initialNameFilter.isEmpty())
448 selectNameFilter(filter: initialNameFilter);
449
450 GtkWidget *acceptButton = gtk_dialog_get_widget_for_response(dialog: gtkDialog, response_id: GTK_RESPONSE_OK);
451 if (acceptButton) {
452 if (opts->isLabelExplicitlySet(label: QFileDialogOptions::Accept))
453 gtk_button_set_label(GTK_BUTTON(acceptButton), qUtf8Printable(opts->labelText(QFileDialogOptions::Accept)));
454 else if (opts->acceptMode() == QFileDialogOptions::AcceptOpen)
455 gtk_button_set_label(GTK_BUTTON(acceptButton), qUtf8Printable(QGtk3Theme::defaultStandardButtonText(QPlatformDialogHelper::Open)));
456 else
457 gtk_button_set_label(GTK_BUTTON(acceptButton), qUtf8Printable(QGtk3Theme::defaultStandardButtonText(QPlatformDialogHelper::Save)));
458 }
459
460 GtkWidget *rejectButton = gtk_dialog_get_widget_for_response(dialog: gtkDialog, response_id: GTK_RESPONSE_CANCEL);
461 if (rejectButton) {
462 if (opts->isLabelExplicitlySet(label: QFileDialogOptions::Reject))
463 gtk_button_set_label(GTK_BUTTON(rejectButton), qUtf8Printable(opts->labelText(QFileDialogOptions::Reject)));
464 else
465 gtk_button_set_label(GTK_BUTTON(rejectButton), qUtf8Printable(QGtk3Theme::defaultStandardButtonText(QPlatformDialogHelper::Cancel)));
466 }
467}
468
469void QGtk3FileDialogHelper::setNameFilters(const QStringList &filters)
470{
471 GtkDialog *gtkDialog = d->gtkDialog();
472 foreach (GtkFileFilter *filter, _filters)
473 gtk_file_chooser_remove_filter(GTK_FILE_CHOOSER(gtkDialog), filter);
474
475 _filters.clear();
476 _filterNames.clear();
477
478 foreach (const QString &filter, filters) {
479 GtkFileFilter *gtkFilter = gtk_file_filter_new();
480 const QString name = filter.left(n: filter.indexOf(c: u'('));
481 const QStringList extensions = cleanFilterList(filter);
482
483 gtk_file_filter_set_name(filter: gtkFilter, qUtf8Printable(name.isEmpty() ? extensions.join(", "_L1) : name));
484 foreach (const QString &ext, extensions)
485 gtk_file_filter_add_pattern(filter: gtkFilter, qUtf8Printable(ext));
486
487 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(gtkDialog), filter: gtkFilter);
488
489 _filters.insert(key: filter, value: gtkFilter);
490 _filterNames.insert(key: gtkFilter, value: filter);
491 }
492}
493
494QGtk3FontDialogHelper::QGtk3FontDialogHelper()
495{
496 d.reset(other: new QGtk3Dialog(gtk_font_chooser_dialog_new(title: "", parent: nullptr), this));
497 g_signal_connect_swapped(d->gtkDialog(), "notify::font", G_CALLBACK(onFontChanged), this);
498}
499
500QGtk3FontDialogHelper::~QGtk3FontDialogHelper()
501{
502}
503
504bool QGtk3FontDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent)
505{
506 applyOptions();
507 return d->show(flags, modality, parent);
508}
509
510void QGtk3FontDialogHelper::exec()
511{
512 d->exec();
513}
514
515void QGtk3FontDialogHelper::hide()
516{
517 d->hide();
518}
519
520static QString qt_fontToString(const QFont &font)
521{
522 PangoFontDescription *desc = pango_font_description_new();
523 pango_font_description_set_size(desc, size: (font.pointSizeF() > 0.0 ? font.pointSizeF() : QFontInfo(font).pointSizeF()) * PANGO_SCALE);
524 pango_font_description_set_family(desc, qUtf8Printable(QFontInfo(font).family()));
525
526 int weight = font.weight();
527 if (weight >= QFont::Black)
528 pango_font_description_set_weight(desc, weight: PANGO_WEIGHT_HEAVY);
529 else if (weight >= QFont::ExtraBold)
530 pango_font_description_set_weight(desc, weight: PANGO_WEIGHT_ULTRABOLD);
531 else if (weight >= QFont::Bold)
532 pango_font_description_set_weight(desc, weight: PANGO_WEIGHT_BOLD);
533 else if (weight >= QFont::DemiBold)
534 pango_font_description_set_weight(desc, weight: PANGO_WEIGHT_SEMIBOLD);
535 else if (weight >= QFont::Medium)
536 pango_font_description_set_weight(desc, weight: PANGO_WEIGHT_MEDIUM);
537 else if (weight >= QFont::Normal)
538 pango_font_description_set_weight(desc, weight: PANGO_WEIGHT_NORMAL);
539 else if (weight >= QFont::Light)
540 pango_font_description_set_weight(desc, weight: PANGO_WEIGHT_LIGHT);
541 else if (weight >= QFont::ExtraLight)
542 pango_font_description_set_weight(desc, weight: PANGO_WEIGHT_ULTRALIGHT);
543 else
544 pango_font_description_set_weight(desc, weight: PANGO_WEIGHT_THIN);
545
546 int style = font.style();
547 if (style == QFont::StyleItalic)
548 pango_font_description_set_style(desc, style: PANGO_STYLE_ITALIC);
549 else if (style == QFont::StyleOblique)
550 pango_font_description_set_style(desc, style: PANGO_STYLE_OBLIQUE);
551 else
552 pango_font_description_set_style(desc, style: PANGO_STYLE_NORMAL);
553
554 char *str = pango_font_description_to_string(desc);
555 QString name = QString::fromUtf8(utf8: str);
556 pango_font_description_free(desc);
557 g_free(mem: str);
558 return name;
559}
560
561static QFont qt_fontFromString(const QString &name)
562{
563 QFont font;
564 PangoFontDescription *desc = pango_font_description_from_string(qUtf8Printable(name));
565 font.setPointSizeF(static_cast<float>(pango_font_description_get_size(desc)) / PANGO_SCALE);
566
567 QString family = QString::fromUtf8(utf8: pango_font_description_get_family(desc));
568 if (!family.isEmpty())
569 font.setFamilies(QStringList{family});
570
571 font.setWeight(QFont::Weight(pango_font_description_get_weight(desc)));
572
573 PangoStyle style = pango_font_description_get_style(desc);
574 if (style == PANGO_STYLE_ITALIC)
575 font.setStyle(QFont::StyleItalic);
576 else if (style == PANGO_STYLE_OBLIQUE)
577 font.setStyle(QFont::StyleOblique);
578 else
579 font.setStyle(QFont::StyleNormal);
580
581 pango_font_description_free(desc);
582 return font;
583}
584
585void QGtk3FontDialogHelper::setCurrentFont(const QFont &font)
586{
587 GtkFontChooser *gtkDialog = GTK_FONT_CHOOSER(d->gtkDialog());
588 gtk_font_chooser_set_font(fontchooser: gtkDialog, qUtf8Printable(qt_fontToString(font)));
589}
590
591QFont QGtk3FontDialogHelper::currentFont() const
592{
593 GtkFontChooser *gtkDialog = GTK_FONT_CHOOSER(d->gtkDialog());
594 gchar *name = gtk_font_chooser_get_font(fontchooser: gtkDialog);
595 QFont font = qt_fontFromString(name: QString::fromUtf8(utf8: name));
596 g_free(mem: name);
597 return font;
598}
599
600void QGtk3FontDialogHelper::onFontChanged(QGtk3FontDialogHelper *dialog)
601{
602 emit dialog->currentFontChanged(font: dialog->currentFont());
603}
604
605void QGtk3FontDialogHelper::applyOptions()
606{
607 GtkDialog *gtkDialog = d->gtkDialog();
608 const QSharedPointer<QFontDialogOptions> &opts = options();
609
610 gtk_window_set_title(GTK_WINDOW(gtkDialog), qUtf8Printable(opts->windowTitle()));
611}
612
613QT_END_NAMESPACE
614
615#include "moc_qgtk3dialoghelpers.cpp"
616

source code of qtbase/src/plugins/platformthemes/gtk3/qgtk3dialoghelpers.cpp