2.5 filebrowser
[blender-staging.git] / source / blender / editors / space_file / file_ops.c
1 /**
2  * $Id:
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2008 Blender Foundation.
21  * All rights reserved.
22  *
23  * 
24  * Contributor(s): Andrea Weikert (c) 2008 Blender Foundation
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 #include "BKE_context.h"
30 #include "BKE_screen.h"
31 #include "BKE_global.h"
32
33 #include "BLI_blenlib.h"
34 #include "BLI_storage_types.h"
35 #ifdef WIN32
36 #include "BLI_winstuff.h"
37 #endif
38 #include "DNA_space_types.h"
39 #include "DNA_userdef_types.h"
40
41 #include "ED_space_api.h"
42 #include "ED_screen.h"
43 #include "ED_fileselect.h"
44
45 #include "RNA_access.h"
46 #include "RNA_define.h"
47
48 #include "UI_interface.h"
49 #include "UI_view2d.h"
50
51 #include "WM_api.h"
52 #include "WM_types.h"
53
54 #include "file_intern.h"
55 #include "filelist.h"
56 #include "fsmenu.h"
57
58 #include <stdlib.h>
59 #include <string.h>
60 #include <stdio.h>
61
62 /* for events */
63 #define NOTACTIVE                       0
64 #define ACTIVATE                        1
65 #define INACTIVATE                      2
66
67 /* ---------- FILE SELECTION ------------ */
68
69 static int find_file_mouse_hor(SpaceFile *sfile, struct ARegion* ar, short x, short y)
70 {
71         float fx,fy;
72         int active_file = -1;
73         int numfiles = filelist_numfiles(sfile->files);
74         View2D* v2d = &ar->v2d;
75
76         UI_view2d_region_to_view(v2d, x, y, &fx, &fy);
77
78         active_file = ED_fileselect_layout_offset(sfile->layout, v2d->tot.xmin + fx, v2d->tot.ymax - fy);
79
80         printf("FINDFILE %d\n", active_file);
81         if ( (active_file < 0) || (active_file >= numfiles) )
82         {
83                 active_file = -1;
84         }
85         return active_file;
86 }
87
88
89 static int find_file_mouse_vert(SpaceFile *sfile, struct ARegion* ar, short x, short y)
90 {
91         int offsetx, offsety;
92         float fx,fy;
93         int active_file = -1;
94         int numfiles = filelist_numfiles(sfile->files);
95         View2D* v2d = &ar->v2d;
96
97         UI_view2d_region_to_view(v2d, x, y, &fx, &fy);
98         
99         active_file = ED_fileselect_layout_offset(sfile->layout, v2d->tot.xmin + fx, v2d->tot.ymax - fy);
100
101         if ( (active_file < 0) || (active_file >= numfiles) )
102         {
103                 active_file = -1;
104         }
105         return active_file;
106 }
107
108 static void file_deselect_all(SpaceFile* sfile)
109 {
110         int numfiles = filelist_numfiles(sfile->files);
111         int i;
112
113         for ( i=0; i < numfiles; ++i) {
114                 struct direntry* file = filelist_file(sfile->files, i);
115                 if (file && (file->flags & ACTIVE)) {
116                         file->flags &= ~ACTIVE;
117                 }
118         }
119 }
120
121 static void file_select(SpaceFile* sfile, FileSelectParams* params, ARegion* ar, const rcti* rect, short val)
122 {
123         int first_file = -1;
124         int last_file = -1;
125         int act_file;
126         short selecting = (val == LEFTMOUSE);
127
128         int numfiles = filelist_numfiles(sfile->files);
129
130         params->selstate = NOTACTIVE;
131         if  ( (params->display == FILE_IMGDISPLAY) || (params->display == FILE_LONGDISPLAY) ) {
132                 first_file = find_file_mouse_hor(sfile, ar, rect->xmin, rect->ymax);
133                 last_file = find_file_mouse_hor(sfile, ar, rect->xmax, rect->ymin);
134         } else {
135                 first_file = find_file_mouse_vert(sfile, ar, rect->xmin, rect->ymax);
136                 last_file = find_file_mouse_vert(sfile, ar, rect->xmax, rect->ymin);
137         }
138         
139         /* select all valid files between first and last indicated */
140         if ( (first_file >= 0) && (first_file < numfiles) && (last_file >= 0) && (last_file < numfiles) ) {
141                 for (act_file = first_file; act_file <= last_file; act_file++) {
142                         struct direntry* file = filelist_file(sfile->files, act_file);
143                         if (selecting) 
144                                 file->flags |= ACTIVE;
145                         else
146                                 file->flags &= ~ACTIVE;
147                 }
148         }
149         
150         printf("Selecting %d %d\n", first_file, last_file);
151
152         /* make the last file active */
153         if (last_file >= 0 && last_file < numfiles) {
154                 struct direntry* file = filelist_file(sfile->files, last_file);
155                 params->active_file = last_file;
156
157                 if(file && S_ISDIR(file->type)) {
158                         /* the path is too long and we are not going up! */
159                         if (strcmp(file->relname, ".") &&
160                                 strcmp(file->relname, "..") &&
161                                 strlen(params->dir) + strlen(file->relname) >= FILE_MAX ) 
162                         {
163                                 // XXX error("Path too long, cannot enter this directory");
164                         } else {
165                                 if (strcmp(file->relname, "..")==0) {
166                                         /* avoids /../../ */
167                                         BLI_parent_dir(params->dir);
168                                 } else {
169                                         strcat(params->dir, file->relname);
170                                         strcat(params->dir,"/");
171                                         params->file[0] = '\0';
172                                         BLI_cleanup_dir(G.sce, params->dir);
173                                 }
174                                 filelist_setdir(sfile->files, params->dir);
175                                 filelist_free(sfile->files);
176                                 params->active_file = -1;
177                         }
178                 }
179                 else if (file)
180                 {
181                         if (file->relname) {
182                                 BLI_strncpy(params->file, file->relname, FILE_MAXFILE);
183                                 /* XXX
184                                 if(event==MIDDLEMOUSE && filelist_gettype(sfile->files)) 
185                                         imasel_execute(sfile);
186                                 */
187                         }
188                         
189                 }       
190                 /* XXX
191                 if(BIF_filelist_gettype(sfile->files)==FILE_MAIN) {
192                         active_imasel_object(sfile);
193                 }
194                 */
195         }
196 }
197
198
199
200 static int file_border_select_exec(bContext *C, wmOperator *op)
201 {
202         ARegion *ar= CTX_wm_region(C);
203         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
204         short val;
205         rcti rect;
206
207         val= RNA_int_get(op->ptr, "event_type");
208         rect.xmin= RNA_int_get(op->ptr, "xmin");
209         rect.ymin= RNA_int_get(op->ptr, "ymin");
210         rect.xmax= RNA_int_get(op->ptr, "xmax");
211         rect.ymax= RNA_int_get(op->ptr, "ymax");
212
213         file_select(sfile, sfile->params, ar, &rect, val );
214         WM_event_add_notifier(C, NC_WINDOW, NULL);
215         return OPERATOR_FINISHED;
216 }
217
218 void FILE_OT_border_select(wmOperatorType *ot)
219 {
220         /* identifiers */
221         ot->name= "Activate/Select File";
222         ot->idname= "FILE_OT_border_select";
223         
224         /* api callbacks */
225         ot->invoke= WM_border_select_invoke;
226         ot->exec= file_border_select_exec;
227         ot->modal= WM_border_select_modal;
228
229         /* rna */
230         RNA_def_int(ot->srna, "event_type", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX);
231         RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
232         RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
233         RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
234         RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
235
236         ot->poll= ED_operator_file_active;
237 }
238
239 static int file_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
240 {
241         ARegion *ar= CTX_wm_region(C);
242         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
243         short val;
244         rcti rect;
245
246         rect.xmin = rect.xmax = event->x - ar->winrct.xmin;
247         rect.ymin = rect.ymax = event->y - ar->winrct.ymin;
248         val = event->val;
249
250         /* single select, deselect all selected first */
251         file_deselect_all(sfile);
252         file_select(sfile, sfile->params, ar, &rect, val );
253         WM_event_add_notifier(C, NC_WINDOW, NULL);
254         return OPERATOR_FINISHED;
255 }
256
257 void FILE_OT_select(wmOperatorType *ot)
258 {
259         /* identifiers */
260         ot->name= "Activate/Select File";
261         ot->idname= "FILE_OT_select";
262         
263         /* api callbacks */
264         ot->invoke= file_select_invoke;
265
266         /* rna */
267
268         ot->poll= ED_operator_file_active;
269 }
270
271 static int file_select_all_invoke(bContext *C, wmOperator *op, wmEvent *event)
272 {
273         ScrArea *sa= CTX_wm_area(C);
274         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
275         int numfiles = filelist_numfiles(sfile->files);
276         int i;
277         int select = 1;
278
279         /* if any file is selected, deselect all first */
280         for ( i=0; i < numfiles; ++i) {
281                 struct direntry* file = filelist_file(sfile->files, i);
282                 if (file && (file->flags & ACTIVE)) {
283                         file->flags &= ~ACTIVE;
284                         select = 0;
285                         ED_area_tag_redraw(sa);
286                 }
287         }
288         /* select all only if previously no file was selected */
289         if (select) {
290                 for ( i=0; i < numfiles; ++i) {
291                         struct direntry* file = filelist_file(sfile->files, i);
292                         if(file && !S_ISDIR(file->type)) {
293                                 file->flags |= ACTIVE;
294                                 ED_area_tag_redraw(sa);
295                         }
296                 }
297         }
298         return OPERATOR_FINISHED;
299 }
300
301 void FILE_OT_select_all(wmOperatorType *ot)
302 {
303         /* identifiers */
304         ot->name= "Select/Deselect all files";
305         ot->idname= "FILE_OT_select_all";
306         
307         /* api callbacks */
308         ot->invoke= file_select_all_invoke;
309
310         /* rna */
311
312         ot->poll= ED_operator_file_active;
313 }
314
315 /* ---------- BOOKMARKS ----------- */
316
317 static void set_active_bookmark(FileSelectParams* params, struct ARegion* ar, short x, short y)
318 {
319         int nentries = fsmenu_get_nentries();
320         float fx, fy;
321         short posy;
322
323         UI_view2d_region_to_view(&ar->v2d, x, y, &fx, &fy);
324
325         posy = ar->v2d.cur.ymax - 2*TILE_BORDER_Y - fy;
326         params->active_bookmark = ((float)posy / (U.fontsize*3.0f/2.0f));
327         if (params->active_bookmark < 0 || params->active_bookmark > nentries) {
328                 params->active_bookmark = -1;
329         }
330 }
331
332 static void file_select_bookmark(SpaceFile* sfile, ARegion* ar, short x, short y)
333 {
334         if (BLI_in_rcti(&ar->v2d.mask, x, y)) {
335                 char *selected;
336                 set_active_bookmark(sfile->params, ar, x, y);
337                 selected= fsmenu_get_entry(sfile->params->active_bookmark);                     
338                 /* which string */
339                 if (selected) {
340                         FileSelectParams* params = sfile->params;
341                         BLI_strncpy(params->dir, selected, sizeof(params->dir));
342                         BLI_cleanup_dir(G.sce, params->dir);
343                         filelist_free(sfile->files);    
344                         filelist_setdir(sfile->files, params->dir);
345                         params->file[0] = '\0';                 
346                         params->active_file = -1;
347                 }
348         }
349 }
350
351 static int bookmark_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
352 {
353         ScrArea *sa= CTX_wm_area(C);
354         ARegion *ar= CTX_wm_region(C);  
355         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
356
357         short x, y;
358
359         x = event->x - ar->winrct.xmin;
360         y = event->y - ar->winrct.ymin;
361
362         file_select_bookmark(sfile, ar, x, y);
363         ED_area_tag_redraw(sa);
364         return OPERATOR_FINISHED;
365 }
366
367 void FILE_OT_select_bookmark(wmOperatorType *ot)
368 {
369         /* identifiers */
370         ot->name= "Select Directory";
371         ot->idname= "FILE_OT_select_bookmark";
372         
373         /* api callbacks */
374         ot->invoke= bookmark_select_invoke;
375         ot->poll= ED_operator_file_active;
376 }
377
378 static int loadimages_invoke(bContext *C, wmOperator *op, wmEvent *event)
379 {
380         ScrArea *sa= CTX_wm_area(C);
381         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
382         if (sfile->files) {
383                 filelist_loadimage_timer(sfile->files);
384                 if (filelist_changed(sfile->files)) {
385                         ED_area_tag_redraw(sa);
386                 }
387         }
388
389         return OPERATOR_FINISHED;
390 }
391
392 void FILE_OT_loadimages(wmOperatorType *ot)
393 {
394         
395         /* identifiers */
396         ot->name= "Load Images";
397         ot->idname= "FILE_OT_loadimages";
398         
399         /* api callbacks */
400         ot->invoke= loadimages_invoke;
401         
402         ot->poll= ED_operator_file_active;
403 }
404
405 int file_hilight_set(SpaceFile *sfile, ARegion *ar, int mx, int my)
406 {
407         FileSelectParams* params;
408         int numfiles, actfile;
409         
410         if(sfile==NULL || sfile->files==NULL) return 0;
411         
412         numfiles = filelist_numfiles(sfile->files);
413         params = ED_fileselect_get_params(sfile);
414         
415         if ( (params->display == FILE_IMGDISPLAY) || (params->display == FILE_LONGDISPLAY)) {
416                 actfile = find_file_mouse_hor(sfile, ar, mx , my);
417         } else {
418                 actfile = find_file_mouse_vert(sfile, ar, mx, my);
419         }
420         
421         if (actfile >= 0 && actfile < numfiles ) {
422                 params->active_file=actfile;
423                 return 1;
424         }
425         return 0;
426 }
427
428 static int file_highlight_invoke(bContext *C, wmOperator *op, wmEvent *event)
429 {
430         ARegion *ar= CTX_wm_region(C);
431         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
432         
433         if( file_hilight_set(sfile, ar, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin)) {
434                 ED_area_tag_redraw(CTX_wm_area(C));
435         }
436         
437         return OPERATOR_FINISHED;
438 }
439
440 void FILE_OT_highlight(struct wmOperatorType *ot)
441 {
442         /* identifiers */
443         ot->name= "Highlight File";
444         ot->idname= "FILE_OT_highlight";
445         
446         /* api callbacks */
447         ot->invoke= file_highlight_invoke;
448         ot->poll= ED_operator_file_active;
449 }
450
451 int file_cancel_exec(bContext *C, wmOperator *unused)
452 {
453         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
454         
455         WM_event_fileselect_event(C, sfile->op, EVT_FILESELECT_CANCEL);
456         sfile->op = NULL;
457         
458         return OPERATOR_FINISHED;
459 }
460
461 void FILE_OT_cancel(struct wmOperatorType *ot)
462 {
463         /* identifiers */
464         ot->name= "Cancel File Load";
465         ot->idname= "FILE_OT_cancel";
466         
467         /* api callbacks */
468         ot->exec= file_cancel_exec;
469         ot->poll= ED_operator_file_active;
470 }
471
472 /* sends events now, so things get handled on windowqueue level */
473 int file_exec(bContext *C, wmOperator *unused)
474 {
475         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
476         char name[FILE_MAX];
477         
478         if(sfile->op) {
479                 wmOperator *op= sfile->op;
480                 
481                 sfile->op = NULL;
482                 BLI_strncpy(name, sfile->params->dir, sizeof(name));
483                 strcat(name, sfile->params->file);
484                 RNA_string_set(op->ptr, "filename", name);
485                 
486                 WM_event_fileselect_event(C, op, EVT_FILESELECT_EXEC);
487         }
488                                 
489         return OPERATOR_FINISHED;
490 }
491
492 void FILE_OT_exec(struct wmOperatorType *ot)
493 {
494         /* identifiers */
495         ot->name= "Execute File Window";
496         ot->idname= "FILE_OT_exec";
497         
498         /* api callbacks */
499         ot->exec= file_exec;
500         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
501 }
502
503
504 int file_parent_exec(bContext *C, wmOperator *unused)
505 {
506         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
507         
508         if(sfile->params) {
509                 BLI_parent_dir(sfile->params->dir);
510                 filelist_setdir(sfile->files, sfile->params->dir);
511                 filelist_free(sfile->files);
512                 sfile->params->active_file = -1;
513         }               
514         ED_area_tag_redraw(CTX_wm_area(C));
515
516         return OPERATOR_FINISHED;
517
518 }
519
520
521 void FILE_OT_parent(struct wmOperatorType *ot)
522 {
523         /* identifiers */
524         ot->name= "Parent File";
525         ot->idname= "FILE_OT_parent";
526         
527         /* api callbacks */
528         ot->exec= file_parent_exec;
529         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
530 }
531
532 struct ARegion *file_buttons_region(struct ScrArea *sa)
533 {
534         ARegion *ar, *arnew;
535         
536         for(ar= sa->regionbase.first; ar; ar= ar->next)
537                 if(ar->regiontype==RGN_TYPE_CHANNELS)
538                         return ar;
539         return NULL;
540 }
541
542 int file_bookmark_toggle_exec(bContext *C, wmOperator *unused)
543 {
544         ScrArea *sa= CTX_wm_area(C);
545         ARegion *ar= file_buttons_region(sa);
546         
547         if(ar) {
548                 ar->flag ^= RGN_FLAG_HIDDEN;
549                 ar->v2d.flag &= ~V2D_IS_INITIALISED; /* XXX should become hide/unhide api? */
550                 
551                 ED_area_initialize(CTX_wm_manager(C), CTX_wm_window(C), sa);
552                 ED_area_tag_redraw(sa);
553         }
554         return OPERATOR_FINISHED;
555 }
556
557 void FILE_OT_bookmark_toggle(struct wmOperatorType *ot)
558 {
559         /* identifiers */
560         ot->name= "Toggle Bookmarks";
561         ot->idname= "FILE_OT_bookmark_toggle";
562         
563         /* api callbacks */
564         ot->exec= file_bookmark_toggle_exec;
565         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
566 }