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_hor(SpaceFile *sfile, struct ARegion* ar, short x, short y)
70 {
71         float fx,fy;
72         int offsetx, offsety;
73         int columns;
74         int active_file = -1;
75         int numfiles = filelist_numfiles(sfile->files);
76         View2D* v2d = &ar->v2d;
77
78         UI_view2d_region_to_view(v2d, x, y, &fx, &fy);
79
80         offsetx = (fx - (v2d->cur.xmin+sfile->tile_border_x))/(sfile->tile_w + 2*sfile->tile_border_x);
81         offsety = (v2d->tot.ymax - sfile->tile_border_y - fy)/(sfile->tile_h + 2*sfile->tile_border_y);
82         columns = (v2d->cur.xmax - v2d->cur.xmin) / (sfile->tile_w+ 2*sfile->tile_border_x);
83         active_file = offsetx + columns*offsety;
84
85         if ( (active_file < 0) || (active_file >= numfiles) )
86         {
87                 active_file = -1;
88         }
89         return active_file;
90 }
91
92
93 static int find_file_mouse_vert(SpaceFile *sfile, struct ARegion* ar, short x, short y)
94 {
95         int offsetx, offsety;
96         float fx,fy;
97         int active_file = -1;
98         int numfiles = filelist_numfiles(sfile->files);
99         int rows;
100         View2D* v2d = &ar->v2d;
101
102         UI_view2d_region_to_view(v2d, x, y, &fx, &fy);
103         
104         offsetx = (fx-sfile->tile_border_x)/(sfile->tile_w + sfile->tile_border_x);
105         offsety = (v2d->cur.ymax-fy-sfile->tile_border_y)/(sfile->tile_h + sfile->tile_border_y);
106         rows = (v2d->cur.ymax - v2d->cur.ymin - 2*sfile->tile_border_y) / (sfile->tile_h+sfile->tile_border_y);
107         active_file = rows*offsetx + offsety;
108         if ( (active_file < 0) || (active_file >= numfiles) )
109         {
110                 active_file = -1;
111         }
112         return active_file;
113 }
114
115 static void file_deselect_all(SpaceFile* sfile)
116 {
117         int numfiles = filelist_numfiles(sfile->files);
118         int i;
119
120         for ( i=0; i < numfiles; ++i) {
121                 struct direntry* file = filelist_file(sfile->files, i);
122                 if (file && (file->flags & ACTIVE)) {
123                         file->flags &= ~ACTIVE;
124                 }
125         }
126 }
127
128 static void file_select(SpaceFile* sfile, FileSelectParams* params, ARegion* ar, const rcti* rect, short val)
129 {
130         int first_file = -1;
131         int last_file = -1;
132         int act_file;
133         short selecting = (val == LEFTMOUSE);
134
135         int numfiles = filelist_numfiles(sfile->files);
136
137         params->selstate = NOTACTIVE;
138         if (params->display) {
139                 first_file = find_file_mouse_hor(sfile, ar, rect->xmin, rect->ymax);
140                 last_file = find_file_mouse_hor(sfile, ar, rect->xmax, rect->ymin);
141         } else {
142                 first_file = find_file_mouse_vert(sfile, ar, rect->xmin, rect->ymax);
143                 last_file = find_file_mouse_vert(sfile, ar, rect->xmax, rect->ymin);
144         }
145         
146         /* select all valid files between first and last indicated */
147         if ( (first_file >= 0) && (first_file < numfiles) && (last_file >= 0) && (last_file < numfiles) ) {
148                 for (act_file = first_file; act_file <= last_file; act_file++) {
149                         struct direntry* file = filelist_file(sfile->files, act_file);
150                         if (selecting) 
151                                 file->flags |= ACTIVE;
152                         else
153                                 file->flags &= ~ACTIVE;
154                 }
155         }
156         
157         /* make the last file active */
158         if (last_file >= 0 && last_file < numfiles) {
159                 struct direntry* file = filelist_file(sfile->files, last_file);
160                 params->active_file = last_file;
161
162                 if(file && S_ISDIR(file->type)) {
163                         /* the path is too long and we are not going up! */
164                         if (strcmp(file->relname, ".") &&
165                                 strcmp(file->relname, "..") &&
166                                 strlen(params->dir) + strlen(file->relname) >= FILE_MAX ) 
167                         {
168                                 // XXX error("Path too long, cannot enter this directory");
169                         } else {
170                                 if (strcmp(file->relname, "..")==0) {
171                                         /* avoids /../../ */
172                                         BLI_parent_dir(params->dir);
173                                 } else {
174                                         strcat(params->dir, file->relname);
175                                         strcat(params->dir,"/");
176                                         params->file[0] = '\0';
177                                         BLI_cleanup_dir(G.sce, params->dir);
178                                 }
179                                 filelist_setdir(sfile->files, params->dir);
180                                 filelist_free(sfile->files);
181                                 params->active_file = -1;
182                         }
183                 }
184                 else if (file)
185                 {
186                         if (file->relname) {
187                                 BLI_strncpy(params->file, file->relname, FILE_MAXFILE);
188                                 /* XXX
189                                 if(event==MIDDLEMOUSE && filelist_gettype(sfile->files)) 
190                                         imasel_execute(sfile);
191                                 */
192                         }
193                         
194                 }       
195                 /* XXX
196                 if(BIF_filelist_gettype(sfile->files)==FILE_MAIN) {
197                         active_imasel_object(sfile);
198                 }
199                 */
200         }
201 }
202
203
204
205 static int file_border_select_exec(bContext *C, wmOperator *op)
206 {
207         ARegion *ar= CTX_wm_region(C);
208         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
209         short val;
210         rcti rect;
211
212         val= RNA_int_get(op->ptr, "event_type");
213         rect.xmin= RNA_int_get(op->ptr, "xmin");
214         rect.ymin= RNA_int_get(op->ptr, "ymin");
215         rect.xmax= RNA_int_get(op->ptr, "xmax");
216         rect.ymax= RNA_int_get(op->ptr, "ymax");
217
218         file_select(sfile, sfile->params, ar, &rect, val );
219         WM_event_add_notifier(C, NC_WINDOW, NULL);
220         return OPERATOR_FINISHED;
221 }
222
223 void ED_FILE_OT_border_select(wmOperatorType *ot)
224 {
225         /* identifiers */
226         ot->name= "Activate/Select File";
227         ot->idname= "ED_FILE_OT_border_select";
228         
229         /* api callbacks */
230         ot->invoke= WM_border_select_invoke;
231         ot->exec= file_border_select_exec;
232         ot->modal= WM_border_select_modal;
233
234         /* rna */
235         RNA_def_int(ot->srna, "event_type", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX);
236         RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
237         RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
238         RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
239         RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
240
241         ot->poll= ED_operator_file_active;
242 }
243
244 static int file_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
245 {
246         ARegion *ar= CTX_wm_region(C);
247         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
248         short val;
249         rcti rect;
250
251         rect.xmin = rect.xmax = event->x - ar->winrct.xmin;
252         rect.ymin = rect.ymax = event->y - ar->winrct.ymin;
253         val = event->val;
254
255         /* single select, deselect all selected first */
256         file_deselect_all(sfile);
257         file_select(sfile, sfile->params, ar, &rect, val );
258         WM_event_add_notifier(C, NC_WINDOW, NULL);
259         return OPERATOR_FINISHED;
260 }
261
262 void ED_FILE_OT_select(wmOperatorType *ot)
263 {
264         /* identifiers */
265         ot->name= "Activate/Select File";
266         ot->idname= "ED_FILE_OT_select";
267         
268         /* api callbacks */
269         ot->invoke= file_select_invoke;
270
271         /* rna */
272
273         ot->poll= ED_operator_file_active;
274 }
275
276 static int file_select_all_invoke(bContext *C, wmOperator *op, wmEvent *event)
277 {
278         ScrArea *sa= CTX_wm_area(C);
279         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
280         int numfiles = filelist_numfiles(sfile->files);
281         int i;
282         int select = 1;
283
284         /* if any file is selected, deselect all first */
285         for ( i=0; i < numfiles; ++i) {
286                 struct direntry* file = filelist_file(sfile->files, i);
287                 if (file && (file->flags & ACTIVE)) {
288                         file->flags &= ~ACTIVE;
289                         select = 0;
290                         ED_area_tag_redraw(sa);
291                 }
292         }
293         /* select all only if previously no file was selected */
294         if (select) {
295                 for ( i=0; i < numfiles; ++i) {
296                         struct direntry* file = filelist_file(sfile->files, i);
297                         if(file && !S_ISDIR(file->type)) {
298                                 file->flags |= ACTIVE;
299                                 ED_area_tag_redraw(sa);
300                         }
301                 }
302         }
303         return OPERATOR_FINISHED;
304 }
305
306 void ED_FILE_OT_select_all(wmOperatorType *ot)
307 {
308         /* identifiers */
309         ot->name= "Select/Deselect all files";
310         ot->idname= "ED_FILE_OT_select_all";
311         
312         /* api callbacks */
313         ot->invoke= file_select_all_invoke;
314
315         /* rna */
316
317         ot->poll= ED_operator_file_active;
318 }
319
320 /* ---------- BOOKMARKS ----------- */
321
322 static void set_active_bookmark(FileSelectParams* params, struct ARegion* ar, short x, short y)
323 {
324         int nentries = fsmenu_get_nentries();
325         float fx, fy;
326         short posy;
327
328         UI_view2d_region_to_view(&ar->v2d, x, y, &fx, &fy);
329
330         posy = ar->v2d.cur.ymax - 2*TILE_BORDER_Y - fy;
331         params->active_bookmark = ((float)posy / (U.fontsize*3.0f/2.0f));
332         if (params->active_bookmark < 0 || params->active_bookmark > nentries) {
333                 params->active_bookmark = -1;
334         }
335 }
336
337 static void file_select_bookmark(SpaceFile* sfile, ARegion* ar, short x, short y)
338 {
339         if (BLI_in_rcti(&ar->v2d.mask, x, y)) {
340                 char *selected;
341                 set_active_bookmark(sfile->params, ar, x, y);
342                 selected= fsmenu_get_entry(sfile->params->active_bookmark);                     
343                 /* which string */
344                 if (selected) {
345                         FileSelectParams* params = sfile->params;
346                         BLI_strncpy(params->dir, selected, sizeof(params->dir));
347                         BLI_cleanup_dir(G.sce, params->dir);
348                         filelist_free(sfile->files);    
349                         filelist_setdir(sfile->files, params->dir);
350                         params->file[0] = '\0';                 
351                         params->active_file = -1;
352                 }
353         }
354 }
355
356 static int bookmark_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
357 {
358         ScrArea *sa= CTX_wm_area(C);
359         ARegion *ar= CTX_wm_region(C);  
360         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
361
362         short x, y;
363
364         x = event->x - ar->winrct.xmin;
365         y = event->y - ar->winrct.ymin;
366
367         file_select_bookmark(sfile, ar, x, y);
368         ED_area_tag_redraw(sa);
369         return OPERATOR_FINISHED;
370 }
371
372 void ED_FILE_OT_select_bookmark(wmOperatorType *ot)
373 {
374         /* identifiers */
375         ot->name= "Select Directory";
376         ot->idname= "ED_FILE_OT_select_bookmark";
377         
378         /* api callbacks */
379         ot->invoke= bookmark_select_invoke;
380         ot->poll= ED_operator_file_active;
381 }
382
383 static int loadimages_invoke(bContext *C, wmOperator *op, wmEvent *event)
384 {
385         ScrArea *sa= CTX_wm_area(C);
386         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
387         if (sfile->files) {
388                 filelist_loadimage_timer(sfile->files);
389                 if (filelist_changed(sfile->files)) {
390                         ED_area_tag_redraw(sa);
391                 }
392         }
393
394         return OPERATOR_FINISHED;
395 }
396
397 void ED_FILE_OT_loadimages(wmOperatorType *ot)
398 {
399         
400         /* identifiers */
401         ot->name= "Load Images";
402         ot->idname= "ED_FILE_OT_loadimages";
403         
404         /* api callbacks */
405         ot->invoke= loadimages_invoke;
406         
407         ot->poll= ED_operator_file_active;
408 }
409
410 int file_hilight_set(SpaceFile *sfile, ARegion *ar, int mx, int my)
411 {
412         FileSelectParams* params;
413         int numfiles, actfile;
414         
415         if(sfile==NULL || sfile->files==NULL) return 0;
416         
417         numfiles = filelist_numfiles(sfile->files);
418         params = ED_fileselect_get_params(sfile);
419         
420         if (params->display) {
421                 actfile = find_file_mouse_hor(sfile, ar, mx , my);
422         } else {
423                 actfile = find_file_mouse_vert(sfile, ar, mx, my);
424         }
425         
426         if (actfile >= 0 && actfile < numfiles ) {
427                 params->active_file=actfile;
428                 return 1;
429         }
430         return 0;
431 }
432
433 static int file_highlight_invoke(bContext *C, wmOperator *op, wmEvent *event)
434 {
435         ARegion *ar= CTX_wm_region(C);
436         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
437         
438         if( file_hilight_set(sfile, ar, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin)) {
439                 ED_area_tag_redraw(CTX_wm_area(C));
440         }
441         
442         return OPERATOR_FINISHED;
443 }
444
445 void ED_FILE_OT_highlight(struct wmOperatorType *ot)
446 {
447         /* identifiers */
448         ot->name= "Highlight File";
449         ot->idname= "ED_FILE_OT_highlight";
450         
451         /* api callbacks */
452         ot->invoke= file_highlight_invoke;
453         ot->poll= ED_operator_file_active;
454 }
455
456 int file_cancel_exec(bContext *C, wmOperator *unused)
457 {
458         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
459         
460         if(sfile->op) {
461                 WM_operator_free(sfile->op);
462                 sfile->op = NULL;
463         }
464         ED_screen_full_prevspace(C);
465         
466         return OPERATOR_FINISHED;
467 }
468
469 void ED_FILE_OT_cancel(struct wmOperatorType *ot)
470 {
471         /* identifiers */
472         ot->name= "Cancel File Load";
473         ot->idname= "ED_FILE_OT_cancel";
474         
475         /* api callbacks */
476         ot->exec= file_cancel_exec;
477         ot->poll= ED_operator_file_active;
478 }
479
480
481 int file_load_exec(bContext *C, wmOperator *unused)
482 {
483         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
484         char name[FILE_MAX];
485         
486         ED_screen_full_prevspace(C);
487         
488         if(sfile->op) {
489                 wmOperator *op= sfile->op;
490                 
491                 /* if load .blend, all UI pointers after exec are invalid! */
492                 /* but, operator can be freed still */
493                 
494                 sfile->op = NULL;
495                 BLI_strncpy(name, sfile->params->dir, sizeof(name));
496                 strcat(name, sfile->params->file);
497                 RNA_string_set(op->ptr, "filename", name);
498                 
499                 /* this gives ownership to pupmenu */
500                 uiPupMenuSaveOver(C, op, name);
501         }
502
503         return OPERATOR_FINISHED;
504 }
505
506 void ED_FILE_OT_load(struct wmOperatorType *ot)
507 {
508         /* identifiers */
509         ot->name= "Load File";
510         ot->idname= "ED_FILE_OT_load";
511         
512         /* api callbacks */
513         ot->exec= file_load_exec;
514         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
515 }
516
517 int file_parent_exec(bContext *C, wmOperator *unused)
518 {
519         SpaceFile *sfile= (SpaceFile*)CTX_wm_space_data(C);
520         
521         if(sfile->params) {
522                 BLI_parent_dir(sfile->params->dir);
523                 filelist_setdir(sfile->files, sfile->params->dir);
524                 filelist_free(sfile->files);
525                 sfile->params->active_file = -1;
526         }               
527         ED_area_tag_redraw(CTX_wm_area(C));
528
529         return OPERATOR_FINISHED;
530
531 }
532
533 void ED_FILE_OT_parent(struct wmOperatorType *ot)
534 {
535         /* identifiers */
536         ot->name= "Parent File";
537         ot->idname= "ED_FILE_OT_parent";
538         
539         /* api callbacks */
540         ot->exec= file_parent_exec;
541         ot->poll= ED_operator_file_active; /* <- important, handler is on window level */
542 }
543
544
545