1 /* Copyright (C) 2000-2012 by George Williams */
2 /*
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions are met:
5 
6  * Redistributions of source code must retain the above copyright notice, this
7  * list of conditions and the following disclaimer.
8 
9  * Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation
11  * and/or other materials provided with the distribution.
12 
13  * The name of the author may not be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15 
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <assert.h>
29 #include <fontforge-config.h>
30 
31 #include "gdraw.h"
32 #include "gdrawP.h"
33 #include "gfile.h"
34 #include "ggadgetP.h"
35 #include "gicons.h"
36 #include "gio.h"
37 #include "gwidgetP.h"
38 #include "ustring.h"
39 #include "utype.h"
40 #ifdef WIN32
41 #include <windows.h>
42 
43 // Returns "CDQ" if C:\, D:\ and Q:\ are available.
44 // This could probably be put in fsys.c, but we don't need it anywhere else,
45 // and shouldn't ever.
46 static unsigned int WIN32_DRIVES_MAXLEN = 26;
_DriveLetters()47 static char* _DriveLetters() {
48     int idx = -1;
49     char* ret = calloc(WIN32_DRIVES_MAXLEN+1,sizeof(char));
50     DWORD drives = GetLogicalDrives();
51     assert(drives > 0);
52     for (int d = 0; d < WIN32_DRIVES_MAXLEN; d++) {
53         if ( (drives&(1 << d)) ) {
54             ret[++idx] = 'A'+d;
55         }
56     }
57     return ret;
58 }
59 #endif
60 
61 /* This isn't really a gadget, it's just a collection of gadgets with some glue*/
62 /*  to make them work together. Therefore there are no expose routines here, */
63 /*  nor mouse, nor key. That's all handled by the individual gadgets themselves*/
64 /* Instead we've got a bunch of routines that make them work as a whole */
65 static GBox gfilechooser_box = GBOX_EMPTY; /* no box */
66 static unichar_t *lastdir;
67 static int showhidden = false;
68 static enum { dirs_mixed, dirs_first, dirs_separate } dir_placement = dirs_mixed;
69 static unichar_t **bookmarks = NULL;
70 static void *prefs_changed_data = NULL;
71 static void (*prefs_changed)(void *) = NULL;
72 
SubMatch(unichar_t * pattern,unichar_t * eop,unichar_t * name,int ignorecase)73 static unichar_t *SubMatch(unichar_t *pattern, unichar_t *eop, unichar_t *name,int ignorecase) {
74     unichar_t ch, *ppt, *npt, *ept, *eon;
75 
76     while ( pattern<eop && ( ch = *pattern)!='\0' ) {
77 	if ( ch=='*' ) {
78 	    if ( pattern[1]=='\0' )
79 return( name+u_strlen(name));
80 	    for ( npt=name; ; ++npt ) {
81 		if ( (eon = SubMatch(pattern+1,eop,npt,ignorecase))!= NULL )
82 return( eon );
83 		if ( *npt=='\0' )
84 return( NULL );
85 	    }
86 	} else if ( ch=='?' ) {
87 	    if ( *name=='\0' )
88 return( NULL );
89 	    ++name;
90 	} else if ( ch=='[' ) {
91 	    /* [<char>...] matches the chars
92 	     * [<char>-<char>...] matches any char within the range (inclusive)
93 	     * the above may be concattenated and the resultant pattern matches
94 	     *		anything thing which matches any of them.
95 	     * [^<char>...] matches any char which does not match the rest of
96 	     *		the pattern
97 	     * []...] as a special case a ']' immediately after the '[' matches
98 	     *		itself and does not end the pattern
99 	     */
100 	    int found = 0, not=0;
101 	    ++pattern;
102 	    if ( pattern[0]=='^' ) { not = 1; ++pattern; }
103 	    for ( ppt = pattern; (ppt!=pattern || *ppt!=']') && *ppt!='\0' ; ++ppt ) {
104 		ch = *ppt;
105 		if ( ppt[1]=='-' && ppt[2]!=']' && ppt[2]!='\0' ) {
106 		    unichar_t ch2 = ppt[2];
107 		    if ( (*name>=ch && *name<=ch2) ||
108 			    (ignorecase && islower(ch) && islower(ch2) &&
109 				    *name>=toupper(ch) && *name<=toupper(ch2)) ||
110 			    (ignorecase && isupper(ch) && isupper(ch2) &&
111 				    *name>=tolower(ch) && *name<=tolower(ch2))) {
112 			if ( !not ) {
113 			    found = 1;
114 	    break;
115 			}
116 		    } else {
117 			if ( not ) {
118 			    found = 1;
119 	    break;
120 			}
121 		    }
122 		    ppt += 2;
123 		} else if ( ch==*name || (ignorecase && tolower(ch)==tolower(*name)) ) {
124 		    if ( !not ) {
125 			found = 1;
126 	    break;
127 		    }
128 		} else {
129 		    if ( not ) {
130 			found = 1;
131 	    break;
132 		    }
133 		}
134 	    }
135 	    if ( !found )
136 return( NULL );
137 	    while ( *ppt!=']' && *ppt!='\0' ) ++ppt;
138 	    pattern = ppt;
139 	    ++name;
140 	} else if ( ch=='{' ) {
141 	    /* matches any of a comma separated list of substrings */
142 	    for ( ppt = pattern+1; *ppt!='\0' ; ppt = ept ) {
143 		for ( ept=ppt; *ept!='}' && *ept!=',' && *ept!='\0'; ++ept );
144 		npt = SubMatch(ppt,ept,name,ignorecase);
145 		if ( npt!=NULL ) {
146 		    unichar_t *ecurly = ept;
147 		    while ( *ecurly!='}' && ecurly<eop && *ecurly!='\0' ) ++ecurly;
148 		    if ( (eon=SubMatch(ecurly+1,eop,npt,ignorecase))!=NULL )
149 return( eon );
150 		}
151 		if ( *ept=='}' )
152 return( NULL );
153 		if ( *ept==',' ) ++ept;
154 	    }
155 	} else if ( ch==*name ) {
156 	    ++name;
157 	} else if ( ignorecase && tolower(ch)==tolower(*name)) {
158 	    ++name;
159 	} else
160 return( NULL );
161 	++pattern;
162     }
163     if (pattern > eop) {
164         return NULL;
165     }
166 return( name );
167 }
168 
169 /* Handles *?{}[] wildcards */
GGadgetWildMatch(unichar_t * pattern,unichar_t * name,int ignorecase)170 int GGadgetWildMatch(unichar_t *pattern, unichar_t *name,int ignorecase) {
171     if ( pattern==NULL )
172 return( true );
173 
174     unichar_t *eop = pattern + u_strlen(pattern);
175 
176     name = SubMatch(pattern,eop,name,ignorecase);
177     if ( name==NULL )
178 return( false );
179     if ( *name=='\0' )
180 return( true );
181 
182 return( false );
183 }
184 
GFileChooserDefFilter(GGadget * g,GDirEntry * ent,const unichar_t * dir)185 enum fchooserret GFileChooserDefFilter(GGadget *g,GDirEntry *ent,const unichar_t *dir) {
186     GFileChooser *gfc = (GFileChooser *) g;
187     int i;
188     char *mime;
189 
190     if ( uc_strcmp(ent->name,".")==0 )	/* Don't show the current directory entry */
191 	return( fc_hide );
192     if ( gfc->wildcard!=NULL && *gfc->wildcard=='.' )
193 	/* If they asked for hidden files, show them */;
194     else if ( !showhidden && ent->name[0]=='.' && uc_strcmp(ent->name,"..")!=0 )
195 	return( fc_hide );
196     if ( ent->isdir )			/* Show all other directories */
197 	return( fc_show );
198     if ( gfc->wildcard==NULL && gfc->mimetypes==NULL )
199 	return( fc_show );
200     /* If we've got a wildcard, and it matches, then show */
201     if ( gfc->wildcard!=NULL && GGadgetWildMatch(gfc->wildcard,ent->name,true))
202 	return( fc_show );
203     /* If no mimetypes then we're done */
204     if ( gfc->mimetypes==NULL )
205 	return( fc_hide );
206     /* match the mimetypes */
207     if( ent->mimetype )
208 	mime = copy(u_to_c(ent->mimetype));
209     else {
210 	char utf8_ent_name[PATH_MAX+1];
211 	strncpy(utf8_ent_name,u_to_c( ent->name ),PATH_MAX);
212 	utf8_ent_name[PATH_MAX]=0;
213 	mime = GIOGetMimeType(utf8_ent_name);
214     }
215 
216     if ( mime ) {
217 	for ( i=0; gfc->mimetypes[i]!=NULL; ++i )
218 	    if ( strcasecmp(u_to_c(gfc->mimetypes[i]),mime)==0 ) {
219 		free(mime);
220 		return( fc_show );
221 	    }
222 	free(mime);
223     }
224 
225     return( fc_hide );
226 }
227 
GFileChooserPickIcon(GDirEntry * e)228 static GImage *GFileChooserPickIcon(GDirEntry *e) {
229     char mime[100];
230     char utf8_ent_name[PATH_MAX+1];
231     mime[0] = mime[99] = utf8_ent_name[PATH_MAX] = 0;
232     strncpy(utf8_ent_name,u_to_c(e->name),PATH_MAX);
233 
234     InitChooserIcons();
235 
236     if ( e->isdir ) {
237 	if ( !strcmp(utf8_ent_name,"..") )
238 	    return( &_GIcon_updir );
239 	return( &_GIcon_dir );
240     }
241     if ( e->mimetype ) {
242 	strncpy(mime,u_to_c(e->mimetype),99);
243     } else {
244 	char *temp;
245 	if ( (temp=GIOguessMimeType(utf8_ent_name)) || (temp=GIOGetMimeType(utf8_ent_name)) ) {
246 	    e->mimetype=u_copy(c_to_u(temp));
247 	    strncpy(mime,temp,99);
248 	    free(temp);
249 	} else
250 	    return( &_GIcon_unknown );
251     }
252     if (strncasecmp("text/", mime, 5) == 0) {
253 	if (strcasecmp("text/html", mime) == 0)
254 	    return( &_GIcon_texthtml );
255 	if (strcasecmp("text/xml", mime) == 0)
256 	    return( &_GIcon_textxml );
257 	if (strcasecmp("text/css", mime) == 0)
258 	    return( &_GIcon_textcss );
259 	if (strcasecmp("text/c", mime) == 0)
260 	    return( &_GIcon_textc );
261 	if (strcasecmp("text/java", mime) == 0)
262 	    return( &_GIcon_textjava );
263 	if (strcasecmp("text/x-makefile", mime) == 0)
264 	    return( &_GIcon_textmake );
265 	if (strcasecmp("text/fontps", mime) == 0)
266 	    return( &_GIcon_textfontps );
267 	if (strcasecmp("text/font", mime) == 0)
268 	    return( &_GIcon_textfontbdf );
269 	if (strcasecmp("text/ps", mime) == 0)
270 	    return( &_GIcon_textps );
271 
272 	return( &_GIcon_textplain );
273     }
274 
275     if (strncasecmp("image/", mime, 6) == 0)
276 	return( &_GIcon_image );
277     if (strncasecmp("video/", mime, 6) == 0)
278 	return( &_GIcon_video );
279     if (strncasecmp("audio/", mime, 6) == 0)
280 	return( &_GIcon_audio );
281     if (strcasecmp("application/x-navidir", mime) == 0 ||
282 	strcasecmp("inode/directory", mime) == 0)
283 	return( &_GIcon_dir );
284     if (strcasecmp("application/x-object", mime) == 0)
285 	return( &_GIcon_object );
286     if (strcasecmp("application/x-core", mime) == 0)
287 	return( &_GIcon_core );
288     if (strcasecmp("application/x-tar", mime) == 0)
289 	return( &_GIcon_tar );
290     if (strcasecmp("application/x-compressed", mime) == 0)
291 	return( &_GIcon_compressed );
292     if (strcasecmp("application/pdf", mime) == 0)
293 	return( &_GIcon_texthtml );
294     if (strcasecmp("application/vnd.font-fontforge-sfd", mime) == 0)
295 	return( &_GIcon_textfontsfd );
296     if (strcasecmp("application/x-font-type1", mime) == 0)
297 	return( &_GIcon_textfontps );
298     if (strcasecmp("application/x-font-ttf", mime) == 0 ||
299 	strcasecmp("application/x-font-otf", mime) == 0 ||
300 	strcasecmp("font/woff", mime) == 0 ||
301 	strcasecmp("font/woff2", mime) == 0 ||
302 	strcasecmp("font/ttf", mime) == 0 ||
303 	strcasecmp("font/otf", mime) == 0) {
304 	return( &_GIcon_ttf );
305     }
306     if (strcasecmp("application/x-font-cid", mime) == 0 )
307 	return( &_GIcon_cid );
308     if (strcasecmp("application/x-macbinary", mime) == 0 ||
309 	strcasecmp("application/x-mac-binhex40", mime) == 0 ) {
310 	return( &_GIcon_mac );
311     }
312     if (strcasecmp("application/x-mac-dfont", mime) == 0 ||
313 	strcasecmp("application/x-mac-suit", mime) == 0 ) {
314 	return( &_GIcon_macttf );
315     }
316     if (strcasecmp("application/x-font-pcf", mime) == 0 ||
317 	strcasecmp("application/x-font-snf", mime) == 0) {
318 	return( &_GIcon_textfontbdf );
319     }
320 
321     return( &_GIcon_unknown );
322 }
323 
GFileChooserFillList(GFileChooser * gfc,GDirEntry * first,const unichar_t * dir)324 static void GFileChooserFillList(GFileChooser *gfc,GDirEntry *first,
325 	const unichar_t *dir) {
326     GDirEntry *e;
327     int len, dlen;
328     GTextInfo **ti, **dti;
329 
330     len = dlen = 0;
331     for ( e=first; e!=NULL; e=e->next ) {
332 	e->fcdata = (gfc->filter)(&gfc->g,e,dir);
333 	if ( e->fcdata!=fc_hide ) {
334 	    if ( e->isdir )
335 		++dlen;
336 	    else
337 		++len;
338 	}
339     }
340 
341     if ( dir_placement == dirs_separate ) {
342 	ti = malloc((len+1)*sizeof(GTextInfo *));
343 	dti = malloc((dlen+1)*sizeof(GTextInfo *));
344 	len = dlen = 0;
345 	for ( e=first; e!=NULL; e=e->next ) {
346 	    if ( e->fcdata!=fc_hide ) {
347 		GTextInfo **me;
348 		if ( e->isdir )
349 		    me = &dti[dlen++];
350 		else
351 		    me = &ti[len++];
352 
353 		*me = calloc(1,sizeof(GTextInfo));
354 		(*me)->text = u_copy(e->name);
355 		(*me)->image = GFileChooserPickIcon(e);
356 		(*me)->fg = COLOR_DEFAULT;
357 		(*me)->bg = COLOR_DEFAULT;
358 		(*me)->font = NULL;
359 		(*me)->disabled = e->fcdata==fc_showdisabled;
360 		(*me)->image_precedes = true;
361 		(*me)->checked = e->isdir;
362 	    }
363 	}
364 	ti[len] = calloc(1,sizeof(GTextInfo));
365 	dti[dlen] = calloc(1,sizeof(GTextInfo));
366 	GGadgetSetList(&gfc->files->g,ti,false);
367 	GGadgetSetList(&gfc->subdirs->g,dti,false);
368     } else {
369 	ti = malloc((len+dlen+1)*sizeof(GTextInfo *));
370 	len = 0;
371 	for ( e=first; e!=NULL; e=e->next ) {
372 	    if ( e->fcdata!=fc_hide ) {
373 		ti[len] = calloc(1,sizeof(GTextInfo));
374 		ti[len]->text = u_copy(e->name);
375 		ti[len]->image = GFileChooserPickIcon(e);
376 		ti[len]->fg = COLOR_DEFAULT;
377 		ti[len]->bg = COLOR_DEFAULT;
378 		ti[len]->font = NULL;
379 		ti[len]->disabled = e->fcdata==fc_showdisabled;
380 		ti[len]->image_precedes = true;
381 		ti[len]->checked = e->isdir;
382 		if ( dir_placement==dirs_first && e->isdir )
383 		    ((GTextInfo2 *) ti[len])->sort_me_first_in_list = true;
384 		++len;
385 	    }
386 	}
387 	ti[len] = calloc(1,sizeof(GTextInfo));
388 	GGadgetSetList(&gfc->files->g,ti,false);
389     }
390 
391     GGadgetScrollListToText(&gfc->files->g,u_GFileNameTail(_GGadgetGetTitle(&gfc->name->g)),true);
392 }
393 
GFileChooserIntermediateDir(GIOControl * gc)394 static void GFileChooserIntermediateDir(GIOControl *gc) {
395     GFileChooser *gfc = (GFileChooser *) (gc->userdata);
396 
397     GFileChooserFillList(gfc,GIOgetDirData(gc),gc->path);
398 }
399 
GFileChooserReceiveDir(GIOControl * gc)400 static void GFileChooserReceiveDir(GIOControl *gc) {
401     GFileChooser *gfc = (GFileChooser *) (gc->userdata);
402 
403     GGadgetSetEnabled(&gfc->files->g,true);
404     GGadgetSetEnabled(&gfc->subdirs->g,true);
405     if ( gfc->lastname!=NULL ) {
406 	GGadgetSetTitle(&gfc->name->g,gfc->lastname);
407 	free(gfc->lastname);
408 	gfc->lastname=NULL;
409     }
410     GFileChooserFillList(gfc,GIOgetDirData(gc),gc->path);
411     GIOclose(gc);
412     gfc->outstanding = NULL;
413     GDrawSetCursor(gfc->g.base,gfc->old_cursor);
414 }
415 
GFileChooserErrorDir(GIOControl * gc)416 static void GFileChooserErrorDir(GIOControl *gc) {
417     GFileChooser *gfc = (GFileChooser *) (gc->userdata);
418     GTextInfo *ti[3], _ti[3];
419     static unichar_t nullstr[] = { 0 };
420 
421     memset(_ti,'\0',sizeof(_ti));
422     _ti[0].text = gc->error;
423     if ( gc->status[0]!='\0' )
424 	_ti[1].text = gc->status;
425     _ti[0].fg = _ti[0].bg = _ti[1].fg = _ti[1].bg = COLOR_DEFAULT;
426     ti[0] = _ti; ti[1] = _ti+1; ti[2] = _ti+2;
427     GGadgetSetEnabled(&gfc->files->g,false);
428     GGadgetSetList(&gfc->files->g,ti,true);
429     GGadgetSetEnabled(&gfc->subdirs->g,false);
430     GGadgetSetList(&gfc->subdirs->g,ti,true);
431     if ( gfc->lastname!=NULL ) {
432 	GGadgetSetTitle(&gfc->name->g,gfc->lastname);
433 	free(gfc->lastname);
434 	gfc->lastname=NULL;
435     } else
436 	GGadgetSetTitle(&gfc->name->g,nullstr);
437     if ( gfc->filterb!=NULL && gfc->ok!=NULL )
438 	_GWidget_MakeDefaultButton(&gfc->ok->g);
439     GIOcancel(gc);
440     gfc->outstanding = NULL;
441     GDrawSetCursor(gfc->g.base,gfc->old_cursor);
442 }
443 
GFileChooserScanDir(GFileChooser * gfc,unichar_t * dir)444 static void GFileChooserScanDir(GFileChooser *gfc,unichar_t *dir) {
445     GTextInfo **ti=NULL;
446     int cnt, tot=0;
447 #ifdef _WIN32
448     char* drives = _DriveLetters();
449     int dcnt = strlen(drives);
450 #endif
451     unichar_t *pt, *ept, *freeme;
452 
453     dir = u_GFileNormalize(dir);
454     while ( 1 ) {
455 	ept = dir;
456 	cnt = 0;
457 	if ( (pt=uc_strstr(dir,"://"))!=NULL ) {
458 	    ept = u_strchr(pt+3,'/');
459 	    if ( ept==NULL )
460 		ept = pt+u_strlen(pt);
461 	    else
462 		++ept;
463 	} else if ( *dir=='/' )
464 	    ept = dir+1;
465 	if ( ept!=dir ) {
466 	    if ( ti!=NULL ) {
467 		ti[tot-cnt] = calloc(1,sizeof(GTextInfo));
468 		ti[tot-cnt]->text = u_copyn(dir,ept-dir);
469 		ti[tot-cnt]->fg = ti[tot-cnt]->bg = COLOR_DEFAULT;
470 	    }
471 	    cnt = 1;
472 	}
473 	for ( pt = ept; *pt!='\0'; pt = ept ) {
474 	    for ( ept = pt; *ept!='/' && *ept!='\0'; ++ept );
475 	    if ( ti!=NULL ) {
476 		ti[tot-cnt] = calloc(1,sizeof(GTextInfo));
477 		ti[tot-cnt]->text = u_copyn(pt,ept-pt);
478 		ti[tot-cnt]->fg = ti[tot-cnt]->bg = COLOR_DEFAULT;
479 	    }
480 	    ++cnt;
481 	    if ( *ept=='/' ) ++ept;
482 	}
483 	if ( ti!=NULL )
484     break;
485 	ti = malloc((cnt+1)*sizeof(GTextInfo *));
486 	tot = cnt-1;
487     }
488 
489 #ifdef _WIN32
490     ti = realloc(ti, (dcnt+cnt+1)*sizeof(GTextInfo *));
491 
492     for (char* c = drives; *c != '\0'; c++) {
493         // Don't put the root twice if we're already on it.
494         if (*c == toupper(dir[0])) continue;
495 
496         ti[cnt] = calloc(1,sizeof(GTextInfo));
497         unichar_t* utemp = calloc(3,sizeof(unichar_t));
498         utemp[0] = *c; utemp[1] = ':'; //utemp[2] = '\\'; // utemp[3] = 0;
499         ti[cnt]->text = utemp;
500         ti[cnt]->fg = ti[cnt]->bg = COLOR_DEFAULT;
501         // We don't want to add a new field to GTextInfo as it's used in a lot of places.
502         // This is the next best thing AFAICT.
503         ti[cnt]->userdata = (void*)1;
504         cnt++;
505     }
506     free(drives);
507 #endif
508     ti[cnt] = calloc(1,sizeof(GTextInfo));
509 
510     GGadgetSetList(&gfc->directories->g,ti,false);
511     GGadgetSelectOneListItem(&gfc->directories->g,0);
512 
513     if ( gfc->outstanding!=NULL ) {
514 	GIOcancel(gfc->outstanding);
515 	gfc->outstanding = NULL;
516     } else {
517 	gfc->old_cursor = GDrawGetCursor(gfc->g.base);
518 	GDrawSetCursor(gfc->g.base,ct_watch);
519     }
520 
521     gfc->outstanding = GIOCreate(dir,gfc,GFileChooserReceiveDir,
522 	    GFileChooserErrorDir);
523     gfc->outstanding->receiveintermediate = GFileChooserIntermediateDir;
524     GIOdir(gfc->outstanding);
525 
526     freeme = NULL;
527     if ( dir[u_strlen(dir)-1]!='/' ) {
528 	freeme = malloc((u_strlen(dir)+3)*sizeof(unichar_t));
529 	u_strcpy(freeme,dir);
530 	uc_strcat(freeme,"/");
531 	dir = freeme;
532     }
533     if ( gfc->hpos+1>=gfc->hmax ) {
534 	gfc->hmax = gfc->hmax+20;
535 	gfc->history = realloc(gfc->history,(gfc->hmax)*sizeof(unichar_t *));
536     }
537     if ( gfc->hcnt==0 ) {
538 	gfc->history[gfc->hcnt++] = u_copy(dir);
539     } else if ( u_strcmp(gfc->history[gfc->hpos],dir)==0 )
540 	/* Just a refresh */;
541     else {
542 	gfc->history[++gfc->hpos] = u_copy(dir);
543 	gfc->hcnt = gfc->hpos+1;
544     }
545     free(freeme);
546 }
547 
548 /* Handle events from the text field */
GFileChooserTextChanged(GGadget * t,GEvent * e)549 static int GFileChooserTextChanged(GGadget *t,GEvent *e) {
550     GFileChooser *gfc;
551     GGadget *g = (GGadget *)GGadgetGetUserData(t);
552 
553     const unichar_t *pt, *spt;
554     unichar_t * pt_toFree = 0, *local_toFree = 0;
555     if ( e->type!=et_controlevent || e->u.control.subtype!=et_textchanged )
556 return( true );
557     spt = pt = _GGadgetGetTitle(t);
558 #ifdef _WIN32
559     local_toFree = u_GFileNormalizePath(u_copy(spt));
560     pt = spt = local_toFree;
561 #endif
562 
563     if ( pt==NULL )
564 return( true );
565     gfc = (GFileChooser *) GGadgetGetUserData(t);
566 
567     if( GFileChooserGetInputFilenameFunc(g)(g, &pt,gfc->inputfilenameprevchar)) {
568 	pt_toFree = (unichar_t*)pt;
569 	spt = pt;
570 	GGadgetSetTitle(g, pt);
571     }
572 
573     while ( *pt && *pt!='*' && *pt!='?' && *pt!='[' && *pt!='{' )
574 	++pt;
575     if ( *spt!='\0' && spt[u_strlen(spt)-1]=='/' )
576 	pt = spt+u_strlen(spt)-1;
577 
578     /* if there are no wildcards and no directories in the filename */
579     /*  then as it gets changed the list box should show the closest */
580     /*  approximation to it */
581     if ( *pt=='\0' ) {
582 	GGadgetScrollListToText(&gfc->files->g,spt,true);
583 	if ( gfc->filterb!=NULL && gfc->ok!=NULL )
584 	    _GWidget_MakeDefaultButton(&gfc->ok->g);
585     } else if ( *pt=='/' && e->u.control.u.tf_changed.from_pulldown!=-1 ) {
586 	GEvent e;
587 	e.type = et_controlevent;
588 	e.u.control.subtype = et_buttonactivate;
589 	e.u.control.g = (gfc->ok!=NULL?&gfc->ok->g:&gfc->g);
590 	e.u.control.u.button.clicks = 0;
591 	e.w = e.u.control.g->base;
592 	if ( e.u.control.g->handle_controlevent != NULL )
593 	    (e.u.control.g->handle_controlevent)(e.u.control.g,&e);
594 	else
595 	    GDrawPostEvent(&e);
596     } else {
597 	if ( gfc->filterb!=NULL && gfc->ok!=NULL )
598 	    _GWidget_MakeDefaultButton(&gfc->filterb->g);
599     }
600     free(gfc->lastname); gfc->lastname = NULL;
601     if(pt_toFree)
602 	free(pt_toFree);
603 
604     if (local_toFree)
605         free(local_toFree);
606 
607     if(gfc->inputfilenameprevchar)
608 	free(gfc->inputfilenameprevchar);
609     gfc->inputfilenameprevchar = u_copy(_GGadgetGetTitle(t));
610 
611 return( true );
612 }
613 
GFileChooserCompletion(GGadget * t,int from_tab)614 static unichar_t **GFileChooserCompletion(GGadget *t,int from_tab) {
615     GFileChooser *gfc;
616     const unichar_t *pt, *spt; unichar_t **ret;
617     GTextInfo **ti;
618     int32 len;
619     int i, cnt, doit, match_len;
620 
621     pt = spt = _GGadgetGetTitle(t);
622     if ( pt==NULL )
623 return( NULL );
624     while ( *pt && *pt!='/' && *pt!='*' && *pt!='?' && *pt!='[' && *pt!='{' )
625 	++pt;
626     if ( *pt!='\0' )
627 return( NULL );		/* Can't complete if not in cur directory or has wildcards */
628 
629     match_len = u_strlen(spt);
630     gfc = (GFileChooser *) GGadgetGetUserData(t);
631     ti = GGadgetGetList(&gfc->files->g,&len);
632     ret = NULL;
633     for ( doit=0; doit<2; ++doit ) {
634 	cnt=0;
635 	for ( i=0; i<len; ++i ) {
636 	    if ( u_strncmp(ti[i]->text,spt,match_len)==0 ) {
637 		if ( doit ) {
638 		    if ( ti[i]->checked /* isdirectory */ ) {
639 			int len = u_strlen(ti[i]->text);
640 			ret[cnt] = malloc((len+2)*sizeof(unichar_t));
641 			u_strcpy(ret[cnt],ti[i]->text);
642 			ret[cnt][len] = '/';
643 			ret[cnt][len+1] = '\0';
644 		    } else
645 			ret[cnt] = u_copy(ti[i]->text);
646 		}
647 		++cnt;
648 	    }
649 	}
650 	if ( doit )
651 	    ret[cnt] = NULL;
652 	else if ( cnt==0 )
653 return( NULL );
654 	else
655 	    ret = malloc((cnt+1)*sizeof(unichar_t *));
656     }
657 return( ret );
658 }
659 
GFileChooserGetCurDir(GFileChooser * gfc,int dirindex)660 static unichar_t *GFileChooserGetCurDir(GFileChooser *gfc,int dirindex) {
661     GTextInfo **ti;
662     int32 len; int j, cnt;
663     unichar_t *dir, *pt;
664 
665     ti = GGadgetGetList(&gfc->directories->g,&len);
666     if ( dirindex==-1 )
667         dirindex = 0;
668     dirindex = dirindex;
669 
670     for ( j=len-1,cnt=0; j>=dirindex; --j ) {
671         if (ti[j]->userdata != NULL)
672             continue;
673         cnt += u_strlen(ti[j]->text)+1;
674     }
675     pt = dir = malloc((cnt+1)*sizeof(unichar_t));
676     for ( j=len-1; j>=dirindex; --j ) {
677         if (ti[j]->userdata != NULL)
678             continue;
679         u_strcpy(pt,ti[j]->text);
680         pt += u_strlen(pt);
681         if ( pt[-1]!='/' )
682             *pt++ = '/';
683     }
684     *pt = '\0';
685 return( dir );
686 }
687 
GFileChooserRefresh(GFileChooser * gfc)688 static void GFileChooserRefresh(GFileChooser *gfc) {
689     unichar_t *dir;
690 
691     dir = GFileChooserGetCurDir(gfc,-1);
692     GFileChooserScanDir(gfc,dir);
693     free(dir);
694 }
695 
696 static void GFileChooserSetTitle(GGadget *g,const unichar_t *tit);
697 
698 /* Handle events from the directory dropdown list */
GFileChooserDListChanged(GGadget * pl,GEvent * e)699 static int GFileChooserDListChanged(GGadget *pl,GEvent *e) {
700     GFileChooser *gfc;
701     int i;
702     unichar_t *dir;
703 
704     if ( e->type!=et_controlevent || e->u.control.subtype!=et_listselected )
705 return( true );
706     i = GGadgetGetFirstListSelectedItem(pl);
707     if ( i==-1 )
708 return(true);
709     gfc = (GFileChooser *) GGadgetGetUserData(pl);
710 #ifdef _WIN32
711     int len;
712     GTextInfo **ti;
713     ti = GGadgetGetList(pl,&len);
714     if (ti[i]->userdata != NULL) {
715         GFileChooserSetTitle((GGadget*)gfc, ti[i]->text);
716         GFileChooserRefresh(gfc);
717 return( true );
718     }
719 #endif
720     if ( i==0 )		/* Nothing changed */
721 return( true );
722     dir = GFileChooserGetCurDir(gfc,i);
723     GFileChooserScanDir(gfc,dir);
724     free(dir);
725 return( true );
726 }
727 
728 /* Handle events from the file list list */
GFileChooserFListSelected(GGadget * gl,GEvent * e)729 static int GFileChooserFListSelected(GGadget *gl,GEvent *e) {
730     GFileChooser *gfc;
731     int i;
732     int32 listlen; int len, cnt, dirpos, apos;
733     unichar_t *dir, *newdir;
734     GTextInfo *ti, **all;
735 
736     if ( e->type!=et_controlevent ||
737 	    ( e->u.control.subtype!=et_listselected &&
738 	      e->u.control.subtype!=et_listdoubleclick ))
739 return( true );
740     if ( ((GList *) gl)->multiple_sel ) {
741 	all = GGadgetGetList(gl,&listlen);
742 	len = cnt = 0;
743 	dirpos = apos = -1;
744 	for ( i=0; i<listlen; ++i ) {
745 	    if ( !all[i]->selected )
746 		/* Not selected? ignore it */;
747 	    else if ( all[i]->checked )		/* Directory */
748 		dirpos = i;
749 	    else {
750 		len += u_strlen( all[i]->text ) + 2;
751 		++cnt;
752 		apos = i;
753 	    }
754 	}
755 	if ( dirpos!=-1 && cnt>0 ) {
756 	    /* If a directory was selected, clear everthing else */
757 	    for ( i=0; i<listlen; ++i ) if ( i!=dirpos )
758 		all[i]->selected = false;
759 	    _ggadget_redraw(gl);
760 	}
761 	if ( dirpos!=-1 ) { cnt = 1; apos = dirpos; }
762     } else {
763 	cnt = 1;
764 	apos = GGadgetGetFirstListSelectedItem(gl);
765     }
766     if ( apos==-1 )
767 return(true);
768     gfc = (GFileChooser *) GGadgetGetUserData(gl);
769     ti = GGadgetGetListItem(gl,apos);
770     if ( e->u.control.subtype==et_listselected && cnt==1 ) {
771 	/* Nope, quite doesn't work. Goal is to remember first filename. But */
772 	/*  if user types into the list box we'll (probably) get several diff*/
773 	/*  filenames before we hit the directory. So we just ignore the typ-*/
774 	/*  ing case for now. */
775 	if ( ti->checked /* it's a directory */ && e->u.control.u.list.from_mouse &&
776 		gfc->lastname==NULL )
777 	    gfc->lastname = GGadgetGetTitle(&gfc->name->g);
778 	if ( ti->checked ) {
779 	    unichar_t *val = malloc((u_strlen(ti->text)+2)*sizeof(unichar_t));
780 	    u_strcpy(val,ti->text);
781 	    uc_strcat(val,"/");
782 	    GGadgetSetTitle(&gfc->name->g,val);
783 	    free(val);
784 	    if ( gfc->filterb!=NULL && gfc->ok!=NULL )
785 		_GWidget_MakeDefaultButton(&gfc->filterb->g);
786 	} else {
787 	    GGadgetSetTitle(&gfc->name->g,ti->text);
788 	    if ( gfc->filterb!=NULL && gfc->ok!=NULL )
789 		_GWidget_MakeDefaultButton(&gfc->ok->g);
790 	    free(gfc->lastname);
791 	    gfc->lastname = NULL;
792 	}
793     } else if ( e->u.control.subtype==et_listselected ) {
794 	unichar_t *val, *upt = malloc((len+1)*sizeof(unichar_t));
795 	val = upt;
796 	for ( i=0; i<listlen; ++i ) {
797 	    if ( all[i]->selected ) {
798 		u_strcpy(upt,all[i]->text);
799 		upt += u_strlen(upt);
800 		if ( --cnt>0 ) {
801 		    uc_strcpy(upt,"; ");
802 		    upt += 2;
803 		}
804 	    }
805 	}
806 	GGadgetSetTitle(&gfc->name->g,val);
807 	free(val);
808     } else if ( ti->checked /* it's a directory */ ) {
809 	dir = GFileChooserGetCurDir(gfc,-1);
810 	newdir = u_GFileAppendFile(dir,ti->text,true);
811 	GFileChooserScanDir(gfc,newdir);
812 	free(dir); free(newdir);
813     } else {
814 	/* Post the double click (on a file) to the parent */
815 	/*  if we know what the ok button is then pretend it got pressed */
816 	/*  otherwise just send a list double click from ourselves */
817 	if ( gfc->ok!=NULL ) {
818 	    e->u.control.subtype = et_buttonactivate;
819 	    e->u.control.g = &gfc->ok->g;
820 	    e->u.control.u.button.clicks = 0;
821 	    e->w = e->u.control.g->base;
822 	} else
823 	    e->u.control.g = &gfc->g;
824 	if ( e->u.control.g->handle_controlevent != NULL )
825 	    (e->u.control.g->handle_controlevent)(e->u.control.g,e);
826 	else
827 	    GDrawPostEvent(e);
828     }
829 return( true );
830 }
831 
GFCHideToggle(GWindow gw,struct gmenuitem * mi,GEvent * e)832 static void GFCHideToggle(GWindow gw,struct gmenuitem *mi,GEvent *e) {
833     GFileChooser *gfc = (GFileChooser *) (mi->ti.userdata);
834     unichar_t *dir;
835 
836     showhidden = !showhidden;
837 
838     dir = GFileChooserGetCurDir(gfc,-1);
839     GFileChooserScanDir(gfc,dir);
840     free(dir);
841 
842     if ( prefs_changed!=NULL )
843 	(prefs_changed)(prefs_changed_data);
844 }
845 
GFCRemetric(GFileChooser * gfc)846 static void GFCRemetric(GFileChooser *gfc) {
847     GRect size;
848 
849     GGadgetGetSize(&gfc->topbox->g,&size);
850     GGadgetResize(&gfc->topbox->g,size.width,size.height);
851 }
852 
GFCDirsAmidToggle(GWindow gw,struct gmenuitem * mi,GEvent * e)853 static void GFCDirsAmidToggle(GWindow gw,struct gmenuitem *mi,GEvent *e) {
854     GFileChooser *gfc = (GFileChooser *) (mi->ti.userdata);
855     unichar_t *dir;
856 
857     if ( dir_placement==dirs_separate ) {
858 	GGadgetSetVisible(&gfc->subdirs->g,false);
859 	GFCRemetric(gfc);
860     }
861     dir_placement = dirs_mixed;
862 
863     dir = GFileChooserGetCurDir(gfc,-1);
864     GFileChooserScanDir(gfc,dir);
865     free(dir);
866 
867     if ( prefs_changed!=NULL )
868 	(prefs_changed)(prefs_changed_data);
869 }
870 
GFCDirsFirstToggle(GWindow gw,struct gmenuitem * mi,GEvent * e)871 static void GFCDirsFirstToggle(GWindow gw,struct gmenuitem *mi,GEvent *e) {
872     GFileChooser *gfc = (GFileChooser *) (mi->ti.userdata);
873     unichar_t *dir;
874 
875     if ( dir_placement==dirs_separate ) {
876 	GGadgetSetVisible(&gfc->subdirs->g,false);
877 	GFCRemetric(gfc);
878     }
879     dir_placement = dirs_first;
880 
881     dir = GFileChooserGetCurDir(gfc,-1);
882     GFileChooserScanDir(gfc,dir);
883     free(dir);
884 
885     if ( prefs_changed!=NULL )
886 	(prefs_changed)(prefs_changed_data);
887 }
888 
GFCDirsSeparateToggle(GWindow gw,struct gmenuitem * mi,GEvent * e)889 static void GFCDirsSeparateToggle(GWindow gw,struct gmenuitem *mi,GEvent *e) {
890     GFileChooser *gfc = (GFileChooser *) (mi->ti.userdata);
891     unichar_t *dir;
892 
893     if ( dir_placement!=dirs_separate ) {
894 	GGadgetSetVisible(&gfc->subdirs->g,true);
895 	GFCRemetric(gfc);
896     }
897     dir_placement = dirs_separate;
898 
899     dir = GFileChooserGetCurDir(gfc,-1);
900     GFileChooserScanDir(gfc,dir);
901     free(dir);
902 
903     if ( prefs_changed!=NULL )
904 	(prefs_changed)(prefs_changed_data);
905 }
906 
GFCRefresh(GWindow gw,struct gmenuitem * mi,GEvent * e)907 static void GFCRefresh(GWindow gw,struct gmenuitem *mi,GEvent *e) {
908     GFileChooser *gfc = (GFileChooser *) (mi->ti.userdata);
909     GFileChooserRefresh(gfc);
910 }
911 
GFileChooserHome(GGadget * g,GEvent * e)912 static int GFileChooserHome(GGadget *g, GEvent *e) {
913     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
914 	unichar_t* homedir = u_GFileGetHomeDocumentsDir();
915 	if ( homedir==NULL )
916 	    GGadgetSetEnabled(g,false);
917 	else {
918 	    GFileChooser *gfc = (GFileChooser *) GGadgetGetUserData(g);
919 	    GFileChooserScanDir(gfc,homedir);
920 	    free(homedir);
921 	}
922     }
923 return( true );
924 }
925 
GFileChooserUpDirButton(GGadget * g,GEvent * e)926 static int GFileChooserUpDirButton(GGadget *g, GEvent *e) {
927     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
928 	GFileChooser *gfc = (GFileChooser *) GGadgetGetUserData(g);
929 	unichar_t *dir, *newdir;
930 	static unichar_t dotdot[] = { '.', '.',  0 };
931 	dir = GFileChooserGetCurDir(gfc,-1);
932 	newdir = u_GFileAppendFile(dir,dotdot,true);
933 	GFileChooserScanDir(gfc,newdir);
934 	free(dir); free(newdir);
935     }
936 return( true );
937 }
938 
939 static GMenuItem gfcpopupmenu[] = {
940     { { (unichar_t *) N_("Show Hidden Files"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 1, 0, 0, 0, 1, 0, 0, 'H' }, '\0', ksm_control, NULL, NULL, GFCHideToggle, 0 },
941     { { (unichar_t *) N_("Directories Amid Files"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 1, 0, 0, 0, 1, 0, 0, 'H' }, '\0', ksm_control, NULL, NULL, GFCDirsAmidToggle, 0 },
942     { { (unichar_t *) N_("Directories First"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 1, 0, 0, 0, 1, 0, 0, 'H' }, '\0', ksm_control, NULL, NULL, GFCDirsFirstToggle, 0 },
943     { { (unichar_t *) N_("Directories Separate"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 1, 0, 0, 0, 1, 0, 0, 'H' }, '\0', ksm_control, NULL, NULL, GFCDirsSeparateToggle, 0 },
944     { { (unichar_t *) N_("Refresh File List"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, 'H' }, '\0', ksm_control, NULL, NULL, GFCRefresh, 0 },
945     GMENUITEM_EMPTY
946 };
947 static int gotten=false;
948 
GFCPopupMenu(GGadget * g,GEvent * e)949 static void GFCPopupMenu(GGadget *g, GEvent *e) {
950     int i;
951     // The reason this casting works is a bit of a hack.
952     // If this was initiated from a right click, `g` will be the GFC GGadget.
953     // Then its userdata would be the initiator's, and NOT the GFC struct.
954     // Thus we cannot call GGadgetGetUserData.
955     // However, since in the GFC struct, its GGadget comes first, effectively
956     // the pointer to its GGadget will equal the pointer to its GFC struct.
957     // I have ensured that all calls to this function pass the GFC GGadget.
958     GFileChooser *gfc = (GFileChooser *) g;
959 
960     for ( i=0; gfcpopupmenu[i].ti.text!=NULL || gfcpopupmenu[i].ti.line; ++i )
961 	gfcpopupmenu[i].ti.userdata = gfc;
962     gfcpopupmenu[0].ti.checked = showhidden;
963     gfcpopupmenu[1].ti.checked = dir_placement == dirs_mixed;
964     gfcpopupmenu[2].ti.checked = dir_placement == dirs_first;
965     gfcpopupmenu[3].ti.checked = dir_placement == dirs_separate;
966     if ( !gotten ) {
967 	gotten = true;
968 	for ( i=0; gfcpopupmenu[i].ti.text!=NULL || gfcpopupmenu[i].ti.line; ++i )
969 	    gfcpopupmenu[i].ti.text = (unichar_t *) _( (char *) gfcpopupmenu[i].ti.text);
970     }
971     GMenuCreatePopupMenu(g->base,e, gfcpopupmenu);
972 }
973 
GFileChooserConfigure(GGadget * g,GEvent * e)974 static int GFileChooserConfigure(GGadget *g, GEvent *e) {
975     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonpress ) {
976 	GEvent fake;
977 	GRect pos;
978 	GGadgetGetSize(g,&pos);
979 	memset(&fake,0,sizeof(fake));
980 	fake.type = et_mousedown;
981 	fake.w = g->base;
982 	fake.u.mouse.x = pos.x;
983 	fake.u.mouse.y = pos.y+pos.height;
984 	GFCPopupMenu((GGadget*)GGadgetGetUserData(g),&fake);
985     }
986 return( true );
987 }
988 
GFCBack(GWindow gw,struct gmenuitem * mi,GEvent * e)989 static void GFCBack(GWindow gw,struct gmenuitem *mi,GEvent *e) {
990     GFileChooser *gfc = (GFileChooser *) (mi->ti.userdata);
991 
992     if ( gfc->hpos<=0 )
993 return;
994     --gfc->hpos;
995     GFileChooserScanDir(gfc,gfc->history[gfc->hpos]);
996 }
997 
GFCForward(GWindow gw,struct gmenuitem * mi,GEvent * e)998 static void GFCForward(GWindow gw,struct gmenuitem *mi,GEvent *e) {
999     GFileChooser *gfc = (GFileChooser *) (mi->ti.userdata);
1000 
1001     if ( gfc->hpos+1>=gfc->hcnt )
1002 return;
1003     ++gfc->hpos;
1004     GFileChooserScanDir(gfc,gfc->history[gfc->hpos]);
1005 }
1006 
GFCAddCur(GWindow gw,struct gmenuitem * mi,GEvent * e)1007 static void GFCAddCur(GWindow gw,struct gmenuitem *mi,GEvent *e) {
1008     GFileChooser *gfc = (GFileChooser *) (mi->ti.userdata);
1009     unichar_t *dir;
1010     int bcnt;
1011 
1012     dir = GFileChooserGetCurDir(gfc,-1);
1013     bcnt = 0;
1014     if ( bookmarks!=NULL )
1015 	for ( ; bookmarks[bcnt]!=NULL; ++bcnt );
1016     bookmarks = realloc(bookmarks,(bcnt+2)*sizeof(unichar_t *));
1017     bookmarks[bcnt] = dir;
1018     bookmarks[bcnt+1] = NULL;
1019 
1020     if ( prefs_changed!=NULL )
1021 	(prefs_changed)(prefs_changed_data);
1022 }
1023 
GFCRemoveBook(GWindow gw,struct gmenuitem * mi,GEvent * e)1024 static void GFCRemoveBook(GWindow gw,struct gmenuitem *mi,GEvent *e) {
1025     int i,bcnt;
1026     char **books;
1027     char *sel;
1028     char *buts[2];
1029 
1030     if ( bookmarks==NULL || bookmarks[0]==NULL )
1031 return;		/* All gone */
1032     for ( bcnt=0; bookmarks[bcnt]!=NULL; ++bcnt );
1033     sel = calloc(bcnt,1);
1034     books = calloc(bcnt+1,sizeof(char *));
1035     for ( bcnt=0; bookmarks[bcnt]!=NULL; ++bcnt )
1036 	books[bcnt] = u2utf8_copy(bookmarks[bcnt]);
1037     books[bcnt] = NULL;
1038     buts[0] = _("_Remove");
1039     buts[1] = _("_Cancel");
1040     if ( GWidgetChoicesBM8( _("Remove bookmarks"),(const char **) books,sel,bcnt,buts,
1041 	    _("Remove selected bookmarks"))==0 ) {
1042 	for ( i=bcnt=0; bookmarks[bcnt]!=NULL; ++bcnt ) {
1043 	    if ( sel[bcnt] ) {
1044 		free(bookmarks[bcnt]);
1045 	    } else {
1046 		bookmarks[i++] = bookmarks[bcnt];
1047 	    }
1048 	}
1049 	bookmarks[i] = NULL;
1050 
1051 	if ( prefs_changed!=NULL )
1052 	    (prefs_changed)(prefs_changed_data);
1053     }
1054     for ( i=0; books[i]!=NULL; ++i )
1055 	free(books[i]);
1056     free(books);
1057     free(sel);
1058 }
1059 
GFCBookmark(GWindow gw,struct gmenuitem * mi,GEvent * e)1060 static void GFCBookmark(GWindow gw,struct gmenuitem *mi,GEvent *e) {
1061     GFileChooser *gfc = (GFileChooser *) (mi->ti.userdata);
1062     char *home;
1063 
1064     if ( *bookmarks[mi->mid]=='~' && bookmarks[mi->mid][1]=='/' &&
1065 	    (home = getenv("HOME"))!=NULL ) {
1066 	unichar_t *space;
1067 	space = malloc((strlen(home)+u_strlen(bookmarks[mi->mid])+2)*sizeof(unichar_t));
1068 	uc_strcpy(space,home);
1069 	u_strcat(space,bookmarks[mi->mid]+1);
1070 	GFileChooserScanDir(gfc,space);
1071 	free(space);
1072     } else
1073 	GFileChooserScanDir(gfc,bookmarks[mi->mid]);
1074 }
1075 
GFCPath(GWindow gw,struct gmenuitem * mi,GEvent * e)1076 static void GFCPath(GWindow gw,struct gmenuitem *mi,GEvent *e) {
1077     GFileChooser *gfc = (GFileChooser *) (mi->ti.userdata);
1078     char *home;
1079 
1080     if ( *gfc->paths[mi->mid]=='~' && gfc->paths[mi->mid][1]=='/' &&
1081 	    (home = getenv("HOME"))!=NULL ) {
1082 	unichar_t *space;
1083 	space = malloc((strlen(home)+u_strlen(bookmarks[mi->mid])+2)*sizeof(unichar_t));
1084 	uc_strcpy(space,home);
1085 	u_strcat(space,gfc->paths[mi->mid]+1);
1086 	GFileChooserScanDir(gfc,space);
1087 	free(space);
1088     } else
1089 	GFileChooserScanDir(gfc,gfc->paths[mi->mid]);
1090 }
1091 
1092 static GMenuItem gfcbookmarkmenu[] = {
1093     { { (unichar_t *) N_("Directory|Back"), &_GIcon_backarrow, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 0, 0, '\0' }, '\0', ksm_control, NULL, NULL, GFCBack, 0 },
1094     { { (unichar_t *) N_("Directory|Forward"), &_GIcon_forwardarrow, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 0, 0, '\0' }, '\0', ksm_control, NULL, NULL, GFCForward, 0 },
1095     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 1, 0, 0, 0, '\0' }, '\0', 0, NULL, NULL, NULL, 0 }, /* line */
1096     { { (unichar_t *) N_("Bookmark Current Dir"), &_GIcon_bookmark, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 0, 0, '\0' }, '\0', ksm_control, NULL, NULL, GFCAddCur, 0 },
1097     { { (unichar_t *) N_("Remove Bookmark..."), &_GIcon_nobookmark, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 0, 0, '\0' }, '\0', ksm_control, NULL, NULL, GFCRemoveBook, 0 },
1098     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 1, 0, 0, 0, '\0' }, '\0', 0, NULL, NULL, NULL, 0 }, /* line */
1099     GMENUITEM_EMPTY
1100 };
1101 static int bgotten=false;
1102 
GFileChooserBookmarks(GGadget * g,GEvent * e)1103 static int GFileChooserBookmarks(GGadget *g, GEvent *e) {
1104     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonpress ) {
1105 	GFileChooser *gfc = (GFileChooser *) GGadgetGetUserData(g);
1106 	GMenuItem *mi;
1107 	int i, bcnt, mcnt, pcnt;
1108 	GEvent fake;
1109 	GRect pos;
1110 
1111 	if ( !bgotten ) {
1112 	    bgotten = true;
1113 	    for ( i=0; gfcbookmarkmenu[i].ti.text!=NULL || gfcbookmarkmenu[i].ti.line; ++i )
1114 		if ( gfcbookmarkmenu[i].ti.text!=NULL )
1115 		    gfcbookmarkmenu[i].ti.text = (unichar_t *) S_( (char *) gfcbookmarkmenu[i].ti.text);
1116 	}
1117 	for ( mcnt=0; gfcbookmarkmenu[mcnt].ti.text!=NULL || gfcbookmarkmenu[mcnt].ti.line; ++mcnt );
1118 	bcnt = 0;
1119 	if ( bookmarks!=NULL )
1120 	    for ( ; bookmarks[bcnt]!=NULL; ++bcnt );
1121 	if ( gfc->paths!=NULL ) {
1122 	    for ( pcnt=0; gfc->paths[pcnt]!=NULL; ++pcnt );
1123 	    if ( pcnt!=0 && bcnt!=0 ) ++pcnt;
1124 	    bcnt += pcnt;
1125 	}
1126 	mi = calloc((mcnt+bcnt+1),sizeof(GMenuItem));
1127 	for ( mcnt=0; gfcbookmarkmenu[mcnt].ti.text!=NULL || gfcbookmarkmenu[mcnt].ti.line; ++mcnt ) {
1128 	    mi[mcnt] = gfcbookmarkmenu[mcnt];
1129 	    mi[mcnt].ti.text = (unichar_t *) copy( (char *) mi[mcnt].ti.text);
1130 	    mi[mcnt].ti.userdata = gfc;
1131 	}
1132 	if ( gfc->hpos==0 )
1133 	    mi[0].ti.disabled = true;		/* can't go further back */
1134 	if ( gfc->hpos+1>=gfc->hcnt )
1135 	    mi[1].ti.disabled = true;		/* can't go further forward */
1136 	if ( bookmarks==NULL )
1137 	    mi[4].ti.disabled = true;		/* can't remove bookmarks, already none */
1138 	else {
1139 	    if ( gfc->paths!=NULL ) {
1140 		for ( bcnt=0; gfc->paths[bcnt]!=NULL; ++bcnt ) {
1141 		    mi[mcnt+bcnt].ti.text = u_copy(gfc->paths[bcnt]);
1142 		    mi[mcnt+bcnt].ti.fg = mi[mcnt+bcnt].ti.bg = COLOR_DEFAULT;
1143 		    mi[mcnt+bcnt].ti.userdata = gfc;
1144 		    mi[mcnt+bcnt].mid = bcnt;
1145 		    mi[mcnt+bcnt].invoke = GFCPath;
1146 		}
1147 		mcnt+=bcnt;
1148 		if ( bookmarks!=NULL && bookmarks[0]!=NULL ) {
1149 		    mi[mcnt].ti.line = true;
1150 		    mi[mcnt].ti.fg = mi[mcnt].ti.bg = COLOR_DEFAULT;
1151 		    ++mcnt;
1152 		}
1153 	    }
1154 	    for ( bcnt=0; bookmarks[bcnt]!=NULL; ++bcnt ) {
1155 		mi[mcnt+bcnt].ti.text = u_copy(bookmarks[bcnt]);
1156 		mi[mcnt+bcnt].ti.fg = mi[mcnt+bcnt].ti.bg = COLOR_DEFAULT;
1157 		mi[mcnt+bcnt].ti.userdata = gfc;
1158 		mi[mcnt+bcnt].mid = bcnt;
1159 		mi[mcnt+bcnt].invoke = GFCBookmark;
1160 	    }
1161 	}
1162 	GGadgetGetSize(g,&pos);
1163 	memset(&fake,0,sizeof(fake));
1164 	fake.type = et_mousedown;
1165 	fake.w = g->base;
1166 	fake.u.mouse.x = pos.x;
1167 	fake.u.mouse.y = pos.y+pos.height;
1168 	GMenuCreatePopupMenu(gfc->g.base,&fake, mi);
1169 	GMenuItemArrayFree(mi);
1170     }
1171 return( true );
1172 }
1173 
1174 /* Routine to be called as the mouse moves across the dlg */
GFileChooserPopupCheck(GGadget * g,GEvent * e)1175 void GFileChooserPopupCheck(GGadget *g,GEvent *e) {
1176     GFileChooser *gfc = (GFileChooser *) g;
1177     int inside=false;
1178 
1179     if ( e->type == et_mousemove && (e->u.mouse.state&ksm_buttons)==0 ) {
1180 	GGadgetEndPopup();
1181 	for ( g=((GContainerD *) (gfc->g.base->widget_data))->gadgets; g!=NULL; g=g->prev ) {
1182 	    if ( g!=(GGadget *) gfc && g!=(GGadget *) (gfc->filterb) &&
1183 		     g!=(GGadget *) (gfc->files) &&
1184 		     g->takes_input &&
1185 		     e->u.mouse.x >= g->r.x &&
1186 		     e->u.mouse.x<g->r.x+g->r.width &&
1187 		     e->u.mouse.y >= g->r.y &&
1188 		     e->u.mouse.y<g->r.y+g->r.height ) {
1189 		inside = true;
1190 	break;
1191 	    }
1192 	}
1193 	if ( !inside )
1194 	    GGadgetPreparePopup(gfc->g.base,gfc->wildcard);
1195     } else if ( e->type == et_mousedown && e->u.mouse.button==3 ) {
1196 	GFCPopupMenu(g,e);
1197     }
1198 }
1199 
1200 /* Routine to be called by the filter button */
GFileChooserFilterIt(GGadget * g)1201 void GFileChooserFilterIt(GGadget *g) {
1202     GFileChooser *gfc = (GFileChooser *) g;
1203     unichar_t *pt, *spt, *slashpt, *dir, *temp;
1204     unichar_t *tofree = NULL;
1205     int wasdir;
1206 
1207     wasdir = gfc->lastname!=NULL;
1208 
1209     spt = (unichar_t *) _GGadgetGetTitle(&gfc->name->g);
1210 #ifdef _WIN32
1211     spt = tofree = u_GFileNormalizePath(u_copy(spt));
1212 #endif
1213     if ( *spt=='\0' ) {		/* Werner tells me that pressing the Filter button with nothing should show the default filter mask */
1214 	if ( gfc->wildcard!=NULL )
1215 	    GGadgetSetTitle(&gfc->name->g,gfc->wildcard);
1216 return;
1217     }
1218 
1219     if (( slashpt = u_strrchr(spt,'/'))==NULL )
1220 	slashpt = spt;
1221     else
1222 	++slashpt;
1223     pt = slashpt;
1224     while ( *pt && *pt!='*' && *pt!='?' && *pt!='[' && *pt!='{' )
1225 	++pt;
1226     if ( *pt!='\0' ) {
1227 	free(gfc->wildcard);
1228 	gfc->wildcard = u_copy(slashpt);
1229     } else if ( gfc->lastname==NULL )
1230 	gfc->lastname = u_copy(slashpt);
1231     if( u_GFileIsAbsolute(spt) )
1232 	dir = u_copyn(spt,slashpt-spt);
1233     else {
1234 	unichar_t *curdir = GFileChooserGetCurDir(gfc,-1);
1235 	if ( slashpt!=spt ) {
1236 	    temp = u_copyn(spt,slashpt-spt);
1237 	    dir = u_GFileAppendFile(curdir,temp,true);
1238 	    free(temp);
1239 	} else if ( wasdir && *pt=='\0' )
1240 	    dir = u_GFileAppendFile(curdir,spt,true);
1241 	else
1242 	    dir = curdir;
1243 	if ( dir!=curdir )
1244 	    free(curdir);
1245     }
1246     GFileChooserScanDir(gfc,dir);
1247     free(dir);
1248     free(tofree);
1249 }
1250 
1251 /* A function that may be connected to a filter button as its handle_controlevent */
GFileChooserFilterEh(GGadget * g,GEvent * e)1252 int GFileChooserFilterEh(GGadget *g, GEvent *e) {
1253     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate )
1254 	GFileChooserFilterIt(GGadgetGetUserData(g));
1255 return( true );
1256 }
1257 
GFileChooserConnectButtons(GGadget * g,GGadget * ok,GGadget * filter)1258 void GFileChooserConnectButtons(GGadget *g,GGadget *ok, GGadget *filter) {
1259     GFileChooser *gfc = (GFileChooser *) g;
1260     gfc->ok = (GButton *) ok;
1261     gfc->filterb = (GButton *) filter;
1262 }
1263 
GFileChooserSetFilterText(GGadget * g,const unichar_t * wildcard)1264 void GFileChooserSetFilterText(GGadget *g,const unichar_t *wildcard) {
1265     GFileChooser *gfc = (GFileChooser *) g;
1266     free(gfc->wildcard);
1267     gfc->wildcard = u_copy(wildcard);
1268 }
1269 
GFileChooserGetFilterText(GGadget * g)1270 unichar_t *GFileChooserGetFilterText(GGadget *g) {
1271     GFileChooser *gfc = (GFileChooser *) g;
1272 return( gfc->wildcard );
1273 }
1274 
GFileChooserSetFilterFunc(GGadget * g,GFileChooserFilterType filter)1275 void GFileChooserSetFilterFunc(GGadget *g,GFileChooserFilterType filter) {
1276     GFileChooser *gfc = (GFileChooser *) g;
1277     if ( filter==NULL )
1278 	filter = GFileChooserDefFilter;
1279     gfc->filter = filter;
1280 }
1281 
GFileChooserGetFilterFunc(GGadget * g)1282 GFileChooserFilterType GFileChooserGetFilterFunc(GGadget *g) {
1283     GFileChooser *gfc = (GFileChooser *) g;
1284 return( gfc->filter );
1285 }
1286 
1287 /**
1288  * no change to the current filename by default
1289  *
1290  * if a change is desired, then currentFilename should point to the
1291  * new string and return 1 to allow the caller to free this new
1292  * string.
1293  */
GFileChooserDefInputFilenameFunc(GGadget * g,const unichar_t ** currentFilename,unichar_t * oldfilename)1294 int GFileChooserDefInputFilenameFunc( GGadget *g,
1295 				      const unichar_t ** currentFilename,
1296 				      unichar_t* oldfilename ) {
1297     return 0;
1298 }
1299 
GFileChooserSetInputFilenameFunc(GGadget * g,GFileChooserInputFilenameFuncType func)1300 void GFileChooserSetInputFilenameFunc(GGadget *g,GFileChooserInputFilenameFuncType func) {
1301     GFileChooser *gfc = (GFileChooser *) g;
1302     if ( func==NULL )
1303 	func = GFileChooserDefInputFilenameFunc;
1304     gfc->inputfilenamefunc = func;
1305 }
1306 
GFileChooserGetInputFilenameFunc(GGadget * g)1307 GFileChooserInputFilenameFuncType GFileChooserGetInputFilenameFunc(GGadget *g) {
1308     GFileChooser *gfc = (GFileChooser *) g;
1309     if ( gfc->inputfilenamefunc==NULL )
1310 	return GFileChooserDefInputFilenameFunc;
1311     return( gfc->inputfilenamefunc );
1312 }
1313 
1314 
GFileChooserSetFilename(GGadget * g,const unichar_t * defaultfile)1315 void GFileChooserSetFilename(GGadget *g,const unichar_t *defaultfile)
1316 {
1317     GFileChooser *gfc = (GFileChooser *) g;
1318 
1319     GGadgetSetTitle(g,defaultfile);
1320 
1321     // if this is the first time we are here, we assume
1322     // the current filename is what it was before. Less NULL
1323     // checks in the callback function below.
1324     if(!gfc->inputfilenameprevchar)
1325 	gfc->inputfilenameprevchar = u_copy(_GGadgetGetTitle(&gfc->name->g));
1326 
1327 }
1328 
1329 
GFileChooserSetMimetypes(GGadget * g,unichar_t ** mimetypes)1330 void GFileChooserSetMimetypes(GGadget *g,unichar_t **mimetypes) {
1331     GFileChooser *gfc = (GFileChooser *) g;
1332     int i;
1333 
1334     if ( gfc->mimetypes ) {
1335 	for ( i=0; gfc->mimetypes[i]!=NULL; ++i )
1336 	    free( gfc->mimetypes[i]);
1337 	free(gfc->mimetypes);
1338     }
1339     if ( mimetypes ) {
1340 	for ( i=0; mimetypes[i]!=NULL; ++i );
1341 	gfc->mimetypes = malloc((i+1)*sizeof(unichar_t *));
1342 	for ( i=0; mimetypes[i]!=NULL; ++i )
1343 	    gfc->mimetypes[i] = u_copy(mimetypes[i]);
1344 	gfc->mimetypes[i] = NULL;
1345     } else
1346 	gfc->mimetypes = NULL;
1347 }
1348 
GFileChooserGetMimetypes(GGadget * g)1349 unichar_t **GFileChooserGetMimetypes(GGadget *g) {
1350     GFileChooser *gfc = (GFileChooser *) g;
1351 return( gfc->mimetypes );
1352 }
1353 
1354 /* Change the current file, or the current directory/file */
GFileChooserSetTitle(GGadget * g,const unichar_t * tit)1355 static void GFileChooserSetTitle(GGadget *g,const unichar_t *tit) {
1356     GFileChooser *gfc = (GFileChooser *) g;
1357     unichar_t *pt, *curdir, *temp, *dir, *base;
1358 
1359     if ( tit==NULL ) {
1360 	curdir = GFileChooserGetCurDir(gfc,-1);
1361 	GFileChooserScanDir(gfc,curdir);
1362 	free(curdir);
1363 return;
1364     }
1365 
1366     pt = u_strrchr(tit,'/');
1367     free(gfc->lastname);
1368     gfc->lastname = NULL;
1369 
1370     if ( u_GFileIsAbsolute(tit) ){
1371 	base = uc_strstr(tit, "://");
1372 	if(!base) base = (unichar_t*) tit;
1373 	if(pt > base && pt[1] && (pt[1]!='.' || pt[2]!='\0')){
1374 	    gfc->lastname = u_copy(pt+1);
1375 	    dir = u_copyn(tit, pt-tit);
1376 	}
1377 	else{
1378 	    dir = u_copy(tit);
1379 	}
1380 	GFileChooserScanDir(gfc,dir);
1381 	free(dir);
1382     } else if ( pt==NULL ) {
1383 	GGadgetSetTitle(&gfc->name->g,tit);
1384 	curdir = GFileChooserGetCurDir(gfc,-1);
1385 	GFileChooserScanDir(gfc,curdir);
1386 	free(curdir);
1387     } else {
1388 	curdir = GFileChooserGetCurDir(gfc,-1);
1389 	temp = u_copyn(tit,pt-tit);
1390 	dir = u_GFileAppendFile(curdir,temp,true);
1391 	free(temp); free(curdir);
1392 	free(gfc->lastname);
1393 	if ( pt[1]!='\0' )
1394 	    gfc->lastname = u_copy(pt+1);
1395 	GFileChooserScanDir(gfc,dir);
1396 	free(dir);
1397     }
1398 }
1399 
GFileChooserRefreshList(GGadget * g)1400 void GFileChooserRefreshList(GGadget *g) {
1401     GFileChooser *gfc = (GFileChooser *) g;
1402     unichar_t *curdir;
1403 
1404     curdir = GFileChooserGetCurDir(gfc,-1);
1405     GFileChooserScanDir(gfc,curdir);
1406     free(curdir);
1407 }
1408 
1409 /* Get the current directory/file */
GFileChooserGetTitle(GGadget * g)1410 static unichar_t *GFileChooserGetTitle(GGadget *g) {
1411     GFileChooser *gfc = (GFileChooser *) g;
1412     unichar_t *spt, *curdir, *file;
1413 
1414     spt = u_GFileNormalizePath(u_copy((unichar_t *)_GGadgetGetTitle(&gfc->name->g)));
1415     if ( u_GFileIsAbsolute(spt) )
1416 	file = spt;
1417     else {
1418 	curdir = GFileChooserGetCurDir(gfc,-1);
1419 	file = u_GFileAppendFile(curdir,spt,gfc->lastname!=NULL);
1420 	free(curdir);
1421     }
1422     if (file != spt) {
1423         free(spt);
1424     }
1425 return( file );
1426 }
1427 
GFileChooser_destroy(GGadget * g)1428 static void GFileChooser_destroy(GGadget *g) {
1429     GFileChooser *gfc = (GFileChooser *) g;
1430     int i;
1431 
1432     free(lastdir);
1433     lastdir = GFileChooserGetCurDir(gfc,-1);
1434 
1435     if ( gfc->outstanding )
1436 	GIOcancel(gfc->outstanding);
1437     GGadgetDestroy(&gfc->topbox->g);	/* destroys everything */
1438     if ( gfc->paths!=NULL ) {
1439 	for ( i=0; gfc->paths[i]!=NULL; ++i )
1440 	    free(gfc->paths[i]);
1441 	free(gfc->paths);
1442     }
1443     free(gfc->wildcard);
1444     free(gfc->lastname);
1445     if ( gfc->mimetypes ) {
1446 	for ( i=0; gfc->mimetypes[i]!=NULL; ++i )
1447 	    free( gfc->mimetypes[i]);
1448 	free(gfc->mimetypes);
1449     }
1450     for ( i=0; i<gfc->hcnt; ++i )
1451 	free(gfc->history[i]);
1452     free(gfc->history);
1453     _ggadget_destroy(&gfc->g);
1454 }
1455 
gfilechooser_expose(GWindow pixmap,GGadget * g,GEvent * event)1456 static int gfilechooser_expose(GWindow pixmap, GGadget *g, GEvent *event) {
1457 return( true );
1458 }
1459 
gfilechooser_mouse(GGadget * g,GEvent * event)1460 static int gfilechooser_mouse(GGadget *g, GEvent *event) {
1461     GFileChooser *gfc = (GFileChooser *) g;
1462 
1463     if (( event->type==et_mouseup || event->type==et_mousedown ) &&
1464 	    (event->u.mouse.button>=4 && event->u.mouse.button<=7) ) {
1465 	if ( gfc->files->vsb!=NULL )
1466 return( GGadgetDispatchEvent(&gfc->files->vsb->g,event));
1467 	else
1468 return( true );
1469     }
1470 
1471 return( false );
1472 }
1473 
gfilechooser_noop(GGadget * g,GEvent * event)1474 static int gfilechooser_noop(GGadget *g, GEvent *event) {
1475 return( false );
1476 }
1477 
gfilechooser_timer(GGadget * g,GEvent * event)1478 static int gfilechooser_timer(GGadget *g, GEvent *event) {
1479 return( false );
1480 }
1481 
gfilechooser_move(GGadget * g,int32 x,int32 y)1482 static void gfilechooser_move(GGadget *g, int32 x, int32 y ) {
1483     GFileChooser *gfc = (GFileChooser *) g;
1484 
1485     GGadgetMove(&gfc->topbox->g,x,y);
1486     _ggadget_move(g,x,y);
1487 }
1488 
gfilechooser_resize(GGadget * g,int32 width,int32 height)1489 static void gfilechooser_resize(GGadget *g, int32 width, int32 height ) {
1490     GFileChooser *gfc = (GFileChooser *) g;
1491 
1492     GGadgetResize(&gfc->topbox->g,width,height);
1493     _ggadget_resize(g,width,height);
1494 }
1495 
gfilechooser_setvisible(GGadget * g,int visible)1496 static void gfilechooser_setvisible(GGadget *g, int visible ) {
1497     GFileChooser *gfc = (GFileChooser *) g;
1498     GGadgetSetVisible(&gfc->files->g,visible);
1499     GGadgetSetVisible(&gfc->directories->g,visible);
1500     GGadgetSetVisible(&gfc->name->g,visible);
1501     GGadgetSetVisible(&gfc->up->g,visible);
1502     GGadgetSetVisible(&gfc->home->g,visible);
1503     GGadgetSetVisible(&gfc->bookmarks->g,visible);
1504     GGadgetSetVisible(&gfc->config->g,visible);
1505     GGadgetSetVisible(&gfc->topbox->g,visible);
1506     _ggadget_setvisible(g,visible);
1507 }
1508 
gfilechooser_setenabled(GGadget * g,int enabled)1509 static void gfilechooser_setenabled(GGadget *g, int enabled ) {
1510     GFileChooser *gfc = (GFileChooser *) g;
1511     GGadgetSetEnabled(&gfc->files->g,enabled);
1512     GGadgetSetEnabled(&gfc->subdirs->g,enabled);
1513     GGadgetSetEnabled(&gfc->directories->g,enabled);
1514     GGadgetSetEnabled(&gfc->name->g,enabled);
1515     GGadgetSetEnabled(&gfc->up->g,enabled);
1516     GGadgetSetEnabled(&gfc->home->g,enabled);
1517     GGadgetSetEnabled(&gfc->bookmarks->g,enabled);
1518     GGadgetSetEnabled(&gfc->config->g,enabled);
1519     GGadgetSetEnabled(&gfc->topbox->g,enabled);
1520     _ggadget_setenabled(g,enabled);
1521 }
1522 
GFileChooserGetDesiredSize(GGadget * g,GRect * outer,GRect * inner)1523 static void GFileChooserGetDesiredSize(GGadget *g,GRect *outer,GRect *inner) {
1524     if ( inner!=NULL ) {
1525 	int bp = GBoxBorderWidth(g->base,g->box);
1526 	inner->x = inner->y = 0;
1527 	inner->width = g->desired_width - 2*bp;
1528 	inner->height = g->desired_height - 2*bp;
1529     }
1530     if ( outer!=NULL ) {
1531 	outer->x = outer->y = 0;
1532 	outer->width = g->desired_width;
1533 	outer->height = g->desired_height;
1534     }
1535 }
1536 
gfilechooser_FillsWindow(GGadget * g)1537 static int gfilechooser_FillsWindow(GGadget *g) {
1538 return( g->prev==NULL &&
1539 	(_GWidgetGetGadgets(g->base)==g ||
1540 	 _GWidgetGetGadgets(g->base)==(GGadget *) ((GFileChooser *) g)->up ));
1541 }
1542 
1543 struct gfuncs GFileChooser_funcs = {
1544     0,
1545     sizeof(struct gfuncs),
1546 
1547     gfilechooser_expose,
1548     gfilechooser_mouse,
1549     gfilechooser_noop,
1550     NULL,
1551     NULL,
1552     gfilechooser_timer,
1553     NULL,
1554 
1555     _ggadget_redraw,
1556     gfilechooser_move,
1557     gfilechooser_resize,
1558     gfilechooser_setvisible,
1559     gfilechooser_setenabled,
1560     _ggadget_getsize,
1561     _ggadget_getinnersize,
1562 
1563     GFileChooser_destroy,
1564 
1565     GFileChooserSetTitle,
1566     NULL,
1567     GFileChooserGetTitle,
1568     NULL,
1569     NULL,
1570     NULL,
1571     NULL,
1572     NULL,
1573 
1574     NULL,
1575     NULL,
1576     NULL,
1577     NULL,
1578     NULL,
1579     NULL,
1580     NULL,
1581     NULL,
1582     NULL,
1583     NULL,
1584 
1585     GFileChooserGetDesiredSize,
1586     _ggadget_setDesiredSize,
1587     gfilechooser_FillsWindow,
1588     NULL
1589 };
1590 
GFileChooserCreateChildren(GFileChooser * gfc,int flags)1591 static void GFileChooserCreateChildren(GFileChooser *gfc, int flags) {
1592     GGadgetCreateData gcd[9], boxes[4], *varray[9], *harray[12], *harray2[4];
1593     GTextInfo label[9];
1594     int k=0, l=0, homek, upk, bookk, confk, dirsk, subdirsk, filesk, textk;
1595 
1596     memset(&gcd,'\0',sizeof(gcd));
1597     memset(&boxes,'\0',sizeof(boxes));
1598     memset(&label,'\0',sizeof(label));
1599 
1600     gcd[k].gd.flags = gg_visible|gg_enabled;
1601     gcd[k].gd.popup_msg = _("Home Folder");
1602     label[k].image = &_GIcon_homefolder;
1603     gcd[k].gd.label = &label[k];
1604     gcd[k].gd.handle_controlevent = GFileChooserHome;
1605     homek = k;
1606     gcd[k++].creator = GButtonCreate;
1607     harray[l++] = &gcd[k-1]; harray[l++] = GCD_Glue;
1608 
1609     gcd[k].gd.flags = gg_visible|gg_enabled;
1610     gcd[k].gd.popup_msg = _("Bookmarks");
1611     label[k].image = &_GIcon_bookmark;
1612     gcd[k].gd.label = &label[k];
1613     gcd[k].gd.handle_controlevent = GFileChooserBookmarks;
1614     bookk = k;
1615     gcd[k++].creator = GButtonCreate;
1616     harray[l++] = &gcd[k-1]; harray[l++] = GCD_Glue;
1617 
1618     gcd[k].gd.flags = gg_visible|gg_enabled|gg_list_exactlyone;
1619     gcd[k].gd.handle_controlevent = GFileChooserDListChanged;
1620     dirsk = k;
1621     gcd[k++].creator = GListButtonCreate;
1622     harray[l++] = &gcd[k-1]; harray[l++] = GCD_Glue;
1623 
1624     gcd[k].gd.flags = gg_visible|gg_enabled;
1625     gcd[k].gd.popup_msg = _("Parent Folder");
1626     label[k].image = &_GIcon_updir;
1627     gcd[k].gd.label = &label[k];
1628     gcd[k].gd.handle_controlevent = GFileChooserUpDirButton;
1629     upk = k;
1630     gcd[k++].creator = GButtonCreate;
1631     harray[l++] = &gcd[k-1]; harray[l++] = GCD_Glue;
1632 
1633     gcd[k].gd.flags = gg_visible|gg_enabled;
1634     gcd[k].gd.popup_msg = _("Configure");
1635     label[k].image = &_GIcon_configtool;
1636     gcd[k].gd.label = &label[k];
1637     gcd[k].gd.handle_controlevent = GFileChooserConfigure;
1638     confk = k;
1639     gcd[k++].creator = GButtonCreate;
1640     harray[l++] = &gcd[k-1]; harray[l++] = NULL;
1641 
1642     boxes[2].gd.flags = gg_enabled|gg_visible;
1643     boxes[2].gd.u.boxelements = harray;
1644     boxes[2].creator = GHBoxCreate;
1645 
1646     l=0;
1647     varray[l++] = &boxes[2];
1648 
1649     if ( dir_placement==dirs_separate )
1650 	gcd[k].gd.flags = gg_visible|gg_enabled|gg_list_alphabetic|gg_list_exactlyone;
1651     else
1652 	gcd[k].gd.flags =            gg_enabled|gg_list_alphabetic|gg_list_exactlyone;
1653     gcd[k].gd.handle_controlevent = GFileChooserFListSelected;
1654     subdirsk = k;
1655     gcd[k++].creator = GListCreate;
1656     harray2[0] = &gcd[k-1];
1657 
1658     if ( flags & gg_file_multiple )
1659 	gcd[k].gd.flags = gg_visible|gg_enabled|gg_list_alphabetic|gg_list_multiplesel;
1660     else
1661 	gcd[k].gd.flags = gg_visible|gg_enabled|gg_list_alphabetic|gg_list_exactlyone;
1662     gcd[k].gd.handle_controlevent = GFileChooserFListSelected;
1663     filesk = k;
1664     gcd[k++].creator = GListCreate;
1665     harray2[1] = &gcd[k-1]; harray2[2] = NULL;
1666 
1667     boxes[3].gd.flags = gg_enabled|gg_visible;
1668     boxes[3].gd.u.boxelements = harray2;
1669     boxes[3].creator = GHBoxCreate;
1670     varray[l++] = &boxes[3];
1671 
1672     gcd[k].gd.flags = gg_visible|gg_enabled;
1673     gcd[k].gd.handle_controlevent = GFileChooserTextChanged;
1674     textk = k;
1675     if ( flags&gg_file_pulldown )
1676 	gcd[k++].creator = GListFieldCreate;
1677     else
1678 	gcd[k++].creator = GTextCompletionCreate;
1679     varray[l++] = &gcd[k-1]; varray[l] = NULL;
1680 
1681     boxes[0].gd.pos.x = gfc->g.r.x;
1682     boxes[0].gd.pos.y = gfc->g.r.y;
1683     boxes[0].gd.pos.width  = gfc->g.r.width;
1684     boxes[0].gd.pos.height = gfc->g.r.height;
1685     boxes[0].gd.flags = gg_enabled|gg_visible;
1686     boxes[0].gd.u.boxelements = varray;
1687     boxes[0].creator = GVBoxCreate;
1688 
1689     for ( l=0; l<k; ++l )
1690 	gcd[l].data = gfc;
1691 
1692     GGadgetsCreate(gfc->g.base,boxes);
1693 
1694     gfc->topbox      = (GHVBox *)      boxes[0].ret;
1695     gfc->home        = (GButton *)     gcd[homek].ret;
1696     gfc->bookmarks   = (GButton *)     gcd[bookk].ret;
1697     gfc->directories = (GListButton *) gcd[dirsk].ret;
1698     gfc->up          = (GButton *)     gcd[upk  ].ret;
1699     gfc->config      = (GButton *)     gcd[confk].ret;
1700     gfc->subdirs     = (GList *)       gcd[subdirsk].ret;
1701     gfc->files       = (GList *)       gcd[filesk].ret;
1702     gfc->name        = (GTextField *)  gcd[textk].ret;
1703 
1704     gfc->home->g.contained = true;
1705     gfc->bookmarks->g.contained = true;
1706     gfc->directories->g.contained = true;
1707     gfc->up->g.contained = true;
1708     gfc->config->g.contained = true;
1709     gfc->subdirs->g.contained = true;
1710     gfc->files->g.contained = true;
1711     gfc->name->g.contained = true;
1712     gfc->topbox->g.contained = true;
1713 
1714     GCompletionFieldSetCompletion(&gfc->name->g,GFileChooserCompletion);
1715     GCompletionFieldSetCompletionMode(&gfc->name->g,true);
1716 
1717     GHVBoxSetExpandableRow(boxes[0].ret,1);
1718     GHVBoxSetExpandableCol(boxes[2].ret,4);
1719     if ( boxes[0].gd.pos.width!=0 && boxes[0].gd.pos.height!=0 )
1720 	GGadgetResize(boxes[0].ret,boxes[0].gd.pos.width,boxes[0].gd.pos.height);
1721 }
1722 
GFileChooserCreate(struct gwindow * base,GGadgetData * gd,void * data)1723 GGadget *GFileChooserCreate(struct gwindow *base, GGadgetData *gd,void *data) {
1724     GFileChooser *gfc = (GFileChooser *) calloc(1,sizeof(GFileChooser));
1725 
1726     gfc->g.funcs = &GFileChooser_funcs;
1727     _GGadget_Create(&gfc->g,base,gd,data,&gfilechooser_box);
1728     gfc->g.takes_input = gfc->g.takes_keyboard = false; gfc->g.focusable = false;
1729     if ( gfc->g.r.width == 0 )
1730 	gfc->g.r.width = GGadgetScale(GDrawPointsToPixels(base,140));
1731     if ( gfc->g.r.height == 0 )
1732 	gfc->g.r.height = GDrawPointsToPixels(base,100);
1733     gfc->g.desired_width = gfc->g.r.width;
1734     gfc->g.desired_height = gfc->g.r.height;
1735     gfc->g.inner = gfc->g.r;
1736     _GGadget_FinalPosition(&gfc->g,base,gd);
1737 
1738     GFileChooserCreateChildren(gfc, gd->flags);
1739     gfc->filter = GFileChooserDefFilter;
1740     GFileChooserSetInputFilenameFunc( (GGadget*)gfc, 0 );
1741     if ( gd->flags & gg_group_end )
1742 	_GGadgetCloseGroup(&gfc->g);
1743 
1744     if ( lastdir==NULL ) {
1745 	static unichar_t dot[] = { '.', '\0' };
1746 	unichar_t buffer[1025];
1747 	lastdir = u_copy(u_GFileGetAbsoluteName(dot,buffer,sizeof(buffer)/sizeof(unichar_t)));
1748     }
1749     if ( gd->label==NULL || gd->label->text==NULL )
1750 	GFileChooserSetTitle(&gfc->g,lastdir);
1751     else if ( u_GFileIsAbsolute(gd->label->text) )
1752 	GFileChooserSetTitle(&gfc->g,gd->label->text);
1753     else {
1754 	unichar_t *temp = u_GFileAppendFile(lastdir,gd->label->text,false);
1755 	temp = u_GFileNormalize(temp);
1756 	GFileChooserSetTitle(&gfc->g,temp);
1757 	free(temp);
1758     }
1759 
1760 return( &gfc->g );
1761 }
1762 
GFileChooserSetDir(GGadget * g,unichar_t * dir)1763 void GFileChooserSetDir(GGadget *g,unichar_t *dir) {
1764     GFileChooserScanDir((GFileChooser *) g,dir);
1765 }
1766 
GFileChooserGetDir(GGadget * g)1767 unichar_t *GFileChooserGetDir(GGadget *g) {
1768 return( GFileChooserGetCurDir((GFileChooser *) g,-1));
1769 }
1770 
GFileChooserReplaceIO(GGadget * g,GIOControl * gc)1771 GIOControl *GFileChooserReplaceIO(GGadget *g,GIOControl *gc) {
1772     GFileChooser *gfc = (GFileChooser *)g;
1773 
1774     if ( gfc->outstanding!=NULL ) {
1775 	GIOclose(gfc->outstanding);
1776 	gfc->outstanding = NULL;
1777 	GDrawSetCursor(gfc->g.base,gfc->old_cursor);
1778     }
1779     if ( gc!=NULL ) {
1780 	gfc->old_cursor = GDrawGetCursor(gfc->g.base);
1781 	GDrawSetCursor(gfc->g.base,ct_watch);
1782 	gfc->outstanding = gc;
1783     }
1784 return( gc );
1785 }
1786 
GFileChooserGetChildren(GGadget * g,GGadget ** pulldown,GGadget ** list,GGadget ** tf)1787 void GFileChooserGetChildren(GGadget *g,GGadget **pulldown, GGadget **list, GGadget **tf) {
1788     GFileChooser *gfc = (GFileChooser *)g;
1789 
1790     if ( pulldown!=NULL ) *pulldown = &gfc->directories->g;
1791     if ( tf!=NULL )	  *tf = &gfc->name->g;
1792     if ( list!=NULL )	  *list = &gfc->files->g;
1793 }
1794 
GFileChooserPosIsDir(GGadget * g,int pos)1795 int GFileChooserPosIsDir(GGadget *g, int pos) {
1796     GFileChooser *gfc = (GFileChooser *)g;
1797     GGadget *list = &gfc->files->g;
1798     GTextInfo *ti;
1799 
1800     ti = GGadgetGetListItem(list,pos);
1801     if ( ti==NULL )
1802 return( false );
1803 
1804 return( ti->checked );
1805 }
1806 
GFileChooserFileNameOfPos(GGadget * g,int pos)1807 unichar_t *GFileChooserFileNameOfPos(GGadget *g, int pos) {
1808     GFileChooser *gfc = (GFileChooser *)g;
1809     GGadget *list = &gfc->files->g;
1810     GTextInfo *ti;
1811     unichar_t *curdir, *file;
1812 
1813     ti = GGadgetGetListItem(list,pos);
1814     if ( ti==NULL )
1815 return( NULL );
1816 
1817     curdir = GFileChooserGetCurDir(gfc,-1);
1818     file = u_GFileAppendFile(curdir,ti->text,false);
1819     free(curdir);
1820 return( file );
1821 }
1822 
GFileChooserSetShowHidden(int sh)1823 void GFileChooserSetShowHidden(int sh) {
1824     showhidden = sh;
1825 }
1826 
GFileChooserGetShowHidden(void)1827 int GFileChooserGetShowHidden(void) {
1828 return( showhidden );
1829 }
1830 
GFileChooserSetDirectoryPlacement(int dp)1831 void GFileChooserSetDirectoryPlacement(int dp) {
1832     dir_placement = dp;
1833 }
1834 
GFileChooserGetDirectoryPlacement(void)1835 int GFileChooserGetDirectoryPlacement(void) {
1836 return( dir_placement );
1837 }
1838 
GFileChooserSetBookmarks(unichar_t ** b)1839 void GFileChooserSetBookmarks(unichar_t **b) {
1840     if ( bookmarks!=NULL && bookmarks!=b ) {
1841 	int i;
1842 
1843 	for ( i=0; bookmarks[i]!=NULL; ++i )
1844 	    free(bookmarks[i]);
1845 	free(bookmarks);
1846     }
1847     bookmarks = b;
1848 }
1849 
GFileChooserGetBookmarks(void)1850 unichar_t **GFileChooserGetBookmarks(void) {
1851 return( bookmarks );
1852 }
1853 
GFileChooserSetPrefsChangedCallback(void * data,void (* p_c)(void *))1854 void GFileChooserSetPrefsChangedCallback(void *data, void (*p_c)(void *)) {
1855     prefs_changed = p_c;
1856     prefs_changed_data = data;
1857 }
1858 
GFileChooserSetPaths(GGadget * g,const char * const * path)1859 void GFileChooserSetPaths(GGadget *g, const char* const* path) {
1860     unichar_t **dirs = NULL;
1861     int dcnt;
1862     GFileChooser *gfc = (GFileChooser *) g;
1863 
1864     if ( gfc->paths!=NULL ) {
1865 	for ( dcnt=0; gfc->paths[dcnt]!=NULL; ++dcnt )
1866 	    free( gfc->paths[dcnt] );
1867 	free(gfc->paths);
1868 	gfc->paths = NULL;
1869     }
1870     if ( path==NULL || path[0]==NULL )
1871 return;
1872 
1873     for ( dcnt=0; path[dcnt]!=NULL; ++dcnt );
1874     gfc->paths = dirs = malloc((dcnt+1)*sizeof(unichar_t *));
1875     for ( dcnt=0; path[dcnt]!=NULL; ++dcnt )
1876 	dirs[dcnt] = utf82u_copy(path[dcnt]);
1877     dirs[dcnt] = NULL;
1878 }
1879