601 lines
17 KiB
C++
601 lines
17 KiB
C++
|
// fl_file_chooser.c
|
||
|
|
||
|
// fltk (Fast Light Tool Kit) version 0.99
|
||
|
// Copyright (C) 1998 Bill Spitzak
|
||
|
|
||
|
// The "completion" file chooser for fltk
|
||
|
// Designed and implemented by Bill Spitzak 12/17/93
|
||
|
// Rewritten for fltk 4/29/96
|
||
|
// Rewritten to use scandir() 1/7/97
|
||
|
|
||
|
#include <config.h>
|
||
|
#include <FL/fl_file_chooser.H>
|
||
|
|
||
|
#include <FL/Fl.H>
|
||
|
#include <FL/Fl_Window.H>
|
||
|
#include <FL/Fl_Box.H>
|
||
|
#include <FL/Fl_Button.H>
|
||
|
#include <FL/Fl_Return_Button.H>
|
||
|
#include <FL/Fl_Browser_.H>
|
||
|
#include <FL/Fl_Input.H>
|
||
|
#include <FL/fl_draw.H>
|
||
|
#include <FL/filename.H>
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <errno.h>
|
||
|
#include <ctype.h>
|
||
|
|
||
|
static void default_callback(const char*) {}
|
||
|
static void (*current_callback)(const char*) = default_callback;
|
||
|
void fl_file_chooser_callback(void (*cb)(const char*)) {
|
||
|
current_callback = cb ? cb : default_callback;
|
||
|
}
|
||
|
|
||
|
// "File Chooser Browser" widget:
|
||
|
class FCB : public Fl_Browser_ {
|
||
|
void* item_first() const ;
|
||
|
void* item_next(void*) const ;
|
||
|
void* item_prev(void*) const ;
|
||
|
int item_height(const dirent*, int) const ;
|
||
|
int item_height(void*) const ;
|
||
|
int item_width(const dirent*) const ;
|
||
|
int item_width(void*) const ;
|
||
|
int item_quick_height(void*) const ;
|
||
|
int incr_height() const ;
|
||
|
void item_draw(void*, int, int, int, int) const ;
|
||
|
int checkdir(const dirent*, char*) const ;
|
||
|
void draw();
|
||
|
void clear_prev();
|
||
|
public:
|
||
|
char listed[FL_PATH_MAX];// current dir & starname
|
||
|
int dirend; // points after last / before starname
|
||
|
int nameend; // length to trailing '*' or '\0'
|
||
|
const char* pattern; // default pattern
|
||
|
dirent** list; // the file names
|
||
|
dirent** last; // pointer after end of list
|
||
|
const char* message; // message if no file names
|
||
|
char preved[FL_PATH_MAX];// directory listed in prev
|
||
|
dirent** prev; // cached list of another directory
|
||
|
dirent** prev_last; // end of that list
|
||
|
int prev_count;
|
||
|
FCB(int x, int y, int w, int h) : Fl_Browser_(x, y, w, h, 0) {
|
||
|
type(FL_HOLD_BROWSER);
|
||
|
listed[0] = 0;
|
||
|
dirend = nameend = 1;
|
||
|
pattern = 0;
|
||
|
list = prev = 0;
|
||
|
message = 0;
|
||
|
}
|
||
|
// ~FCB nyi
|
||
|
void clear();
|
||
|
void set(const char*);
|
||
|
int get(char*);
|
||
|
};
|
||
|
|
||
|
// "File Chooser Window" widget:
|
||
|
class FCW : public Fl_Window {
|
||
|
public:
|
||
|
int handle(int);
|
||
|
Fl_Input input;
|
||
|
Fl_Button* ok_button;
|
||
|
Fl_Button* cancel_button;
|
||
|
Fl_Button* normal_button;
|
||
|
FCB browser;
|
||
|
FCW();
|
||
|
};
|
||
|
|
||
|
/* Files are marked as being directories by replacing the trailing null
|
||
|
with a '/' if it is a directory, a '\001' if it is *not* a directory.
|
||
|
An item has height (and is thus selectable) if it is either a directory
|
||
|
or if it matches the pattern. Quick-height assummes all unknown files
|
||
|
are directories, and thus saves the time needed to do a stat().
|
||
|
*/
|
||
|
|
||
|
// return pointer to last character:
|
||
|
static const char* end_of_name(const dirent* d) {
|
||
|
#if HAVE_DIRENT_H
|
||
|
const char* e;
|
||
|
for (e = d->d_name; ;e++) switch (*e) {
|
||
|
case 0: case 1: case '/': return e;
|
||
|
}
|
||
|
#else
|
||
|
// warning: clobbers byte after end of name
|
||
|
return d->d_name + d->d_namelen;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
// return true if item is directory, when given pointer to last character:
|
||
|
int FCB::checkdir(const dirent* d, char* e) const {
|
||
|
if (*e == 1) return 0;
|
||
|
if (*e == '/') return 1;
|
||
|
char buf[FL_PATH_MAX];
|
||
|
memcpy(buf, listed, dirend);
|
||
|
memcpy(buf+dirend, d->d_name, e-d->d_name);
|
||
|
*(buf+dirend+(e-d->d_name)) = 0;
|
||
|
if (filename_isdir(buf)) {
|
||
|
*e = '/'; return 1;
|
||
|
} else {
|
||
|
*e = 1; return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void* FCB::item_first() const {return list;}
|
||
|
|
||
|
void* FCB::item_next(void* p) const {
|
||
|
if ((dirent**)p+1 >= last) return 0;
|
||
|
return (dirent**)p+1;
|
||
|
}
|
||
|
|
||
|
void* FCB::item_prev(void* p) const {
|
||
|
if ((dirent**)p <= list) return 0;
|
||
|
return ((dirent**)p)-1;
|
||
|
}
|
||
|
|
||
|
static int ido_matching(const dirent* p, const char* e, const char* n) {
|
||
|
// replace / or 1 at end with 0 and do match, then put back. yukko
|
||
|
int save = *e; *(char*)e = 0;
|
||
|
int r = filename_match(p->d_name, n);
|
||
|
*(char*)e = save;
|
||
|
return(r);
|
||
|
}
|
||
|
|
||
|
int FCB::incr_height() const {return textsize()+2;}
|
||
|
|
||
|
int FCB::item_height(const dirent* p, int slow) const {
|
||
|
const char* e = end_of_name(p);
|
||
|
if (listed[dirend]) {
|
||
|
// if (p->d_name[0]=='.' && listed[dirend]!='.') return 0;
|
||
|
if (listed[nameend-1]=='/') {
|
||
|
if (slow ? !checkdir(p, (char*)e) : *e==1) return 0;
|
||
|
((char*)listed)[nameend-1] = 0;
|
||
|
int r = ido_matching(p, e, listed+dirend);
|
||
|
((char*)listed)[nameend-1] = '/';
|
||
|
if (!r) return 0;
|
||
|
} else {
|
||
|
if (!ido_matching(p, e, listed+dirend)) return 0;
|
||
|
}
|
||
|
} else {
|
||
|
if (p->d_name[0]=='.') return 0;
|
||
|
if (pattern && (slow ? !checkdir(p, (char*)e) : *e==1) &&
|
||
|
!ido_matching(p, e, pattern)) return 0;
|
||
|
}
|
||
|
return textsize()+2;
|
||
|
}
|
||
|
|
||
|
int FCB::item_height(void* x) const {
|
||
|
return item_height(*(const dirent**)x, 1);
|
||
|
}
|
||
|
|
||
|
int FCB::item_quick_height(void* x) const {
|
||
|
return item_height(*(const dirent**)x, 0);
|
||
|
}
|
||
|
|
||
|
void FCB::item_draw(void* v, int x, int y, int, int h) const {
|
||
|
const dirent* p = *(const dirent**)v;
|
||
|
const char* e = end_of_name(p);
|
||
|
if (checkdir(p, (char*)e)) e++;
|
||
|
if (v == selection()) fl_color(contrast(textcolor(), selection_color()));
|
||
|
else fl_color(textcolor());
|
||
|
fl_font(textfont(), textsize(), default_font(), default_size());
|
||
|
fl_draw(p->d_name, e-p->d_name, x+4, y+h-3);
|
||
|
}
|
||
|
|
||
|
int FCB::item_width(const dirent* p) const {
|
||
|
const char* e = end_of_name(p); if (*e == '/') e++;
|
||
|
fl_font(textfont(), textsize(), default_font(), default_size());
|
||
|
return (int)fl_width(p->d_name, e-p->d_name)+4;
|
||
|
}
|
||
|
|
||
|
int FCB::item_width(void* x) const {
|
||
|
return item_width(*(const dirent**)x);
|
||
|
}
|
||
|
|
||
|
// "get" the current value by copying the name of the selected file
|
||
|
// or if none are selected, by copying as many common letters as
|
||
|
// possible of the matched file list:
|
||
|
int FCB::get(char* buf) {
|
||
|
dirent** q = (dirent**)selection(); // the file to copy from
|
||
|
int n = 0; // number of letters
|
||
|
if (q) { // a file is selected
|
||
|
const char* e = end_of_name(*q);
|
||
|
n = e - (*q)->d_name;
|
||
|
if (*e == '/') n++;
|
||
|
} else { // do filename completion
|
||
|
for (q = list; q < last && !item_height(*q, 0); q++);
|
||
|
if (q < last) {
|
||
|
const char* e = end_of_name(*q);
|
||
|
n = e - (*q)->d_name;
|
||
|
if (*e == '/') n++;
|
||
|
for (dirent** r = q+1; n && r < last; r++) {
|
||
|
if (!item_height(*r, 0)) continue;
|
||
|
int i;
|
||
|
for (i=0; i<n && (*q)->d_name[i]==(*r)->d_name[i]; i++);
|
||
|
n = i;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (n) {
|
||
|
memcpy(buf, listed, dirend);
|
||
|
memcpy(buf+dirend, (*q)->d_name, n);
|
||
|
buf[dirend+n]=0;
|
||
|
}
|
||
|
return n;
|
||
|
}
|
||
|
|
||
|
// "set" the current value by changing the directory being listed and
|
||
|
// changing the highlighted item, if possible:
|
||
|
void FCB::set(const char* buf) {
|
||
|
|
||
|
int bufdirend;
|
||
|
int ispattern = 0;
|
||
|
const char* c = buf;
|
||
|
for (bufdirend=0; *c;) switch(*c++) {
|
||
|
case '?': case '[': case '*': case '{': ispattern = 1; goto BREAK;
|
||
|
#if defined(WIN32) || defined(__EMX__)
|
||
|
case '\\':
|
||
|
#endif
|
||
|
case '/': bufdirend=c-buf; break;
|
||
|
}
|
||
|
#if defined(WIN32) || defined(__EMX__)
|
||
|
if ((!bufdirend) && isalpha(buf[0]) && (buf[1]==':')) bufdirend = 2;
|
||
|
#endif
|
||
|
BREAK:
|
||
|
int bufend = strlen(buf);
|
||
|
if (bufend<=bufdirend) ispattern = 1;
|
||
|
|
||
|
// if directory is different, change list to xxx/ :
|
||
|
if (bufdirend != dirend || strncmp(buf, listed, bufdirend)) {
|
||
|
if (prev &&
|
||
|
preved[bufdirend]==0 && !strncmp(buf, preved, bufdirend)) {
|
||
|
strcpy(preved, listed); preved[dirend] = 0;
|
||
|
dirent** t;
|
||
|
t = prev; prev = list; list = t;
|
||
|
t = prev_last; prev_last = last; last = t;
|
||
|
strcpy(listed, buf);
|
||
|
dirend = nameend = bufdirend;
|
||
|
message = 0;
|
||
|
} else {
|
||
|
if (list) {
|
||
|
clear_prev();
|
||
|
strcpy(preved, listed); preved[dirend]=0;
|
||
|
prev = list;
|
||
|
prev_last = last;
|
||
|
}
|
||
|
list = last = 0;
|
||
|
message = "reading..."; redraw(); Fl::flush();
|
||
|
strcpy(listed, buf);
|
||
|
dirend = nameend = bufdirend;
|
||
|
listed[dirend] = listed[dirend+1] = 0;
|
||
|
int n = filename_list(dirend ? listed : ".", &list);
|
||
|
if (n < 0) {
|
||
|
if (errno==ENOENT) message = "No such directory";
|
||
|
else message = strerror(errno);
|
||
|
n = 0; list = 0;
|
||
|
} else message = 0;
|
||
|
last = list+n;
|
||
|
}
|
||
|
if (list && last <= list+2) message = "Empty directory";
|
||
|
new_list();
|
||
|
}
|
||
|
|
||
|
dirent** q = 0; // will point to future selection
|
||
|
int any = 0; // true if any names shown
|
||
|
|
||
|
// do we match one item in the previous list?
|
||
|
if (!ispattern && bufend >= nameend) {
|
||
|
for (q = list; ; q++) {
|
||
|
if (q >= last) {q = 0; break;}
|
||
|
if (item_height(*q, 0)==0) continue;
|
||
|
any = 1;
|
||
|
const char* a = (*q)->d_name;
|
||
|
const char* b = buf+bufdirend;
|
||
|
while (*b && *a==*b) {a++; b++;}
|
||
|
if (!*b && (*a==0 || /* *a=='/' ||*/ *a==1)) break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// no, change the list pattern to the new text + a star:
|
||
|
if (!q) {
|
||
|
strcpy(listed+dirend, buf+bufdirend);
|
||
|
nameend = bufend;
|
||
|
if (!ispattern) {listed[nameend]='*'; listed[nameend+1]=0;}
|
||
|
any = 0;
|
||
|
// search again for an exact match:
|
||
|
for (q = list; ; q++) {
|
||
|
if (q >= last) {q = 0; break;}
|
||
|
if (item_height(*q, 0)==0) continue;
|
||
|
any = 1;
|
||
|
const char* a = (*q)->d_name;
|
||
|
const char* b = buf+bufdirend;
|
||
|
while (*b && *a==*b) {a++; b++;}
|
||
|
if (!*b && (*a==0 || /* *a=='/' ||*/ *a==1)) break;
|
||
|
}
|
||
|
new_list();
|
||
|
}
|
||
|
|
||
|
if (any) message = 0;
|
||
|
else if (!message) message = "No matching files";
|
||
|
select_only(q);
|
||
|
if (q) current_callback(buf);
|
||
|
}
|
||
|
|
||
|
void FCB::draw() {
|
||
|
if (message) {
|
||
|
Fl_Boxtype b = box(); if (!b) b = Fl_Input_::default_box();
|
||
|
draw_box(b,color());
|
||
|
fl_color(FL_INACTIVE_COLOR);
|
||
|
fl_draw(message, x()+7, y()+3, w(), h()-3, FL_ALIGN_TOP_LEFT);
|
||
|
} else {
|
||
|
Fl_Browser_::draw();
|
||
|
if (full_height()<=0) {
|
||
|
message = "No matching files";
|
||
|
draw();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FCB::clear_prev() {
|
||
|
if (prev) {
|
||
|
for (dirent**p=prev_last-1; p>=prev; p--) free((void*)*p);
|
||
|
free((void*)prev);
|
||
|
prev = prev_last = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FCB::clear() {
|
||
|
if (list) {
|
||
|
for (dirent**p=last-1; p>=list; p--) free((void*)*p);
|
||
|
free((void*)list);
|
||
|
list = last = 0;
|
||
|
}
|
||
|
clear_prev();
|
||
|
listed[0] = 0; dirend = 1;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////
|
||
|
|
||
|
static void fcb_cb(Fl_Widget*, void* v) {
|
||
|
FCW* w = (FCW*)v;
|
||
|
char buf[FL_PATH_MAX];
|
||
|
if (w->browser.get(buf)) {
|
||
|
w->input.value(buf);
|
||
|
w->input.position(10000);
|
||
|
// w->input.position(10000, w->browser.dirend);
|
||
|
if (Fl::event_button()==1) {
|
||
|
if (Fl::event_clicks()) w->ok_button->do_callback();
|
||
|
else w->browser.set(buf);
|
||
|
} else {
|
||
|
current_callback(buf);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void tab_cb(Fl_Widget*, void* v) {
|
||
|
FCW* w = (FCW*)v;
|
||
|
char buf[FL_PATH_MAX];
|
||
|
if (w->browser.get(buf)) {
|
||
|
w->input.value(buf);
|
||
|
w->input.position(10000);
|
||
|
w->browser.set(buf);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if defined(WIN32) || defined(__EMX__)
|
||
|
// ':' needs very special handling!
|
||
|
static inline int isdirsep(char c) {return c=='/' || c=='\\';}
|
||
|
#else
|
||
|
#define isdirsep(c) ((c)=='/')
|
||
|
#endif
|
||
|
|
||
|
static void input_cb(Fl_Widget*, void* v) {
|
||
|
FCW* w = (FCW*)v;
|
||
|
const char* buf = w->input.value();
|
||
|
char localbuf[FL_PATH_MAX];
|
||
|
if (buf[0] && isdirsep(buf[w->input.size()-1])
|
||
|
&& filename_expand(localbuf, buf)) {
|
||
|
buf = localbuf;
|
||
|
w->input.value(localbuf);
|
||
|
w->input.position(10000);
|
||
|
}
|
||
|
w->browser.set(buf);
|
||
|
}
|
||
|
|
||
|
static void up_cb(Fl_Widget*, void* v) { // the .. button
|
||
|
FCW* w = (FCW*)v;
|
||
|
char* p;
|
||
|
char* newname;
|
||
|
char buf[FL_PATH_MAX];
|
||
|
p = w->browser.listed+w->browser.dirend-1; // point right before last '/'
|
||
|
if (p < w->browser.listed)
|
||
|
newname = "../"; // go up from current directory
|
||
|
else {
|
||
|
for (; p>w->browser.listed; p--) if (isdirsep(*(p-1))) break;
|
||
|
if (isdirsep(*p) || *p=='.' &&
|
||
|
(isdirsep(p[1]) || p[1]=='.' && isdirsep(p[2]))) {
|
||
|
p = w->browser.listed+w->browser.dirend;
|
||
|
memcpy(buf, w->browser.listed, p-w->browser.listed);
|
||
|
strcpy(buf+(p-w->browser.listed), "../");
|
||
|
} else {
|
||
|
memcpy(buf, w->browser.listed, p-w->browser.listed);
|
||
|
buf[p-w->browser.listed] = 0;
|
||
|
}
|
||
|
newname = buf;
|
||
|
}
|
||
|
w->input.value(newname);
|
||
|
w->input.position(10000);
|
||
|
w->browser.set(newname);
|
||
|
}
|
||
|
|
||
|
static void dir_cb(Fl_Widget* obj, void* v) { // directory buttons
|
||
|
FCW* w = (FCW*)v;
|
||
|
const char* p = obj->label(); if (*p=='&') p++;
|
||
|
char buf[FL_PATH_MAX];
|
||
|
char* q; for (q=buf; *p && *p!=' '; *q++ = *p++); *q = 0;
|
||
|
filename_expand(buf, buf);
|
||
|
w->input.value(buf);
|
||
|
w->input.position(10000);
|
||
|
w->browser.set(buf);
|
||
|
}
|
||
|
|
||
|
static void working_cb(Fl_Widget*, void* v) { // directory buttons
|
||
|
FCW*w = (FCW*)v;
|
||
|
char buf[FL_PATH_MAX];
|
||
|
filename_absolute(buf, "");
|
||
|
w->input.value(buf);
|
||
|
w->input.position(10000);
|
||
|
w->browser.set(buf);
|
||
|
}
|
||
|
|
||
|
static void files_cb(Fl_Widget* obj, void* v) { // file pattern buttons
|
||
|
FCW* w = (FCW*)v;
|
||
|
char buf[FL_PATH_MAX];
|
||
|
strcpy(buf, w->input.value());
|
||
|
char* q = buf+w->browser.dirend;
|
||
|
if (obj != w->normal_button) { // tack on first word of label
|
||
|
const char* p = obj->label(); if (*p=='&') p++;
|
||
|
for (; *p && *p!=' '; *q++ = *p++);
|
||
|
}
|
||
|
*q = 0;
|
||
|
w->input.value(buf);
|
||
|
w->input.position(10000, w->browser.dirend);
|
||
|
w->browser.set(buf);
|
||
|
}
|
||
|
|
||
|
/*----------------------- The Main Routine ----------------------*/
|
||
|
#define HEIGHT_BOX (4*WIDTH_SPC+HEIGHT_BUT+HEIGHT_INPUT+HEIGHT_BROWSER)
|
||
|
#define HEIGHT_BUT 25
|
||
|
#define HEIGHT_INPUT 30
|
||
|
#define HEIGHT_BROWSER (9*HEIGHT_BUT+2) // must be > buttons*HEIGHT_BUT
|
||
|
#define WIDTH_BOX (3*WIDTH_SPC+WIDTH_BUT+WIDTH_BROWSER)
|
||
|
#define WIDTH_BROWSER 350
|
||
|
#define WIDTH_BUT 125
|
||
|
#define WIDTH_OK 70
|
||
|
#define WIDTH_SPC 5
|
||
|
|
||
|
int FCW::handle(int event) {
|
||
|
if (Fl_Window::handle(event)) return 1;
|
||
|
if (event==FL_KEYBOARD && Fl::event_key()==FL_Tab) {
|
||
|
tab_cb(this, this);
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// set this to make extra directory-jumping button:
|
||
|
const char* fl_file_chooser_button;
|
||
|
extern const char* fl_ok;
|
||
|
extern const char* fl_cancel;
|
||
|
|
||
|
FCW::FCW() : Fl_Window(WIDTH_BOX, HEIGHT_BOX),
|
||
|
input(WIDTH_SPC, HEIGHT_BOX-HEIGHT_BUT-2*WIDTH_SPC-HEIGHT_INPUT,
|
||
|
WIDTH_BOX-2*WIDTH_SPC, HEIGHT_INPUT, 0),
|
||
|
browser(2*WIDTH_SPC+WIDTH_BUT, WIDTH_SPC,
|
||
|
WIDTH_BROWSER, HEIGHT_BROWSER)
|
||
|
{
|
||
|
int but_y = WIDTH_SPC;
|
||
|
input.callback(input_cb, this);
|
||
|
input.when(FL_WHEN_CHANGED);
|
||
|
// add(browser);
|
||
|
browser.callback(fcb_cb, this);
|
||
|
|
||
|
begin();
|
||
|
Fl_Widget* obj;
|
||
|
obj = ok_button = new Fl_Return_Button(
|
||
|
WIDTH_BOX-2*(WIDTH_SPC+WIDTH_OK), HEIGHT_BOX-WIDTH_SPC-HEIGHT_BUT,
|
||
|
WIDTH_OK, HEIGHT_BUT, fl_ok);
|
||
|
obj = cancel_button = new Fl_Button(
|
||
|
WIDTH_BOX-WIDTH_SPC-WIDTH_OK, HEIGHT_BOX-WIDTH_SPC-HEIGHT_BUT,
|
||
|
WIDTH_OK, HEIGHT_BUT, fl_cancel);
|
||
|
cancel_button->shortcut("^[");
|
||
|
|
||
|
obj=new Fl_Button(WIDTH_SPC,but_y,WIDTH_BUT,HEIGHT_BUT, "&Up one directory");
|
||
|
obj->callback(up_cb, this);
|
||
|
but_y += HEIGHT_BUT;
|
||
|
|
||
|
obj = new Fl_Button(WIDTH_SPC, but_y, WIDTH_BUT, HEIGHT_BUT, "&~/ Home");
|
||
|
obj->callback(dir_cb, this);
|
||
|
but_y += HEIGHT_BUT;
|
||
|
|
||
|
obj = new Fl_Button(WIDTH_SPC, but_y, WIDTH_BUT, HEIGHT_BUT, "&/ Root");
|
||
|
obj->callback(dir_cb, this);
|
||
|
but_y += HEIGHT_BUT;
|
||
|
|
||
|
obj=new Fl_Button(WIDTH_SPC, but_y, WIDTH_BUT, HEIGHT_BUT, "&Current dir");
|
||
|
obj->callback(working_cb, this);
|
||
|
but_y += HEIGHT_BUT;
|
||
|
|
||
|
if (fl_file_chooser_button) {
|
||
|
obj=new Fl_Button(WIDTH_SPC,but_y,WIDTH_BUT,HEIGHT_BUT,fl_file_chooser_button);
|
||
|
obj->callback(dir_cb, this);
|
||
|
but_y += HEIGHT_BUT;
|
||
|
}
|
||
|
|
||
|
normal_button = new Fl_Button(WIDTH_SPC, but_y, WIDTH_BUT, HEIGHT_BUT, "");
|
||
|
normal_button->callback(files_cb, this);
|
||
|
but_y += HEIGHT_BUT;
|
||
|
|
||
|
obj = new Fl_Button(WIDTH_SPC,but_y, WIDTH_BUT, HEIGHT_BUT, "* &All files");
|
||
|
obj->callback(files_cb, this);
|
||
|
but_y += HEIGHT_BUT;
|
||
|
|
||
|
obj = new Fl_Button(WIDTH_SPC,but_y,WIDTH_BUT,HEIGHT_BUT, ". &Hidden files");
|
||
|
obj->callback(files_cb, this);
|
||
|
but_y += HEIGHT_BUT;
|
||
|
|
||
|
obj = new Fl_Button(WIDTH_SPC,but_y,WIDTH_BUT,HEIGHT_BUT, "*/ &Directories");
|
||
|
obj->callback(files_cb, this);
|
||
|
but_y += HEIGHT_BUT;
|
||
|
|
||
|
resizable(new Fl_Box(browser.x(), but_y,
|
||
|
cancel_button->x()-browser.x(),
|
||
|
browser.y()+browser.h()-but_y));
|
||
|
// add(input); // put last for better draw() speed
|
||
|
end();
|
||
|
set_modal();
|
||
|
}
|
||
|
|
||
|
char* fl_file_chooser(const char* message, const char* pat, const char* fname)
|
||
|
{
|
||
|
static FCW* f; if (!f) f = new FCW();
|
||
|
f->ok_button->label(fl_ok);
|
||
|
f->cancel_button->label(fl_cancel);
|
||
|
|
||
|
if (pat && !*pat) pat = 0;
|
||
|
if (fname && *fname) {
|
||
|
f->input.value(fname);
|
||
|
} else if (f->browser.pattern != pat && (!pat || !f->browser.pattern ||
|
||
|
strcmp(pat,f->browser.pattern))) {
|
||
|
// if pattern is different, remove name but leave old directory:
|
||
|
const char* p = f->input.value();
|
||
|
const char* q = filename_name(p);
|
||
|
f->input.value(p, q-p);
|
||
|
}
|
||
|
f->browser.pattern = pat;
|
||
|
f->normal_button->label(pat ? pat : "visible files");
|
||
|
f->browser.set(f->input.value());
|
||
|
f->input.position(10000, f->browser.dirend);
|
||
|
|
||
|
f->label(message);
|
||
|
f->hotspot(f);
|
||
|
f->show();
|
||
|
int ok = 0;
|
||
|
for (;;) {
|
||
|
Fl::wait();
|
||
|
Fl_Widget* o = Fl::readqueue();
|
||
|
if (o == f->ok_button) {ok = 1; break;}
|
||
|
else if (o == f->cancel_button || o == f) break;
|
||
|
}
|
||
|
f->hide();
|
||
|
f->browser.clear();
|
||
|
|
||
|
if (!ok) return 0;
|
||
|
const char* r = f->input.value();
|
||
|
const char* p;
|
||
|
for (p=r+f->browser.dirend; *p; p++)
|
||
|
if (*p=='*' || *p=='?' || *p=='[' || *p=='{') return 0;
|
||
|
return (char*)r;
|
||
|
}
|
||
|
|
||
|
// end of fl_file_chooser.C
|