* More 2.5 UI tweaks - text/controls/file browser
[blender.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         posy -= U.fontsize*2.0f;        /* header */
327         
328         params->active_bookmark = ((float)posy / (U.fontsize*2.0f));
329         if (params->active_bookmark < 0 || params->active_bookmark > nentries) {
330                 params->active_bookmark = -1;
331         }
332 }
333
334 static void file_select_bookmark(SpaceFile* sfile, ARegion* ar, short x, short y)
335 {
336         if (BLI_in_rcti(&ar->v2d.mask, x, y)) {
337                 char *selected;
338                 set_active_bookmark(sfile->params, ar, x, y);
339                 selected= fsmenu_get_entry(sfile->params->active_bookmark);                     
340                 /* which string */
341                 if (selected) {
342                         FileSelectParams* params = sfile->params;
343                         BLI_strncpy(params->dir, selected, sizeof(params->dir));
344                         BLI_cleanup_dir(G.sce, params->dir);
345                         filelist_free(sfile->files);    
346                         filelist_setdir(sfile->files, params->dir);
347                         params->file[0] = '\0';                 
348                         params->active_file = -1;
349                 }
350         }
351 }
352
353 static int bookmark_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
354 {
355         ScrArea *sa= CTX_wm_area(C);
356         ARegion *ar= CTX_wm_region(C);  
357         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
358
359         short x, y;
360
361         x = event->x - ar->winrct.xmin;
362         y = event->y - ar->winrct.ymin;
363
364         file_select_bookmark(sfile, ar, x, y);
365         ED_area_tag_redraw(sa);
366         return OPERATOR_FINISHED;
367 }
368
369 void FILE_OT_select_bookmark(wmOperatorType *ot)
370 {
371         /* identifiers */
372         ot->name= "Select Directory";
373         ot->idname= "FILE_OT_select_bookmark";
374         
375         /* api callbacks */
376         ot->invoke= bookmark_select_invoke;
377         ot->poll= ED_operator_file_active;
378 }
379
380 static int loadimages_invoke(bContext *C, wmOperator *op, wmEvent *event)
381 {
382         ScrArea *sa= CTX_wm_area(C);
383         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
384         if (sfile->files) {
385                 filelist_loadimage_timer(sfile->files);
386                 if (filelist_changed(sfile->files)) {
387                         ED_area_tag_redraw(sa);
388                 }
389         }
390
391         return OPERATOR_FINISHED;
392 }
393
394 void FILE_OT_loadimages(wmOperatorType *ot)
395 {
396         
397         /* identifiers */
398         ot->name= "Load Images";
399         ot->idname= "FILE_OT_loadimages";
400         
401         /* api callbacks */
402         ot->invoke= loadimages_invoke;
403         
404         ot->poll= ED_operator_file_active;
405 }
406
407 int file_hilight_set(SpaceFile *sfile, ARegion *ar, int mx, int my)
408 {
409         FileSelectParams* params;
410         int numfiles, actfile;
411         
412         if(sfile==NULL || sfile->files==NULL) return 0;
413         
414         numfiles = filelist_numfiles(sfile->files);
415         params = ED_fileselect_get_params(sfile);
416         
417         if ( (params->display == FILE_IMGDISPLAY) || (params->display == FILE_LONGDISPLAY)) {
418                 actfile = find_file_mouse_hor(sfile, ar, mx , my);
419         } else {
420                 actfile = find_file_mouse_vert(sfile, ar, mx, my);
421         }
422         
423         if (actfile >= 0 && actfile < numfiles ) {
424                 params->active_file=actfile;
425                 return 1;
426         }
427         return 0;
428 }
429
430 static int file_highlight_invoke(bContext *C, wmOperator *op, wmEvent *event)
431 {
432         ARegion *ar= CTX_wm_region(C);
433         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
434         
435         if( file_hilight_set(sfile, ar, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin)) {
436                 ED_area_tag_redraw(CTX_wm_area(C));
437         }
438         
439         return OPERATOR_FINISHED;
440 }
441
442 void FILE_OT_highlight(struct wmOperatorType *ot)
443 {
444         /* identifiers */
445         ot->name= "Highlight File";
446         ot->idname= "FILE_OT_highlight";
447         
448         /* api callbacks */
449         ot->invoke= file_highlight_invoke;
450         ot->poll= ED_operator_file_active;
451 }
452
453 int file_cancel_exec(bContext *C, wmOperator *unused)
454 {
455         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
456         
457         WM_event_fileselect_event(C, sfile->op, EVT_FILESELECT_CANCEL);
458         sfile->op = NULL;
459         
460         return OPERATOR_FINISHED;
461 }
462
463 void FILE_OT_cancel(struct wmOperatorType *ot)
464 {
465         /* identifiers */
466         ot->name= "Cancel File Load";
467         ot->idname= "FILE_OT_cancel";
468         
469         /* api callbacks */
470         ot->exec= file_cancel_exec;
471         ot->poll= ED_operator_file_active;
472 }
473
474 /* sends events now, so things get handled on windowqueue level */
475 int file_exec(bContext *C, wmOperator *unused)
476 {
477         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
478         char name[FILE_MAX];
479         
480         if(sfile->op) {
481                 wmOperator *op= sfile->op;
482                 
483                 sfile->op = NULL;
484                 BLI_strncpy(name, sfile->params->dir, sizeof(name));
485                 strcat(name, sfile->params->file);
486                 RNA_string_set(op->ptr, "filename", name);
487                 
488                 WM_event_fileselect_event(C, op, EVT_FILESELECT_EXEC);
489         }
490                                 
491         return OPERATOR_FINISHED;
492 }
493
494 void FILE_OT_exec(struct wmOperatorType *ot)
495 {
496         /* identifiers */
497         ot->name= "Execute File Window";
498         ot->idname= "FILE_OT_exec";
499         
500         /* api callbacks */
501         ot->exec= file_exec;
502         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
503 }
504
505
506 int file_parent_exec(bContext *C, wmOperator *unused)
507 {
508         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
509         
510         if(sfile->params) {
511                 BLI_parent_dir(sfile->params->dir);
512                 filelist_setdir(sfile->files, sfile->params->dir);
513                 filelist_free(sfile->files);
514                 sfile->params->active_file = -1;
515         }               
516         ED_area_tag_redraw(CTX_wm_area(C));
517
518         return OPERATOR_FINISHED;
519
520 }
521
522
523 void FILE_OT_parent(struct wmOperatorType *ot)
524 {
525         /* identifiers */
526         ot->name= "Parent File";
527         ot->idname= "FILE_OT_parent";
528         
529         /* api callbacks */
530         ot->exec= file_parent_exec;
531         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
532 }
533
534 struct ARegion *file_buttons_region(struct ScrArea *sa)
535 {
536         ARegion *ar, *arnew;
537         
538         for(ar= sa->regionbase.first; ar; ar= ar->next)
539                 if(ar->regiontype==RGN_TYPE_CHANNELS)
540                         return ar;
541         return NULL;
542 }
543
544 int file_bookmark_toggle_exec(bContext *C, wmOperator *unused)
545 {
546         ScrArea *sa= CTX_wm_area(C);
547         ARegion *ar= file_buttons_region(sa);
548         
549         if(ar) {
550                 ar->flag ^= RGN_FLAG_HIDDEN;
551                 ar->v2d.flag &= ~V2D_IS_INITIALISED; /* XXX should become hide/unhide api? */
552                 
553                 ED_area_initialize(CTX_wm_manager(C), CTX_wm_window(C), sa);
554                 ED_area_tag_redraw(sa);
555         }
556         return OPERATOR_FINISHED;
557 }
558
559 void FILE_OT_bookmark_toggle(struct wmOperatorType *ot)
560 {
561         /* identifiers */
562         ot->name= "Toggle Bookmarks";
563         ot->idname= "FILE_OT_bookmark_toggle";
564         
565         /* api callbacks */
566         ot->exec= file_bookmark_toggle_exec;
567         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
568 }