summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMake/options.cmake9
-rw-r--r--configh.cmake.in9
-rw-r--r--configh.in9
-rw-r--r--src/CMakeLists.txt4
-rw-r--r--src/Fl_Native_File_Chooser_GTK.cxx28
-rw-r--r--src/Fl_Native_File_Chooser_Zenity.H54
-rw-r--r--src/Fl_Native_File_Chooser_Zenity.cxx285
7 files changed, 393 insertions, 5 deletions
diff --git a/CMake/options.cmake b/CMake/options.cmake
index 25c5a181c..c5c83ae2d 100644
--- a/CMake/options.cmake
+++ b/CMake/options.cmake
@@ -830,6 +830,15 @@ endif (OPTION_FILESYSTEM_SUPPORT)
#######################################################################
#######################################################################
+option (OPTION_USE_ZENITY "Fl_Native_File_Chooser may run zenity" ON)
+if (OPTION_USE_ZENITY)
+ set (USE_ZENITY 1)
+else ()
+ set (USE_ZENITY 0)
+endif (OPTION_USE_ZENITY)
+#######################################################################
+
+#######################################################################
option (OPTION_USE_KDIALOG "Fl_Native_File_Chooser may run kdialog" ON)
if (OPTION_USE_KDIALOG)
set (USE_KDIALOG 1)
diff --git a/configh.cmake.in b/configh.cmake.in
index dde9ee40a..df211630e 100644
--- a/configh.cmake.in
+++ b/configh.cmake.in
@@ -343,7 +343,14 @@
#cmakedefine FL_CFG_NO_FILESYSTEM_SUPPORT 1
/*
- * Do we want class Fl_Native_File_Chooser to run kdialog when desktop is KDE?
+ * Do we want class Fl_Native_File_Chooser to run zenity when available?
+ */
+
+#cmakedefine01 USE_ZENITY
+
+/*
+ * Do we want class Fl_Native_File_Chooser to run kdialog when zenity is
+ * unavailable and desktop is KDE?
*/
#cmakedefine01 USE_KDIALOG
diff --git a/configh.in b/configh.in
index e45061b31..b9000f567 100644
--- a/configh.in
+++ b/configh.in
@@ -343,7 +343,14 @@
#undef FL_CFG_NO_FILESYSTEM_SUPPORT
/*
- * Do we want class Fl_Native_File_Chooser to run kdialog when desktop is KDE?
+ * Do we want class Fl_Native_File_Chooser to run zenity if available?
+ */
+
+#define USE_ZENITY 1
+
+/*
+ * Do we want class Fl_Native_File_Chooser to run kdialog when zenity is
+ * unavailable and desktop is KDE?
*/
#define USE_KDIALOG 1
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 49825d6be..f316d11de 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -215,6 +215,10 @@ if (FLTK_USE_X11 AND NOT OPTION_USE_WAYLAND)
Fl_get_key.cxx
)
+ if (OPTION_USE_ZENITY)
+ set (DRIVER_FILES ${DRIVER_FILES} Fl_Native_File_Chooser_Zenity.cxx)
+ endif (OPTION_USE_ZENITY)
+
if (OPTION_USE_KDIALOG)
set (DRIVER_FILES ${DRIVER_FILES} Fl_Native_File_Chooser_Kdialog.cxx)
endif (OPTION_USE_KDIALOG)
diff --git a/src/Fl_Native_File_Chooser_GTK.cxx b/src/Fl_Native_File_Chooser_GTK.cxx
index ff5df7293..e41c4230f 100644
--- a/src/Fl_Native_File_Chooser_GTK.cxx
+++ b/src/Fl_Native_File_Chooser_GTK.cxx
@@ -17,6 +17,9 @@
#include <config.h>
#include <FL/Fl_Native_File_Chooser.H>
+#if USE_ZENITY
+# include "Fl_Native_File_Chooser_Zenity.H"
+#endif
#if USE_KDIALOG
# include "Fl_Native_File_Chooser_Kdialog.H"
#endif
@@ -923,15 +926,34 @@ void Fl_GTK_Native_File_Chooser_Driver::probe_for_GTK_libs(void) {
#endif // HAVE_DLSYM && HAVE_DLFCN_H
Fl_Native_File_Chooser::Fl_Native_File_Chooser(int val) {
- // Use kdialog if available at run-time and if using the KDE desktop,
- // else, use GTK dialog if available at run-time
+ // Use zenity if available at run-time even if using the KDE desktop,
+ // because its portal integration means the KDE chooser will be used.
+ // Else use kdialog if available at run-time and if using the KDE
+ // desktop, else, use GTK dialog if available at run-time
// otherwise, use FLTK file chooser.
platform_fnfc = NULL;
fl_open_display();
if (Fl::option(Fl::OPTION_FNFC_USES_GTK)) {
+#if USE_ZENITY
+ if (val != BROWSE_MULTI_DIRECTORY) {
+ if (!Fl_Zenity_Native_File_Chooser_Driver::have_looked_for_zenity) {
+ // First Time here, try to find zenity
+ FILE *pipe = popen("zenity --version 2> /dev/null", "r");
+ if (pipe) {
+ char *p, line[100] = "";
+ p = fgets(line, sizeof(line), pipe);
+ if (p && strlen(line) > 0) Fl_Zenity_Native_File_Chooser_Driver::did_find_zenity = true;
+ pclose(pipe);
+ }
+ Fl_Zenity_Native_File_Chooser_Driver::have_looked_for_zenity = true;
+ }
+ // if we found zenity, we will use the Fl_Zenity_Native_File_Chooser_Driver
+ if (Fl_Zenity_Native_File_Chooser_Driver::did_find_zenity) platform_fnfc = new Fl_Zenity_Native_File_Chooser_Driver(val);
+ }
+#endif // USE_ZENITY
#if USE_KDIALOG
const char *desktop = getenv("XDG_CURRENT_DESKTOP");
- if (desktop && strcmp(desktop, "KDE") == 0 && val != BROWSE_MULTI_DIRECTORY) {
+ if (!platform_fnfc && desktop && strcmp(desktop, "KDE") == 0 && val != BROWSE_MULTI_DIRECTORY) {
if (!Fl_Kdialog_Native_File_Chooser_Driver::have_looked_for_kdialog) {
// First Time here, try to find kdialog
FILE *pipe = popen("kdialog -v 2> /dev/null", "r");
diff --git a/src/Fl_Native_File_Chooser_Zenity.H b/src/Fl_Native_File_Chooser_Zenity.H
new file mode 100644
index 000000000..90614bb4f
--- /dev/null
+++ b/src/Fl_Native_File_Chooser_Zenity.H
@@ -0,0 +1,54 @@
+//
+// FLTK native file chooser widget : Zenity version
+//
+// Copyright 2021-2022 by Bill Spitzak and others.
+//
+// This library is free software. Distribution and use rights are outlined in
+// the file "COPYING" which should have been included with this file. If this
+// file is missing or damaged, see the license at:
+//
+// https://www.fltk.org/COPYING.php
+//
+// Please see the following page on how to report bugs and issues:
+//
+// https://www.fltk.org/bugs.php
+//
+
+#ifndef FL_ZENITY_NATIVE_FILE_CHOOSER_H
+#define FL_ZENITY_NATIVE_FILE_CHOOSER_H 1
+
+#include <FL/Fl_Native_File_Chooser.H>
+
+class FL_EXPORT Fl_Zenity_Native_File_Chooser_Driver : public Fl_Native_File_Chooser_FLTK_Driver {
+ friend class Fl_Native_File_Chooser;
+ struct fnfc_pipe_struct {
+ char *all_files;
+ int fd;
+ };
+ static void fnfc_fd_cb(int fd, fnfc_pipe_struct *data);
+ char **_pathnames;
+ int _tpathnames;
+ char *_directory;
+ char *_preset_file;
+ char *_title;
+ static bool did_find_zenity;
+ static bool have_looked_for_zenity;
+ Fl_Zenity_Native_File_Chooser_Driver(int val);
+ ~Fl_Zenity_Native_File_Chooser_Driver();
+ int count() const;
+ const char *filename() const;
+ const char *filename(int i) const;
+ int show();
+ char *parse_filter(const char *f);
+ const char *filter() const;
+ virtual void filter(const char *f);
+ int filters() const;
+ void preset_file(const char *val);
+ const char *preset_file() const;
+ void directory(const char *val);
+ const char *directory() const;
+ void title(const char *val);
+ const char *title() const;
+};
+
+#endif // FL_ZENITY_NATIVE_FILE_CHOOSER_H
diff --git a/src/Fl_Native_File_Chooser_Zenity.cxx b/src/Fl_Native_File_Chooser_Zenity.cxx
new file mode 100644
index 000000000..2699d2b27
--- /dev/null
+++ b/src/Fl_Native_File_Chooser_Zenity.cxx
@@ -0,0 +1,285 @@
+//
+// FLTK native file chooser widget : Zenity version
+//
+// Copyright 2021-2022 by Bill Spitzak and others.
+//
+// This library is free software. Distribution and use rights are outlined in
+// the file "COPYING" which should have been included with this file. If this
+// file is missing or damaged, see the license at:
+//
+// https://www.fltk.org/COPYING.php
+//
+// Please see the following page on how to report bugs and issues:
+//
+// https://www.fltk.org/bugs.php
+//
+
+#include <config.h>
+#include <FL/Fl_Native_File_Chooser.H>
+#include "Fl_Native_File_Chooser_Zenity.H"
+#include "Fl_Window_Driver.H"
+#include "drivers/Unix/Fl_Unix_System_Driver.H"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+
+/* Fl_Zenity_Native_File_Chooser_Driver : file chooser based on the "zenity" command */
+
+bool Fl_Zenity_Native_File_Chooser_Driver::did_find_zenity = false;
+bool Fl_Zenity_Native_File_Chooser_Driver::have_looked_for_zenity = false;
+
+
+Fl_Zenity_Native_File_Chooser_Driver::Fl_Zenity_Native_File_Chooser_Driver(int val) : Fl_Native_File_Chooser_FLTK_Driver(val) {
+ _tpathnames = 0;
+ _pathnames = NULL;
+ _directory = NULL;
+ _preset_file = NULL;
+ _title = NULL;
+}
+
+
+Fl_Zenity_Native_File_Chooser_Driver::~Fl_Zenity_Native_File_Chooser_Driver() {
+ for (int i = 0; i < _tpathnames; i++) delete[] _pathnames[i];
+ delete[] _pathnames;
+ if (_preset_file) free(_preset_file);
+ if (_directory) free(_directory);
+ if (_title) free(_title);
+}
+
+
+void Fl_Zenity_Native_File_Chooser_Driver::fnfc_fd_cb(int fd,
+ Fl_Zenity_Native_File_Chooser_Driver::fnfc_pipe_struct *data) {
+ char tmp[FL_PATH_MAX];
+ int l = read(fd, tmp, sizeof(tmp)-1);
+ if (l > 0) {
+ tmp[l] = 0;
+ data->all_files = Fl_Native_File_Chooser_Driver::strapp(data->all_files, tmp);
+ } else {
+ data->fd = -1;
+ }
+}
+
+
+static int fnfc_dispatch(int /*event*/, Fl_Window* /*win*/) {
+ return 0;
+}
+
+
+int Fl_Zenity_Native_File_Chooser_Driver::show() {
+ const char *option;
+ switch (_btype) {
+ case Fl_Native_File_Chooser::BROWSE_MULTI_DIRECTORY: {
+ // BROWSE_MULTI_DIRECTORY is not supported by zenity, run other chooser instead
+ Fl_Native_File_Chooser fnfc(Fl_Native_File_Chooser::BROWSE_MULTI_DIRECTORY);
+ fnfc.title( title() );
+ fnfc.directory(directory());
+ fnfc.preset_file(preset_file());
+ fnfc.filter(filter());
+ fnfc.options(options());
+ int retval = fnfc.show();
+ for (int i = 0; i < _tpathnames; i++) delete[] _pathnames[i];
+ delete[] _pathnames; _pathnames = NULL;
+ _tpathnames = fnfc.count();
+ if (_tpathnames && retval == 0) {
+ _pathnames = new char*[_tpathnames];
+ for (int i = 0; i < _tpathnames; i++) {
+ _pathnames[i] = new char[strlen(fnfc.filename(i))+1];
+ strcpy(_pathnames[i], fnfc.filename(i));
+ }
+ }
+ return retval;
+ }
+ break;
+ case Fl_Native_File_Chooser::BROWSE_DIRECTORY:
+ case Fl_Native_File_Chooser::BROWSE_SAVE_DIRECTORY:
+ option = "--file-selection --directory";
+ break;
+
+ case Fl_Native_File_Chooser::BROWSE_SAVE_FILE:
+ option = "--file-selection --save";
+ break;
+
+ case Fl_Native_File_Chooser::BROWSE_MULTI_FILE:
+ option = "--file-selection --multiple";
+ break;
+
+ default:
+ option = "--file-selection";
+ }
+ char *preset = NULL;
+ if (_preset_file) {
+ preset = new char[strlen(_preset_file) + 15];
+ sprintf(preset, "--filename '%s'", _preset_file);
+ }
+ else if (_directory) {
+ // This doesn't actually seem to do anything, but supply it anyway.
+ preset = new char[strlen(_directory) + 15];
+ sprintf(preset, "--filename '%s'", _directory);
+ }
+ char *command = new char[strlen(option) + strlen(preset) + (_title?strlen(_title)+11:0) +
+ (_parsedfilt?strlen(_parsedfilt):0) + 50];
+ strcpy(command, "zenity ");
+ if (_title) {
+ sprintf(command+strlen(command), " --title '%s'", _title);
+ }
+ sprintf(command+strlen(command), " %s %s ", option, preset ? preset : "");
+ delete[] preset;
+ if (_parsedfilt) sprintf(command+strlen(command), " \"%s\" ", _parsedfilt);
+ strcat(command, "2> /dev/null"); // get rid of stderr output
+//puts(command);
+ FILE *pipe = popen(command, "r");
+ fnfc_pipe_struct data;
+ data.all_files = NULL;
+ if (pipe) {
+ data.fd = fileno(pipe);
+ Fl::add_fd(data.fd, FL_READ, (Fl_FD_Handler)fnfc_fd_cb, &data);
+ Fl_Event_Dispatch old_dispatch = Fl::event_dispatch();
+ // prevent FLTK from processing any event
+ Fl::event_dispatch(fnfc_dispatch);
+ void *control = ((Fl_Unix_System_Driver*)Fl::system_driver())->control_maximize_button(NULL);
+ // run event loop until pipe finishes
+ while (data.fd >= 0) Fl::wait();
+ Fl::remove_fd(fileno(pipe));
+ pclose(pipe);
+ // return to previous event processing by FLTK
+ Fl::event_dispatch(old_dispatch);
+ if (control) ((Fl_Unix_System_Driver*)Fl::system_driver())->control_maximize_button(control);
+ if (data.all_files) {
+ // process text received from pipe
+ if (data.all_files[strlen(data.all_files)-1] == '\n') data.all_files[strlen(data.all_files)-1] = 0;
+ for (int i = 0; i < _tpathnames; i++) delete[] _pathnames[i];
+ delete[] _pathnames;
+ char *p = data.all_files;
+ int count = 1;
+ while ((p = strchr(p+1, ' '))) count++;
+ _pathnames = new char*[count];
+ _tpathnames = 0;
+ char *q = strtok(data.all_files, " ");
+ while (q) {
+ _pathnames[_tpathnames] = new char[strlen(q)+1];
+ strcpy(_pathnames[_tpathnames], q);
+ _tpathnames++;
+ q = strtok(NULL, " ");
+ }
+ }
+ }
+ delete[] command;
+ if (_title) { free(_title); _title = NULL; }
+ if (!pipe) return -1;
+ return (data.all_files == NULL ? 1 : 0);
+}
+
+
+const char *Fl_Zenity_Native_File_Chooser_Driver::filename() const {
+ return _tpathnames >= 1 ? _pathnames[0] : NULL;
+}
+
+const char *Fl_Zenity_Native_File_Chooser_Driver::filename (int i) const {
+ return _tpathnames > i ? _pathnames[i] : NULL;
+}
+
+const char *Fl_Zenity_Native_File_Chooser_Driver::filter() const {
+ return _filter;
+}
+
+int Fl_Zenity_Native_File_Chooser_Driver::filters() const {
+ return (_nfilters ? _nfilters - 1 : 0);
+}
+
+int Fl_Zenity_Native_File_Chooser_Driver::count() const {
+ return _tpathnames;
+}
+
+char *Fl_Zenity_Native_File_Chooser_Driver::parse_filter(const char *f) {
+ //In: "*.H\n" or "*.H" Out: "(*.H)"
+ //In: "Headers\t*.H\n" Out: "Headers (*.H)"
+ //In: "Headers\t*.{H,h}\n" Out: "Headers (*.H *.h)"
+ const char *p = strchr(f, '\t');
+ if (!p) p = f - 1;
+ const char *q = strchr(f, '\n'); if (!q) q = f + strlen(f);
+ const char *r = strchr(f, '{');
+ char *developed = NULL;
+ if (r) { // with {}
+ char *lead = new char[r-p];
+ memcpy(lead, p+1, (r-p)-1); lead[(r-p)-1] = 0;
+ const char *r2 = strchr(r, '}');
+ char *ends = new char[r2-r];
+ memcpy(ends, r+1, (r2-r)-1); ends[(r2-r)-1] = 0;
+ char *ptr;
+ char *part = strtok_r(ends, ",", &ptr);
+ while (part) {
+ developed = strapp(developed, lead);
+ developed = strapp(developed, part);
+ developed = strapp(developed, " ");
+ part = strtok_r(NULL, ",", &ptr);
+ }
+ if (developed[strlen(developed)-1] == ' ') developed[strlen(developed)-1] = 0;
+ delete[] lead;
+ delete[] ends;
+ }
+ int lout = (p>f?p-f:0) + 2 + (r?strlen(developed):((q-p)-1)) + 2;
+ char *out = new char[lout]; *out = 0;
+ if (p > f) {memcpy(out, f, p-f); out[p-f] = 0; }
+ strcat(out, " (");
+ if (r) {
+ strcpy(out+strlen(out), developed);
+ strfree(developed);
+ }
+ else memcpy(out+strlen(out), p+1, (q-p));
+ strcat(out, ")");
+//puts(out);
+ return out;
+}
+
+
+void Fl_Zenity_Native_File_Chooser_Driver::filter(const char *f) {
+ _parsedfilt = strfree(_parsedfilt); // clear previous parsed filter (if any)
+ _nfilters = 0;
+ if (!f) return;
+ _filter = strdup(f);
+ char *f2 = strdup(f);
+ char *ptr;
+ char *part = strtok_r(f2, "\n", &ptr);
+ while (part) {
+ char *p = parse_filter(part);
+ _parsedfilt = strapp(_parsedfilt, p);
+ _parsedfilt = strapp(_parsedfilt, "\\n");
+ delete[] p;
+ _nfilters++;
+ part = strtok_r(NULL, "\n", &ptr);
+ }
+ free(f2);
+ _parsedfilt = strapp(_parsedfilt, "All files (*)");
+ _nfilters++;
+//puts(_parsedfilt);
+}
+
+void Fl_Zenity_Native_File_Chooser_Driver::preset_file(const char *val) {
+ if (_preset_file) free(_preset_file);
+ _preset_file = strdup(val);
+}
+
+const char *Fl_Zenity_Native_File_Chooser_Driver::preset_file() const {
+ return _preset_file;
+}
+
+void Fl_Zenity_Native_File_Chooser_Driver::directory(const char *val) {
+ if (_directory) free(_directory);
+ _directory = strdup(val);
+}
+
+const char *Fl_Zenity_Native_File_Chooser_Driver::directory() const {
+ return _directory;
+}
+
+void Fl_Zenity_Native_File_Chooser_Driver::title(const char *val)
+{
+ if (_title) free(_title);
+ _title = strdup(val);
+}
+
+const char *Fl_Zenity_Native_File_Chooser_Driver::title() const {
+ return _title;
+}