NLA SoC: Merge from 2.5
[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(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         if ( (active_file < 0) || (active_file >= numfiles) )
81         {
82                 active_file = -1;
83         }
84         return active_file;
85 }
86
87
88 static void file_deselect_all(SpaceFile* sfile)
89 {
90         int numfiles = filelist_numfiles(sfile->files);
91         int i;
92
93         for ( i=0; i < numfiles; ++i) {
94                 struct direntry* file = filelist_file(sfile->files, i);
95                 if (file && (file->flags & ACTIVE)) {
96                         file->flags &= ~ACTIVE;
97                 }
98         }
99 }
100
101 static void file_select(SpaceFile* sfile, ARegion* ar, const rcti* rect, short val)
102 {
103         int first_file = -1;
104         int last_file = -1;
105         int act_file;
106         short selecting = (val == LEFTMOUSE);
107         FileSelectParams *params = ED_fileselect_get_params(sfile);
108         FileLayout *layout = ED_fileselect_get_layout(sfile, ar);
109
110         int numfiles = filelist_numfiles(sfile->files);
111
112         params->selstate = NOTACTIVE;
113         first_file = find_file_mouse(sfile, ar, rect->xmin, rect->ymax);
114         last_file = find_file_mouse(sfile, ar, rect->xmax, rect->ymin);
115         
116         /* select all valid files between first and last indicated */
117         if ( (first_file >= 0) && (first_file < numfiles) && (last_file >= 0) && (last_file < numfiles) ) {
118                 for (act_file = first_file; act_file <= last_file; act_file++) {
119                         struct direntry* file = filelist_file(sfile->files, act_file);
120                         if (selecting) 
121                                 file->flags |= ACTIVE;
122                         else
123                                 file->flags &= ~ACTIVE;
124                 }
125         }
126
127         /* make the last file active */
128         if (last_file >= 0 && last_file < numfiles) {
129                 struct direntry* file = filelist_file(sfile->files, last_file);
130                 params->active_file = last_file;
131
132                 if(file && S_ISDIR(file->type)) {
133                         /* the path is too long and we are not going up! */
134                         if (strcmp(file->relname, ".") &&
135                                 strcmp(file->relname, "..") &&
136                                 strlen(params->dir) + strlen(file->relname) >= FILE_MAX ) 
137                         {
138                                 // XXX error("Path too long, cannot enter this directory");
139                         } else {
140                                 if (strcmp(file->relname, "..")==0) {
141                                         /* avoids /../../ */
142                                         BLI_parent_dir(params->dir);
143                                 } else {
144                                         strcat(params->dir, file->relname);
145                                         strcat(params->dir,"/");
146                                         params->file[0] = '\0';
147                                         BLI_cleanup_dir(G.sce, params->dir);
148                                 }
149                                 filelist_setdir(sfile->files, params->dir);
150                                 filelist_free(sfile->files);
151                                 params->active_file = -1;
152                         }
153                 }
154                 else if (file)
155                 {
156                         if (file->relname) {
157                                 BLI_strncpy(params->file, file->relname, FILE_MAXFILE);
158                                 /* XXX
159                                 if(event==MIDDLEMOUSE && filelist_gettype(sfile->files)) 
160                                         imasel_execute(sfile);
161                                 */
162                         }
163                         
164                 }       
165                 /* XXX
166                 if(BIF_filelist_gettype(sfile->files)==FILE_MAIN) {
167                         active_imasel_object(sfile);
168                 }
169                 */
170         }
171 }
172
173
174
175 static int file_border_select_exec(bContext *C, wmOperator *op)
176 {
177         ARegion *ar= CTX_wm_region(C);
178         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
179         short val;
180         rcti rect;
181
182         val= RNA_int_get(op->ptr, "event_type");
183         rect.xmin= RNA_int_get(op->ptr, "xmin");
184         rect.ymin= RNA_int_get(op->ptr, "ymin");
185         rect.xmax= RNA_int_get(op->ptr, "xmax");
186         rect.ymax= RNA_int_get(op->ptr, "ymax");
187
188         file_select(sfile, ar, &rect, val );
189         WM_event_add_notifier(C, NC_WINDOW, NULL);
190         return OPERATOR_FINISHED;
191 }
192
193 void FILE_OT_select_border(wmOperatorType *ot)
194 {
195         /* identifiers */
196         ot->name= "Activate/Select File";
197         ot->idname= "FILE_OT_select_border";
198         
199         /* api callbacks */
200         ot->invoke= WM_border_select_invoke;
201         ot->exec= file_border_select_exec;
202         ot->modal= WM_border_select_modal;
203
204         /* rna */
205         RNA_def_int(ot->srna, "event_type", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX);
206         RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
207         RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
208         RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
209         RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
210
211         ot->poll= ED_operator_file_active;
212 }
213
214 static int file_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
215 {
216         ARegion *ar= CTX_wm_region(C);
217         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
218         short val;
219         rcti rect;
220
221         rect.xmin = rect.xmax = event->x - ar->winrct.xmin;
222         rect.ymin = rect.ymax = event->y - ar->winrct.ymin;
223         val = event->val;
224
225         if (BLI_in_rcti(&ar->v2d.mask, rect.xmin, rect.ymin)) { 
226
227                 /* single select, deselect all selected first */
228                 file_deselect_all(sfile);
229                 file_select(sfile, ar, &rect, val );
230                 WM_event_add_notifier(C, NC_WINDOW, NULL);
231         }
232         return OPERATOR_FINISHED;
233 }
234
235 void FILE_OT_select(wmOperatorType *ot)
236 {
237         /* identifiers */
238         ot->name= "Activate/Select File";
239         ot->idname= "FILE_OT_select";
240         
241         /* api callbacks */
242         ot->invoke= file_select_invoke;
243
244         /* rna */
245
246         ot->poll= ED_operator_file_active;
247 }
248
249 static int file_select_all_invoke(bContext *C, wmOperator *op, wmEvent *event)
250 {
251         ScrArea *sa= CTX_wm_area(C);
252         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
253         int numfiles = filelist_numfiles(sfile->files);
254         int i;
255         int select = 1;
256
257         /* if any file is selected, deselect all first */
258         for ( i=0; i < numfiles; ++i) {
259                 struct direntry* file = filelist_file(sfile->files, i);
260                 if (file && (file->flags & ACTIVE)) {
261                         file->flags &= ~ACTIVE;
262                         select = 0;
263                         ED_area_tag_redraw(sa);
264                 }
265         }
266         /* select all only if previously no file was selected */
267         if (select) {
268                 for ( i=0; i < numfiles; ++i) {
269                         struct direntry* file = filelist_file(sfile->files, i);
270                         if(file && !S_ISDIR(file->type)) {
271                                 file->flags |= ACTIVE;
272                                 ED_area_tag_redraw(sa);
273                         }
274                 }
275         }
276         return OPERATOR_FINISHED;
277 }
278
279 void FILE_OT_select_all_toggle(wmOperatorType *ot)
280 {
281         /* identifiers */
282         ot->name= "Select/Deselect all files";
283         ot->idname= "FILE_OT_select_all_toggle";
284         
285         /* api callbacks */
286         ot->invoke= file_select_all_invoke;
287
288         /* rna */
289
290         ot->poll= ED_operator_file_active;
291 }
292
293 /* ---------- BOOKMARKS ----------- */
294
295 static int file_select_bookmark_category(SpaceFile* sfile, ARegion* ar, short x, short y, FSMenuCategory category)
296 {
297         struct FSMenu* fsmenu = fsmenu_get();
298         int nentries = fsmenu_get_nentries(fsmenu, category);
299         int linestep = file_font_pointsize()*2.0f;
300         short xs, ys;
301         int i;
302         int selected = -1;
303
304         for (i=0; i < nentries; ++i) {
305                 fsmenu_get_pos(fsmenu, category, i, &xs, &ys);
306                 if ( (y<=ys) && (y>ys-linestep) ) {
307                         fsmenu_select_entry(fsmenu, category, i);
308                         selected = i;
309                         break;
310                 }
311         }
312         return selected;
313 }
314
315 static void file_select_bookmark(SpaceFile* sfile, ARegion* ar, short x, short y)
316 {
317         float fx, fy;
318         int selected;
319         FSMenuCategory category = FS_CATEGORY_SYSTEM;
320
321         if (BLI_in_rcti(&ar->v2d.mask, x, y)) {
322                 char *entry;
323
324                 UI_view2d_region_to_view(&ar->v2d, x, y, &fx, &fy);
325                 selected = file_select_bookmark_category(sfile, ar, fx, fy, FS_CATEGORY_SYSTEM);
326                 if (selected<0) {
327                         category = FS_CATEGORY_BOOKMARKS;
328                         selected = file_select_bookmark_category(sfile, ar, fx, fy, category);
329                 }
330                 if (selected<0) {
331                         category = FS_CATEGORY_RECENT;
332                         selected = file_select_bookmark_category(sfile, ar, fx, fy, category);
333                 }
334                 
335                 if (selected>=0) {
336                         entry= fsmenu_get_entry(fsmenu_get(), category, selected);                      
337                         /* which string */
338                         if (entry) {
339                                 FileSelectParams* params = sfile->params;
340                                 BLI_strncpy(params->dir, entry, sizeof(params->dir));
341                                 BLI_cleanup_dir(G.sce, params->dir);
342                                 filelist_free(sfile->files);    
343                                 filelist_setdir(sfile->files, params->dir);
344                                 params->file[0] = '\0';                 
345                                 params->active_file = -1;
346                         }
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         actfile = find_file_mouse(sfile, ar, mx , my);
416         
417         if (params && (actfile >= 0) && (actfile < numfiles) ) {
418                 params->active_file=actfile;
419                 return 1;
420         } 
421         params->active_file= -1;
422         return 0;
423 }
424
425 static int file_highlight_invoke(bContext *C, wmOperator *op, wmEvent *event)
426 {
427         ARegion *ar= CTX_wm_region(C);
428         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
429         
430         if( file_hilight_set(sfile, ar, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin)) {
431                 ED_area_tag_redraw(CTX_wm_area(C));
432         }
433         
434         return OPERATOR_FINISHED;
435 }
436
437 void FILE_OT_highlight(struct wmOperatorType *ot)
438 {
439         /* identifiers */
440         ot->name= "Highlight File";
441         ot->idname= "FILE_OT_highlight";
442         
443         /* api callbacks */
444         ot->invoke= file_highlight_invoke;
445         ot->poll= ED_operator_file_active;
446 }
447
448 int file_cancel_exec(bContext *C, wmOperator *unused)
449 {
450         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
451         
452         WM_event_fileselect_event(C, sfile->op, EVT_FILESELECT_CANCEL);
453         sfile->op = NULL;
454         
455         return OPERATOR_FINISHED;
456 }
457
458 void FILE_OT_cancel(struct wmOperatorType *ot)
459 {
460         /* identifiers */
461         ot->name= "Cancel File Load";
462         ot->idname= "FILE_OT_cancel";
463         
464         /* api callbacks */
465         ot->exec= file_cancel_exec;
466         ot->poll= ED_operator_file_active;
467 }
468
469 /* sends events now, so things get handled on windowqueue level */
470 int file_exec(bContext *C, wmOperator *unused)
471 {
472         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
473         char name[FILE_MAX];
474         
475         if(sfile->op) {
476                 wmOperator *op= sfile->op;
477                 
478                 sfile->op = NULL;
479                 BLI_strncpy(name, sfile->params->dir, sizeof(name));
480                 strcat(name, sfile->params->file);
481                 RNA_string_set(op->ptr, "filename", name);
482                 
483                 /* some ops have multiple files to select */
484                 {
485                         PointerRNA itemptr;
486                         int i, numfiles = filelist_numfiles(sfile->files);
487                         struct direntry *file;
488                         if(RNA_struct_find_property(op->ptr, "files")) {
489                                 for (i=0; i<numfiles; i++) {
490                                         file = filelist_file(sfile->files, i);
491                                         if(file->flags & ACTIVE) {
492                                                 if ((file->type & S_IFDIR)==0) {
493                                                         RNA_collection_add(op->ptr, "files", &itemptr);
494                                                         RNA_string_set(&itemptr, "name", file->relname);
495                                                 }
496                                         }
497                                 }
498                         }
499                         
500                         if(RNA_struct_find_property(op->ptr, "dirs")) {
501                                 for (i=0; i<numfiles; i++) {
502                                         file = filelist_file(sfile->files, i);
503                                         if(file->flags & ACTIVE) {
504                                                 if ((file->type & S_IFDIR)) {
505                                                         RNA_collection_add(op->ptr, "dirs", &itemptr);
506                                                         RNA_string_set(&itemptr, "name", file->relname);
507                                                 }
508                                         }
509                                 }
510                         }
511                 }
512                 
513                 fsmenu_insert_entry(fsmenu_get(), FS_CATEGORY_RECENT, sfile->params->dir,0, 1);
514                 BLI_make_file_string(G.sce, name, BLI_gethome(), ".Bfs");
515                 fsmenu_write_file(fsmenu_get(), name);
516                 WM_event_fileselect_event(C, op, EVT_FILESELECT_EXEC);
517         }
518                                 
519         return OPERATOR_FINISHED;
520 }
521
522 void FILE_OT_exec(struct wmOperatorType *ot)
523 {
524         /* identifiers */
525         ot->name= "Execute File Window";
526         ot->idname= "FILE_OT_exec";
527         
528         /* api callbacks */
529         ot->exec= file_exec;
530         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
531 }
532
533
534 int file_parent_exec(bContext *C, wmOperator *unused)
535 {
536         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
537         
538         if(sfile->params) {
539                 BLI_parent_dir(sfile->params->dir);
540                 filelist_setdir(sfile->files, sfile->params->dir);
541                 filelist_free(sfile->files);
542                 sfile->params->active_file = -1;
543         }               
544         ED_area_tag_redraw(CTX_wm_area(C));
545
546         return OPERATOR_FINISHED;
547
548 }
549
550
551 void FILE_OT_parent(struct wmOperatorType *ot)
552 {
553         /* identifiers */
554         ot->name= "Parent File";
555         ot->idname= "FILE_OT_parent";
556         
557         /* api callbacks */
558         ot->exec= file_parent_exec;
559         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
560 }
561
562
563 int file_refresh_exec(bContext *C, wmOperator *unused)
564 {
565         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
566         
567         if(sfile->params) {
568                 filelist_setdir(sfile->files, sfile->params->dir);
569                 filelist_free(sfile->files);
570                 sfile->params->active_file = -1;
571         }               
572         ED_area_tag_redraw(CTX_wm_area(C));
573
574         return OPERATOR_FINISHED;
575
576 }
577
578
579 void FILE_OT_refresh(struct wmOperatorType *ot)
580 {
581         /* identifiers */
582         ot->name= "Refresh Filelist";
583         ot->idname= "FILE_OT_refresh";
584         
585         /* api callbacks */
586         ot->exec= file_refresh_exec;
587         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
588 }
589
590 struct ARegion *file_buttons_region(struct ScrArea *sa)
591 {
592         ARegion *ar;
593         
594         for(ar= sa->regionbase.first; ar; ar= ar->next)
595                 if(ar->regiontype==RGN_TYPE_CHANNELS)
596                         return ar;
597         return NULL;
598 }
599
600 int file_bookmark_toggle_exec(bContext *C, wmOperator *unused)
601 {
602         ScrArea *sa= CTX_wm_area(C);
603         ARegion *ar= file_buttons_region(sa);
604         
605         if(ar) {
606                 ar->flag ^= RGN_FLAG_HIDDEN;
607                 ar->v2d.flag &= ~V2D_IS_INITIALISED; /* XXX should become hide/unhide api? */
608                 
609                 ED_area_initialize(CTX_wm_manager(C), CTX_wm_window(C), sa);
610                 ED_area_tag_redraw(sa);
611         }
612         return OPERATOR_FINISHED;
613 }
614
615 void FILE_OT_bookmark_toggle(struct wmOperatorType *ot)
616 {
617         /* identifiers */
618         ot->name= "Toggle Bookmarks";
619         ot->idname= "FILE_OT_bookmark_toggle";
620         
621         /* api callbacks */
622         ot->exec= file_bookmark_toggle_exec;
623         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
624 }