don't stat bookmark files on load, can make blender hang on slow networks (eg, the...
[blender-staging.git] / source / blender / editors / space_file / filesel.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version. 
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2008 Blender Foundation.
19  * All rights reserved.
20  *
21  * 
22  * Contributor(s): Blender Foundation
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/editors/space_file/filesel.c
28  *  \ingroup spfile
29  */
30
31
32 #include <string.h>
33 #include <stdio.h>
34 #include <math.h>
35
36 #include <sys/stat.h>
37 #include <sys/types.h>
38
39 /* path/file handeling stuff */
40 #ifdef WIN32
41 #  include <io.h>
42 #  include <direct.h>
43 #  include "BLI_winstuff.h"
44 #else
45 #  include <unistd.h>
46 #  include <sys/times.h>
47 #  include <dirent.h>
48 #  include <unistd.h>
49 #endif
50
51 #include "DNA_space_types.h"
52 #include "DNA_screen_types.h"
53 #include "DNA_userdef_types.h"
54
55 #include "MEM_guardedalloc.h"
56
57 #include "BLI_blenlib.h"
58 #include "BLI_linklist.h"
59 #include "BLI_dynstr.h"
60 #include "BLI_utildefines.h"
61
62 #include "BKE_context.h"
63 #include "BKE_global.h"
64 #include "BKE_main.h"
65
66 #include "BLF_api.h"
67
68
69 #include "ED_fileselect.h"
70
71 #include "WM_api.h"
72 #include "WM_types.h"
73
74
75 #include "RNA_access.h"
76
77 #include "UI_interface.h"
78 #include "UI_interface_icons.h"
79
80 #include "file_intern.h"
81 #include "filelist.h"
82
83 #if defined WIN32 && !defined _LIBC
84 # include "BLI_fnmatch.h" /* use fnmatch included in blenlib */
85 #else
86 # include <fnmatch.h>
87 #endif
88
89 FileSelectParams* ED_fileselect_get_params(struct SpaceFile *sfile)
90 {
91         if (!sfile->params) {
92                 ED_fileselect_set_params(sfile);
93         }
94         return sfile->params;
95 }
96
97 short ED_fileselect_set_params(SpaceFile *sfile)
98 {
99         FileSelectParams *params;
100         wmOperator *op = sfile->op;
101
102         /* create new parameters if necessary */
103         if (!sfile->params) {
104                 sfile->params= MEM_callocN(sizeof(FileSelectParams), "fileselparams");
105                 /* set path to most recently opened .blend */
106                 BLI_split_dirfile(G.main->name, sfile->params->dir, sfile->params->file, sizeof(sfile->params->dir), sizeof(sfile->params->file));
107                 sfile->params->filter_glob[0] = '\0';
108         }
109
110         params = sfile->params;
111
112         /* set the parameters from the operator, if it exists */
113         if (op) {
114                 const short is_files= (RNA_struct_find_property(op->ptr, "files") != NULL);
115                 const short is_filepath= (RNA_struct_find_property(op->ptr, "filepath") != NULL);
116                 const short is_filename= (RNA_struct_find_property(op->ptr, "filename") != NULL);
117                 const short is_directory= (RNA_struct_find_property(op->ptr, "directory") != NULL);
118                 const short is_relative_path= (RNA_struct_find_property(op->ptr, "relative_path") != NULL);
119
120                 BLI_strncpy(params->title, RNA_struct_ui_name(op->type->srna), sizeof(params->title));
121
122                 if (RNA_struct_find_property(op->ptr, "filemode"))
123                         params->type = RNA_int_get(op->ptr, "filemode");
124                 else
125                         params->type = FILE_SPECIAL;
126
127                 if (is_filepath && RNA_struct_property_is_set(op->ptr, "filepath")) {
128                         char name[FILE_MAX];
129                         RNA_string_get(op->ptr, "filepath", name);
130                         if (params->type == FILE_LOADLIB) {
131                                 BLI_strncpy(params->dir, name, sizeof(params->dir));
132                                 sfile->params->file[0]= '\0';
133                         }
134                         else {
135                                 BLI_split_dirfile(name, sfile->params->dir, sfile->params->file, sizeof(sfile->params->dir), sizeof(sfile->params->file));
136                         }
137                 }
138                 else {
139                         if (is_directory && RNA_struct_property_is_set(op->ptr, "directory")) {
140                                 RNA_string_get(op->ptr, "directory", params->dir);
141                                 sfile->params->file[0]= '\0';
142                         }
143
144                         if (is_filename && RNA_struct_property_is_set(op->ptr, "filename")) {
145                                 RNA_string_get(op->ptr, "filename", params->file);
146                         }
147                 }
148
149                 if (params->dir[0]) {
150                         BLI_cleanup_dir(G.main->name, params->dir);
151                         BLI_path_abs(params->dir, G.main->name);
152                 }
153
154                 if (is_directory==TRUE && is_filename==FALSE && is_filepath==FALSE && is_files==FALSE) {
155                         params->flag |= FILE_DIRSEL_ONLY;
156                 }
157                 else {
158                         params->flag &= ~FILE_DIRSEL_ONLY;
159                 }
160
161                 params->filter = 0;
162                 if (RNA_struct_find_property(op->ptr, "filter_blender"))
163                         params->filter |= RNA_boolean_get(op->ptr, "filter_blender") ? BLENDERFILE : 0;
164                 if (RNA_struct_find_property(op->ptr, "filter_image"))
165                         params->filter |= RNA_boolean_get(op->ptr, "filter_image") ? IMAGEFILE : 0;
166                 if (RNA_struct_find_property(op->ptr, "filter_movie"))
167                         params->filter |= RNA_boolean_get(op->ptr, "filter_movie") ? MOVIEFILE : 0;
168                 if (RNA_struct_find_property(op->ptr, "filter_text"))
169                         params->filter |= RNA_boolean_get(op->ptr, "filter_text") ? TEXTFILE : 0;
170                 if (RNA_struct_find_property(op->ptr, "filter_python"))
171                         params->filter |= RNA_boolean_get(op->ptr, "filter_python") ? PYSCRIPTFILE : 0;
172                 if (RNA_struct_find_property(op->ptr, "filter_font"))
173                         params->filter |= RNA_boolean_get(op->ptr, "filter_font") ? FTFONTFILE : 0;
174                 if (RNA_struct_find_property(op->ptr, "filter_sound"))
175                         params->filter |= RNA_boolean_get(op->ptr, "filter_sound") ? SOUNDFILE : 0;
176                 if (RNA_struct_find_property(op->ptr, "filter_text"))
177                         params->filter |= RNA_boolean_get(op->ptr, "filter_text") ? TEXTFILE : 0;
178                 if (RNA_struct_find_property(op->ptr, "filter_folder"))
179                         params->filter |= RNA_boolean_get(op->ptr, "filter_folder") ? FOLDERFILE : 0;
180                 if (RNA_struct_find_property(op->ptr, "filter_btx"))
181                         params->filter |= RNA_boolean_get(op->ptr, "filter_btx") ? BTXFILE : 0;
182                 if (RNA_struct_find_property(op->ptr, "filter_collada"))
183                         params->filter |= RNA_boolean_get(op->ptr, "filter_collada") ? COLLADAFILE : 0;
184                 if (RNA_struct_find_property(op->ptr, "filter_glob")) {
185                         RNA_string_get(op->ptr, "filter_glob", params->filter_glob);
186                         params->filter |= (OPERATORFILE|FOLDERFILE);
187                 }
188                 else {
189                         params->filter_glob[0] = '\0';
190                 }
191
192                 if (params->filter != 0) {
193                         if (U.uiflag & USER_FILTERFILEEXTS) {
194                                 params->flag |= FILE_FILTER;
195                         }
196                         else {
197                                 params->flag &= ~FILE_FILTER;
198                         }
199                 }
200
201                 if (U.uiflag & USER_HIDE_DOT) {
202                         params->flag |= FILE_HIDE_DOT;
203                 }
204                 else {
205                         params->flag &= ~FILE_HIDE_DOT;
206                 }
207                 
208
209                 if (params->type == FILE_LOADLIB) {
210                         params->flag |= RNA_boolean_get(op->ptr, "link") ? FILE_LINK : 0;
211                         params->flag |= RNA_boolean_get(op->ptr, "autoselect") ? FILE_AUTOSELECT : 0;
212                         params->flag |= RNA_boolean_get(op->ptr, "active_layer") ? FILE_ACTIVELAY : 0;
213                 }
214
215                 if (RNA_struct_find_property(op->ptr, "display_type"))
216                         params->display= RNA_enum_get(op->ptr, "display_type");
217
218                 if (params->display==FILE_DEFAULTDISPLAY) {
219                         if (U.uiflag & USER_SHOW_THUMBNAILS) {
220                                 if (params->filter & (IMAGEFILE|MOVIEFILE))
221                                         params->display= FILE_IMGDISPLAY;
222                                 else
223                                         params->display= FILE_SHORTDISPLAY;
224                         }
225                         else {
226                                 params->display= FILE_SHORTDISPLAY;
227                         }
228                 }
229
230                 if (is_relative_path) {
231                         if (!RNA_struct_property_is_set(op->ptr, "relative_path")) {
232                                 RNA_boolean_set(op->ptr, "relative_path", U.flag & USER_RELPATHS);
233                         }
234                 }
235         }
236         else {
237                 /* default values, if no operator */
238                 params->type = FILE_UNIX;
239                 params->flag |= FILE_HIDE_DOT;
240                 params->flag &= ~FILE_DIRSEL_ONLY;
241                 params->display = FILE_SHORTDISPLAY;
242                 params->filter = 0;
243                 params->filter_glob[0] = '\0';
244                 params->sort = FILE_SORT_ALPHA;
245         }
246
247
248         /* initialize the list with previous folders */
249         if (!sfile->folders_prev)
250                 sfile->folders_prev = folderlist_new();
251         folderlist_pushdir(sfile->folders_prev, sfile->params->dir);
252
253         /* switching thumbnails needs to recalc layout [#28809] */
254         if (sfile->layout) {
255                 sfile->layout->dirty= TRUE;
256         }
257
258         return 1;
259 }
260
261 void ED_fileselect_reset_params(SpaceFile *sfile)
262 {
263         sfile->params->type = FILE_UNIX;
264         sfile->params->flag = 0;
265         sfile->params->title[0] = '\0';
266 }
267
268 int ED_fileselect_layout_numfiles(FileLayout* layout, struct ARegion *ar)
269 {
270         int numfiles;
271
272         if (layout->flag & FILE_LAYOUT_HOR) {
273                 int width = (int)(ar->v2d.cur.xmax - ar->v2d.cur.xmin - 2*layout->tile_border_x);
274                 numfiles = (int)((float)width / (float)layout->tile_w + 0.5f);
275                 return numfiles*layout->rows;
276         }
277         else {
278                 int height = (int)(ar->v2d.cur.ymax - ar->v2d.cur.ymin - 2*layout->tile_border_y);
279                 numfiles = (int)((float)height/(float)layout->tile_h + 0.5f);
280                 return numfiles*layout->columns;
281         }
282 }
283
284 static int is_inside(int x, int y, int cols, int rows)
285 {
286         return ( (x >= 0) && (x<cols) && (y>=0) && (y<rows) );
287 }
288
289 FileSelection ED_fileselect_layout_offset_rect(FileLayout* layout, const rcti* rect)
290 {
291         int colmin, colmax, rowmin, rowmax;
292         FileSelection sel;
293         sel.first = sel.last = -1;
294
295         if (layout == NULL)
296                 return sel;
297         
298         colmin = (rect->xmin)/(layout->tile_w + 2*layout->tile_border_x);
299         rowmin = (rect->ymin)/(layout->tile_h + 2*layout->tile_border_y);
300         colmax = (rect->xmax)/(layout->tile_w + 2*layout->tile_border_x);
301         rowmax = (rect->ymax)/(layout->tile_h + 2*layout->tile_border_y);
302         
303         if ( is_inside(colmin, rowmin, layout->columns, layout->rows) || 
304                  is_inside(colmax, rowmax, layout->columns, layout->rows) ) {
305                 CLAMP(colmin, 0, layout->columns-1);
306                 CLAMP(rowmin, 0, layout->rows-1);
307                 CLAMP(colmax, 0, layout->columns-1);
308                 CLAMP(rowmax, 0, layout->rows-1);
309         } 
310         
311         if ((colmin > layout->columns-1) || (rowmin > layout->rows-1)) {
312                 sel.first = -1;
313         }
314         else {
315                 if (layout->flag & FILE_LAYOUT_HOR) 
316                         sel.first = layout->rows*colmin + rowmin;
317                 else
318                         sel.first = colmin + layout->columns*rowmin;
319         }
320         if ((colmax > layout->columns-1) || (rowmax > layout->rows-1)) {
321                 sel.last = -1;
322         }
323         else {
324                 if (layout->flag & FILE_LAYOUT_HOR) 
325                         sel.last = layout->rows*colmax + rowmax;
326                 else
327                         sel.last = colmax + layout->columns*rowmax;
328         }
329
330         return sel;
331 }
332
333 int ED_fileselect_layout_offset(FileLayout* layout, int x, int y)
334 {
335         int offsetx, offsety;
336         int active_file;
337
338         if (layout == NULL)
339                 return -1;
340         
341         offsetx = (x)/(layout->tile_w + 2*layout->tile_border_x);
342         offsety = (y)/(layout->tile_h + 2*layout->tile_border_y);
343         
344         if (offsetx > layout->columns - 1) return -1;
345         if (offsety > layout->rows - 1) return -1;
346         
347         if (layout->flag & FILE_LAYOUT_HOR) 
348                 active_file = layout->rows*offsetx + offsety;
349         else
350                 active_file = offsetx + layout->columns*offsety;
351         return active_file;
352 }
353
354 void ED_fileselect_layout_tilepos(FileLayout* layout, int tile, int *x, int *y)
355 {
356         if (layout->flag == FILE_LAYOUT_HOR) {
357                 *x = layout->tile_border_x + (tile/layout->rows)*(layout->tile_w+2*layout->tile_border_x);
358                 *y = layout->tile_border_y + (tile%layout->rows)*(layout->tile_h+2*layout->tile_border_y);
359         }
360         else {
361                 *x = layout->tile_border_x + ((tile)%layout->columns)*(layout->tile_w+2*layout->tile_border_x);
362                 *y = layout->tile_border_y + ((tile)/layout->columns)*(layout->tile_h+2*layout->tile_border_y);
363         }
364 }
365
366 /* Shorten a string to a given width w. 
367  * If front is set, shorten from the front,
368  * otherwise shorten from the end. */
369 float file_shorten_string(char* string, float w, int front)
370 {       
371         char temp[FILE_MAX];
372         short shortened = 0;
373         float sw = 0;
374         float pad = 0;
375
376         if (w <= 0) {
377                 *string = '\0';
378                 return 0.0;
379         }
380
381         sw = file_string_width(string);
382         if (front == 1) {
383                 char *s = string;
384                 BLI_strncpy(temp, "...", 4);
385                 pad = file_string_width(temp);
386                 while ((*s) && (sw+pad>w)) {
387                         s++;
388                         sw = file_string_width(s);
389                         shortened = 1;
390                 }
391                 if (shortened) {
392                         int slen = strlen(s);
393                         BLI_strncpy(temp+3, s, slen+1);
394                         temp[slen+4] = '\0';
395                         BLI_strncpy(string, temp, slen+4);
396                 }
397         }
398         else {
399                 char *s = string;
400                 while (sw>w) {
401                         int slen = strlen(string);
402                         string[slen-1] = '\0';
403                         sw = file_string_width(s);
404                         shortened = 1;
405                 }
406
407                 if (shortened) {
408                         int slen = strlen(string);
409                         if (slen > 3) {
410                                 BLI_strncpy(string+slen-3, "...", 4);
411                         }
412                 }
413         }
414         
415         return sw;
416 }
417
418 float file_string_width(const char* str)
419 {
420         uiStyle *style= UI_GetStyle();
421         uiStyleFontSet(&style->widget);
422         return BLF_width(style->widget.uifont_id, str);
423 }
424
425 float file_font_pointsize(void)
426 {
427 #if 0
428         float s;
429         char tmp[2] = "X";
430         uiStyle *style= UI_GetStyle();
431         uiStyleFontSet(&style->widget);
432         s = BLF_height(style->widget.uifont_id, tmp);
433         return style->widget.points;
434 #else
435         uiStyle *style= UI_GetStyle();
436         uiStyleFontSet(&style->widget);
437         return style->widget.points * UI_DPI_FAC;
438 #endif
439 }
440
441 static void column_widths(struct FileList* files, struct FileLayout* layout)
442 {
443         int i;
444         int numfiles = filelist_numfiles(files);
445
446         for (i=0; i<MAX_FILE_COLUMN; ++i) {
447                 layout->column_widths[i] = 0;
448         }
449
450         for (i=0; (i < numfiles); ++i) {
451                 struct direntry* file = filelist_file(files, i);        
452                 if (file) {
453                         float len;
454                         len = file_string_width(file->relname);
455                         if (len > layout->column_widths[COLUMN_NAME]) layout->column_widths[COLUMN_NAME] = len;
456                         len = file_string_width(file->date);
457                         if (len > layout->column_widths[COLUMN_DATE]) layout->column_widths[COLUMN_DATE] = len;
458                         len = file_string_width(file->time);
459                         if (len > layout->column_widths[COLUMN_TIME]) layout->column_widths[COLUMN_TIME] = len;
460                         len = file_string_width(file->size);
461                         if (len > layout->column_widths[COLUMN_SIZE]) layout->column_widths[COLUMN_SIZE] = len;
462                         len = file_string_width(file->mode1);
463                         if (len > layout->column_widths[COLUMN_MODE1]) layout->column_widths[COLUMN_MODE1] = len;
464                         len = file_string_width(file->mode2);
465                         if (len > layout->column_widths[COLUMN_MODE2]) layout->column_widths[COLUMN_MODE2] = len;
466                         len = file_string_width(file->mode3);
467                         if (len > layout->column_widths[COLUMN_MODE3]) layout->column_widths[COLUMN_MODE3] = len;
468                         len = file_string_width(file->owner);
469                         if (len > layout->column_widths[COLUMN_OWNER]) layout->column_widths[COLUMN_OWNER] = len;
470                 }
471         }
472 }
473
474 void ED_fileselect_init_layout(struct SpaceFile *sfile, struct ARegion *ar)
475 {
476         FileSelectParams *params = ED_fileselect_get_params(sfile);
477         FileLayout *layout= NULL;
478         View2D *v2d= &ar->v2d;
479         int maxlen = 0;
480         int numfiles;
481         int textheight;
482
483         if (sfile->layout == NULL) {
484                 sfile->layout = MEM_callocN(sizeof(struct FileLayout), "file_layout");
485                 sfile->layout->dirty = TRUE;
486         }
487         else if (sfile->layout->dirty == FALSE) {
488                 return;
489         }
490
491         numfiles = filelist_numfiles(sfile->files);
492         textheight = (int)file_font_pointsize();
493         layout = sfile->layout;
494         layout->textheight = textheight;
495
496         if (params->display == FILE_IMGDISPLAY) {
497                 layout->prv_w = 96;
498                 layout->prv_h = 96;
499                 layout->tile_border_x = 6;
500                 layout->tile_border_y = 6;
501                 layout->prv_border_x = 6;
502                 layout->prv_border_y = 6;
503                 layout->tile_w = layout->prv_w + 2*layout->prv_border_x;
504                 layout->tile_h = layout->prv_h + 2*layout->prv_border_y + textheight;
505                 layout->width= (int)(v2d->cur.xmax - v2d->cur.xmin - 2*layout->tile_border_x);
506                 layout->columns= layout->width / (layout->tile_w + 2*layout->tile_border_x);
507                 if (layout->columns > 0)
508                         layout->rows= numfiles/layout->columns + 1; // XXX dirty, modulo is zero
509                 else {
510                         layout->columns = 1;
511                         layout->rows= numfiles + 1; // XXX dirty, modulo is zero
512                 }
513                 layout->height= sfile->layout->rows*(layout->tile_h+2*layout->tile_border_y) + layout->tile_border_y*2;
514                 layout->flag = FILE_LAYOUT_VER;
515         }
516         else {
517                 layout->prv_w = 0;
518                 layout->prv_h = 0;
519                 layout->tile_border_x = 8;
520                 layout->tile_border_y = 2;
521                 layout->prv_border_x = 0;
522                 layout->prv_border_y = 0;
523                 layout->tile_h = textheight*3/2;
524                 layout->height= (int)(v2d->cur.ymax - v2d->cur.ymin - 2*layout->tile_border_y);
525                 layout->rows = layout->height / (layout->tile_h + 2*layout->tile_border_y);
526
527                 column_widths(sfile->files, layout);
528
529                 if (params->display == FILE_SHORTDISPLAY) {
530                         maxlen = ICON_DEFAULT_WIDTH_SCALE + 4 +
531                                          (int)layout->column_widths[COLUMN_NAME] + 12 +
532                                          (int)layout->column_widths[COLUMN_SIZE] + 12;
533                 }
534                 else {
535                         maxlen = ICON_DEFAULT_WIDTH_SCALE + 4 +
536                                          (int)layout->column_widths[COLUMN_NAME] + 12 +
537 #ifndef WIN32
538                                          (int)layout->column_widths[COLUMN_MODE1] + 12 +
539                                          (int)layout->column_widths[COLUMN_MODE2] + 12 +
540                                          (int)layout->column_widths[COLUMN_MODE3] + 12 +
541                                          (int)layout->column_widths[COLUMN_OWNER] + 12 +
542 #endif
543                                          (int)layout->column_widths[COLUMN_DATE] + 12 +
544                                          (int)layout->column_widths[COLUMN_TIME] + 12 +
545                                          (int)layout->column_widths[COLUMN_SIZE] + 12;
546
547                 }
548                 layout->tile_w = maxlen;
549                 if (layout->rows > 0)
550                         layout->columns = numfiles/layout->rows + 1; // XXX dirty, modulo is zero
551                 else {
552                         layout->rows = 1;
553                         layout->columns = numfiles + 1; // XXX dirty, modulo is zero
554                 }
555                 layout->width = sfile->layout->columns * (layout->tile_w + 2*layout->tile_border_x) + layout->tile_border_x*2;
556                 layout->flag = FILE_LAYOUT_HOR;
557         }
558         layout->dirty= FALSE;
559 }
560
561 FileLayout* ED_fileselect_get_layout(struct SpaceFile *sfile, struct ARegion *ar)
562 {
563         if (!sfile->layout) {
564                 ED_fileselect_init_layout(sfile, ar);
565         }
566         return sfile->layout;
567 }
568
569 void file_change_dir(bContext *C, int checkdir)
570 {
571         SpaceFile *sfile= CTX_wm_space_file(C);
572
573         if (sfile->params) {
574
575                 ED_fileselect_clear(C, sfile);
576
577                 if (checkdir && BLI_is_dir(sfile->params->dir)==0) {
578                         BLI_strncpy(sfile->params->dir, filelist_dir(sfile->files), sizeof(sfile->params->dir));
579                         /* could return but just refresh the current dir */
580                 }
581                 filelist_setdir(sfile->files, sfile->params->dir);
582                 
583                 if (folderlist_clear_next(sfile))
584                         folderlist_free(sfile->folders_next);
585
586                 folderlist_pushdir(sfile->folders_prev, sfile->params->dir);
587
588         }
589 }
590
591 int file_select_match(struct SpaceFile *sfile, const char *pattern)
592 {
593         int match = 0;
594         if (strchr(pattern, '*') || strchr(pattern, '?') || strchr(pattern, '[')) {
595                 int i;
596                 struct direntry *file;
597                 int n = filelist_numfiles(sfile->files);
598
599                 for (i = 0; i < n; i++) {
600                         file = filelist_file(sfile->files, i);
601                         if (fnmatch(pattern, file->relname, 0) == 0) {
602                                 file->selflag |= SELECTED_FILE;
603                                 match = 1;
604                         }
605                 }
606         }
607         return match;
608 }
609
610 void autocomplete_directory(struct bContext *C, char *str, void *UNUSED(arg_v))
611 {
612         SpaceFile *sfile= CTX_wm_space_file(C);
613
614         /* search if str matches the beginning of name */
615         if (str[0] && sfile->files) {
616                 char dirname[FILE_MAX];
617
618                 DIR *dir;
619                 struct dirent *de;
620                 
621                 BLI_split_dir_part(str, dirname, sizeof(dirname));
622
623                 dir = opendir(dirname);
624
625                 if (dir) {
626                         AutoComplete *autocpl= autocomplete_begin(str, FILE_MAX);
627
628                         while ((de = readdir(dir)) != NULL) {
629                                 if (strcmp(".", de->d_name)==0 || strcmp("..", de->d_name)==0) {
630                                         /* pass */
631                                 }
632                                 else {
633                                         char path[FILE_MAX];
634                                         struct stat status;
635                                         
636                                         BLI_join_dirfile(path, sizeof(path), dirname, de->d_name);
637
638                                         if (stat(path, &status) == 0) {
639                                                 if (S_ISDIR(status.st_mode)) { /* is subdir */
640                                                         autocomplete_do_name(autocpl, path);
641                                                 }
642                                         }
643                                 }
644                         }
645                         closedir(dir);
646
647                         autocomplete_end(autocpl, str);
648                         if (BLI_exists(str)) {
649                                 BLI_add_slash(str);
650                         }
651                         else {
652                                 BLI_strncpy(sfile->params->dir, str, sizeof(sfile->params->dir));
653                         }
654                 }
655         }
656 }
657
658 void autocomplete_file(struct bContext *C, char *str, void *UNUSED(arg_v))
659 {
660         SpaceFile *sfile= CTX_wm_space_file(C);
661
662         /* search if str matches the beginning of name */
663         if (str[0] && sfile->files) {
664                 AutoComplete *autocpl= autocomplete_begin(str, FILE_MAX);
665                 int nentries = filelist_numfiles(sfile->files);
666                 int i;
667
668                 for (i= 0; i<nentries; ++i) {
669                         struct direntry* file = filelist_file(sfile->files, i);
670                         if (file && S_ISREG(file->type)) {
671                                 autocomplete_do_name(autocpl, file->relname);
672                         }
673                 }
674                 autocomplete_end(autocpl, str);
675         }
676 }
677
678 void ED_fileselect_clear(struct bContext *C, struct SpaceFile *sfile)
679 {
680         /* only NULL in rare cases - [#29734] */
681         if (sfile->files) {
682                 thumbnails_stop(sfile->files, C);
683                 filelist_freelib(sfile->files);
684                 filelist_free(sfile->files);
685         }
686
687         sfile->params->active_file = -1;
688         WM_event_add_notifier(C, NC_SPACE|ND_SPACE_FILE_LIST, NULL);
689 }
690
691 void ED_fileselect_exit(struct bContext *C, struct SpaceFile *sfile)
692 {
693         if (!sfile) return;
694         if (sfile->op) {
695                 WM_event_fileselect_event(C, sfile->op, EVT_FILESELECT_EXTERNAL_CANCEL);
696                 sfile->op = NULL;
697         }
698
699         folderlist_free(sfile->folders_prev);
700         folderlist_free(sfile->folders_next);
701         
702         if (sfile->files) {
703                 ED_fileselect_clear(C, sfile);
704                 MEM_freeN(sfile->files);
705                 sfile->files= NULL;
706         }
707
708 }