code cleanup: use const events for modal and invoke operators.
[blender.git] / source / blender / windowmanager / intern / wm_dragdrop.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) 2010 Blender Foundation.
19  * All rights reserved.
20  *
21  * 
22  * Contributor(s): Blender Foundation
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/windowmanager/intern/wm_dragdrop.c
28  *  \ingroup wm
29  */
30
31
32 #include <string.h>
33
34 #include "DNA_windowmanager_types.h"
35 #include "DNA_screen_types.h"
36
37 #include "MEM_guardedalloc.h"
38
39 #include "BLF_translation.h"
40
41 #include "BLI_blenlib.h"
42
43 #include "BIF_gl.h"
44 #include "BIF_glutil.h"
45
46 #include "BKE_blender.h"
47 #include "BKE_context.h"
48 #include "BKE_idprop.h"
49 #include "BKE_library.h"
50 #include "BKE_main.h"
51 #include "BKE_screen.h"
52 #include "BKE_global.h"
53
54 #include "IMB_imbuf_types.h"
55 #include "IMB_imbuf.h"
56
57 #include "UI_interface.h"
58 #include "UI_interface_icons.h"
59
60 #include "RNA_access.h"
61
62 #include "WM_api.h"
63 #include "WM_types.h"
64 #include "wm_event_system.h"
65 #include "wm.h"
66
67
68 /* ****************************************************** */
69
70 static ListBase dropboxes = {NULL, NULL};
71
72 /* drop box maps are stored global for now */
73 /* these are part of blender's UI/space specs, and not like keymaps */
74 /* when editors become configurable, they can add own dropbox definitions */
75
76 typedef struct wmDropBoxMap {
77         struct wmDropBoxMap *next, *prev;
78         
79         ListBase dropboxes;
80         short spaceid, regionid;
81         char idname[KMAP_MAX_NAME];
82         
83 } wmDropBoxMap;
84
85 /* spaceid/regionid is zero for window drop maps */
86 ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid)
87 {
88         wmDropBoxMap *dm;
89         
90         for (dm = dropboxes.first; dm; dm = dm->next)
91                 if (dm->spaceid == spaceid && dm->regionid == regionid)
92                         if (0 == strncmp(idname, dm->idname, KMAP_MAX_NAME))
93                                 return &dm->dropboxes;
94         
95         dm = MEM_callocN(sizeof(struct wmDropBoxMap), "dropmap list");
96         BLI_strncpy(dm->idname, idname, KMAP_MAX_NAME);
97         dm->spaceid = spaceid;
98         dm->regionid = regionid;
99         BLI_addtail(&dropboxes, dm);
100         
101         return &dm->dropboxes;
102 }
103
104
105
106 wmDropBox *WM_dropbox_add(ListBase *lb, const char *idname, int (*poll)(bContext *, wmDrag *, const wmEvent *),
107                           void (*copy)(wmDrag *, wmDropBox *))
108 {
109         wmDropBox *drop = MEM_callocN(sizeof(wmDropBox), "wmDropBox");
110         
111         drop->poll = poll;
112         drop->copy = copy;
113         drop->ot = WM_operatortype_find(idname, 0);
114         drop->opcontext = WM_OP_INVOKE_DEFAULT;
115         
116         if (drop->ot == NULL) {
117                 MEM_freeN(drop);
118                 printf("Error: dropbox with unknown operator: %s\n", idname);
119                 return NULL;
120         }
121         WM_operator_properties_alloc(&(drop->ptr), &(drop->properties), idname);
122         
123         BLI_addtail(lb, drop);
124         
125         return drop;
126 }
127
128 void wm_dropbox_free(void)
129 {
130         wmDropBoxMap *dm;
131         
132         for (dm = dropboxes.first; dm; dm = dm->next) {
133                 wmDropBox *drop;
134                 
135                 for (drop = dm->dropboxes.first; drop; drop = drop->next) {
136                         if (drop->ptr) {
137                                 WM_operator_properties_free(drop->ptr);
138                                 MEM_freeN(drop->ptr);
139                         }
140                 }
141                 BLI_freelistN(&dm->dropboxes);
142         }
143         
144         BLI_freelistN(&dropboxes);
145 }
146
147 /* *********************************** */
148
149 /* note that the pointer should be valid allocated and not on stack */
150 wmDrag *WM_event_start_drag(struct bContext *C, int icon, int type, void *poin, double value)
151 {
152         wmWindowManager *wm = CTX_wm_manager(C);
153         wmDrag *drag = MEM_callocN(sizeof(struct wmDrag), "new drag");
154         
155         /* keep track of future multitouch drag too, add a mousepointer id or so */
156         /* if multiple drags are added, they're drawn as list */
157         
158         BLI_addtail(&wm->drags, drag);
159         drag->icon = icon;
160         drag->type = type;
161         if (type == WM_DRAG_PATH)
162                 BLI_strncpy(drag->path, poin, FILE_MAX);
163         else
164                 drag->poin = poin;
165         drag->value = value;
166         
167         return drag;
168 }
169
170 void WM_event_drag_image(wmDrag *drag, ImBuf *imb, float scale, int sx, int sy)
171 {
172         drag->imb = imb;
173         drag->scale = scale;
174         drag->sx = sx;
175         drag->sy = sy;
176 }
177
178
179 static const char *dropbox_active(bContext *C, ListBase *handlers, wmDrag *drag, wmEvent *event)
180 {
181         wmEventHandler *handler = handlers->first;
182         for (; handler; handler = handler->next) {
183                 if (handler->dropboxes) {
184                         wmDropBox *drop = handler->dropboxes->first;
185                         for (; drop; drop = drop->next) {
186                                 if (drop->poll(C, drag, event))
187                                         /* XXX Doing translation here might not be ideal, but later we have no more
188                                          *     access to ot (and hence op context)... */
189                                         return RNA_struct_ui_name(drop->ot->srna);
190                         }
191                 }
192         }
193         return NULL;
194 }
195
196 /* return active operator name when mouse is in box */
197 static const char *wm_dropbox_active(bContext *C, wmDrag *drag, wmEvent *event)
198 {
199         wmWindow *win = CTX_wm_window(C);
200         ScrArea *sa = CTX_wm_area(C);
201         ARegion *ar = CTX_wm_region(C);
202         const char *name;
203         
204         name = dropbox_active(C, &win->handlers, drag, event);
205         if (name) return name;
206         
207         name = dropbox_active(C, &sa->handlers, drag, event);
208         if (name) return name;
209         
210         name = dropbox_active(C, &ar->handlers, drag, event);
211         if (name) return name;
212
213         return NULL;
214 }
215
216
217 static void wm_drop_operator_options(bContext *C, wmDrag *drag, wmEvent *event)
218 {
219         wmWindow *win = CTX_wm_window(C);
220         
221         /* for multiwin drags, we only do this if mouse inside */
222         if (event->x < 0 || event->y < 0 || event->x > win->sizex || event->y > win->sizey)
223                 return;
224         
225         drag->opname[0] = 0;
226         
227         /* check buttons (XXX todo rna and value) */
228         if (UI_but_active_drop_name(C) ) {
229                 strcpy(drag->opname, IFACE_("Paste name"));
230         }
231         else {
232                 const char *opname = wm_dropbox_active(C, drag, event);
233                 
234                 if (opname) {
235                         BLI_strncpy(drag->opname, opname, FILE_MAX);
236                         // WM_cursor_modal(win, CURSOR_COPY);
237                 }
238                 // else
239                 //      WM_cursor_restore(win);
240                 /* unsure about cursor type, feels to be too much */
241         }
242 }
243
244 /* called in inner handler loop, region context */
245 void wm_drags_check_ops(bContext *C, wmEvent *event)
246 {
247         wmWindowManager *wm = CTX_wm_manager(C);
248         wmDrag *drag;
249         
250         for (drag = wm->drags.first; drag; drag = drag->next) {
251                 wm_drop_operator_options(C, drag, event);
252         }
253 }
254
255 /* ************** draw ***************** */
256
257 static void wm_drop_operator_draw(const char *name, int x, int y)
258 {
259         int width = UI_GetStringWidth(name);
260         
261         glColor4ub(0, 0, 0, 50);
262         
263         uiSetRoundBox(UI_CNR_ALL | UI_RB_ALPHA);
264         uiRoundBox(x, y, x + width + 8, y + 15, 4);
265         
266         glColor4ub(255, 255, 255, 255);
267         UI_DrawString(x + 4, y + 4, name);
268 }
269
270 static const char *wm_drag_name(wmDrag *drag)
271 {
272         switch (drag->type) {
273                 case WM_DRAG_ID:
274                 {
275                         ID *id = (ID *)drag->poin;
276                         return id->name + 2;
277                 }
278                 case WM_DRAG_PATH:
279                         return drag->path;
280                 case WM_DRAG_NAME:
281                         return (char *)drag->path;
282         }
283         return "";
284 }
285
286 static void drag_rect_minmax(rcti *rect, int x1, int y1, int x2, int y2)
287 {
288         if (rect->xmin > x1)
289                 rect->xmin = x1;
290         if (rect->xmax < x2)
291                 rect->xmax = x2;
292         if (rect->ymin > y1)
293                 rect->ymin = y1;
294         if (rect->ymax < y2)
295                 rect->ymax = y2;
296 }
297
298 /* called in wm_draw.c */
299 /* if rect set, do not draw */
300 void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect)
301 {
302         wmWindowManager *wm = CTX_wm_manager(C);
303         wmDrag *drag;
304         int cursorx, cursory, x, y;
305         
306         cursorx = win->eventstate->x;
307         cursory = win->eventstate->y;
308         if (rect) {
309                 rect->xmin = rect->xmax = cursorx;
310                 rect->ymin = rect->ymax = cursory;
311         }
312         
313         /* XXX todo, multiline drag draws... but maybe not, more types mixed wont work well */
314         glEnable(GL_BLEND);
315         for (drag = wm->drags.first; drag; drag = drag->next) {
316                 
317                 /* image or icon */
318                 if (drag->imb) {
319                         x = cursorx - drag->sx / 2;
320                         y = cursory - drag->sy / 2;
321                         
322                         if (rect)
323                                 drag_rect_minmax(rect, x, y, x + drag->sx, y + drag->sy);
324                         else {
325                                 glColor4f(1.0, 1.0, 1.0, 0.65); /* this blends texture */
326                                 glaDrawPixelsTexScaled(x, y, drag->imb->x, drag->imb->y, GL_UNSIGNED_BYTE, drag->imb->rect, drag->scale, drag->scale);
327                         }
328                 }
329                 else {
330                         x = cursorx - 8;
331                         y = cursory - 2;
332                         
333                         /* icons assumed to be 16 pixels */
334                         if (rect)
335                                 drag_rect_minmax(rect, x, y, x + 16, y + 16);
336                         else
337                                 UI_icon_draw_aspect(x, y, drag->icon, 1.0, 0.8);
338                 }
339                 
340                 /* item name */
341                 if (drag->imb) {
342                         x = cursorx - drag->sx / 2;
343                         y = cursory - drag->sy / 2 - 16;
344                 }
345                 else {
346                         x = cursorx + 10;
347                         y = cursory + 1;
348                 }
349                 
350                 if (rect) {
351                         int w =  UI_GetStringWidth(wm_drag_name(drag));
352                         drag_rect_minmax(rect, x, y, x + w, y + 16);
353                 }
354                 else {
355                         glColor4ub(255, 255, 255, 255);
356                         UI_DrawString(x, y, wm_drag_name(drag));
357                 }
358                 
359                 /* operator name with roundbox */
360                 if (drag->opname[0]) {
361                         if (drag->imb) {
362                                 x = cursorx - drag->sx / 2;
363                                 y = cursory + drag->sy / 2 + 4;
364                         }
365                         else {
366                                 x = cursorx - 8;
367                                 y = cursory + 16;
368                         }
369                         
370                         if (rect) {
371                                 int w =  UI_GetStringWidth(wm_drag_name(drag));
372                                 drag_rect_minmax(rect, x, y, x + w, y + 16);
373                         }
374                         else 
375                                 wm_drop_operator_draw(drag->opname, x, y);
376                         
377                 }
378         }
379         glDisable(GL_BLEND);
380 }