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