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