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.
This commit is contained in:
Trent McPheron
2022-04-03 20:04:00 -04:00
committed by ManoloFLTK
parent 2ddb27f0f2
commit 576271fb04
7 changed files with 393 additions and 5 deletions
+9
View File
@@ -829,6 +829,15 @@ if (OPTION_FILESYSTEM_SUPPORT)
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)
+8 -1
View File
@@ -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
+8 -1
View File
@@ -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
+4
View File
@@ -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)
+25 -3
View File
@@ -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");
+54
View File
@@ -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
+285
View File
@@ -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;
}