diff options
| author | Trent McPheron <twilightinzero@gmail.com> | 2022-04-03 20:04:00 -0400 |
|---|---|---|
| committer | ManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com> | 2022-12-16 16:21:23 +0100 |
| commit | 576271fb04e1c2f9ba1f95c0399fef2f73af3b06 (patch) | |
| tree | 3740f4878b837a1254cd3f7becc97321c1d0f31c /src/Fl_Native_File_Chooser_Zenity.cxx | |
| parent | 2ddb27f0f293eb0e57e32194e4f48c72614500ab (diff) | |
Add Zenity-based file chooser based on the KDialog one (HugLifeTiZ)
If available, it is used on Linux regardless of the current desktop because
it offers free XDG portal integration, which means it picks the correct file
chooser on all desktops, and allows for meaningful file selection in sandbox
environments like Flatpak.
Diffstat (limited to 'src/Fl_Native_File_Chooser_Zenity.cxx')
| -rw-r--r-- | src/Fl_Native_File_Chooser_Zenity.cxx | 285 |
1 files changed, 285 insertions, 0 deletions
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; +} |
