1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2010 Blender Foundation.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup wm
22 *
23 * Our own drag-and-drop, drag state and drop boxes.
24 */
25
26 #include <string.h>
27
28 #include "DNA_screen_types.h"
29 #include "DNA_windowmanager_types.h"
30
31 #include "MEM_guardedalloc.h"
32
33 #include "BLT_translation.h"
34
35 #include "BLI_blenlib.h"
36
37 #include "BIF_glutil.h"
38
39 #include "BKE_context.h"
40 #include "BKE_idtype.h"
41
42 #include "GPU_shader.h"
43 #include "GPU_state.h"
44 #include "GPU_viewport.h"
45
46 #include "IMB_imbuf_types.h"
47
48 #include "UI_interface.h"
49 #include "UI_interface_icons.h"
50
51 #include "RNA_access.h"
52
53 #include "WM_api.h"
54 #include "WM_types.h"
55 #include "wm_event_system.h"
56
57 /* ****************************************************** */
58
59 static ListBase dropboxes = {NULL, NULL};
60
61 /* drop box maps are stored global for now */
62 /* these are part of blender's UI/space specs, and not like keymaps */
63 /* when editors become configurable, they can add own dropbox definitions */
64
65 typedef struct wmDropBoxMap {
66 struct wmDropBoxMap *next, *prev;
67
68 ListBase dropboxes;
69 short spaceid, regionid;
70 char idname[KMAP_MAX_NAME];
71
72 } wmDropBoxMap;
73
74 /* spaceid/regionid is zero for window drop maps */
WM_dropboxmap_find(const char * idname,int spaceid,int regionid)75 ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid)
76 {
77 LISTBASE_FOREACH (wmDropBoxMap *, dm, &dropboxes) {
78 if (dm->spaceid == spaceid && dm->regionid == regionid) {
79 if (STREQLEN(idname, dm->idname, KMAP_MAX_NAME)) {
80 return &dm->dropboxes;
81 }
82 }
83 }
84
85 wmDropBoxMap *dm = MEM_callocN(sizeof(struct wmDropBoxMap), "dropmap list");
86 BLI_strncpy(dm->idname, idname, KMAP_MAX_NAME);
87 dm->spaceid = spaceid;
88 dm->regionid = regionid;
89 BLI_addtail(&dropboxes, dm);
90
91 return &dm->dropboxes;
92 }
93
WM_dropbox_add(ListBase * lb,const char * idname,bool (* poll)(bContext *,wmDrag *,const wmEvent *,const char **),void (* copy)(wmDrag *,wmDropBox *))94 wmDropBox *WM_dropbox_add(ListBase *lb,
95 const char *idname,
96 bool (*poll)(bContext *, wmDrag *, const wmEvent *, const char **),
97 void (*copy)(wmDrag *, wmDropBox *))
98 {
99 wmDropBox *drop = MEM_callocN(sizeof(wmDropBox), "wmDropBox");
100 drop->poll = poll;
101 drop->copy = copy;
102 drop->ot = WM_operatortype_find(idname, 0);
103 drop->opcontext = WM_OP_INVOKE_DEFAULT;
104
105 if (drop->ot == NULL) {
106 MEM_freeN(drop);
107 printf("Error: dropbox with unknown operator: %s\n", idname);
108 return NULL;
109 }
110 WM_operator_properties_alloc(&(drop->ptr), &(drop->properties), idname);
111
112 BLI_addtail(lb, drop);
113
114 return drop;
115 }
116
wm_dropbox_free(void)117 void wm_dropbox_free(void)
118 {
119
120 LISTBASE_FOREACH (wmDropBoxMap *, dm, &dropboxes) {
121 LISTBASE_FOREACH (wmDropBox *, drop, &dm->dropboxes) {
122 if (drop->ptr) {
123 WM_operator_properties_free(drop->ptr);
124 MEM_freeN(drop->ptr);
125 }
126 }
127 BLI_freelistN(&dm->dropboxes);
128 }
129
130 BLI_freelistN(&dropboxes);
131 }
132
133 /* *********************************** */
134
135 /* note that the pointer should be valid allocated and not on stack */
WM_event_start_drag(struct bContext * C,int icon,int type,void * poin,double value,unsigned int flags)136 wmDrag *WM_event_start_drag(
137 struct bContext *C, int icon, int type, void *poin, double value, unsigned int flags)
138 {
139 wmWindowManager *wm = CTX_wm_manager(C);
140 wmDrag *drag = MEM_callocN(sizeof(struct wmDrag), "new drag");
141
142 /* keep track of future multitouch drag too, add a mousepointer id or so */
143 /* if multiple drags are added, they're drawn as list */
144
145 BLI_addtail(&wm->drags, drag);
146 drag->flags = flags;
147 drag->icon = icon;
148 drag->type = type;
149 if (type == WM_DRAG_PATH) {
150 BLI_strncpy(drag->path, poin, FILE_MAX);
151 }
152 else if (type == WM_DRAG_ID) {
153 if (poin) {
154 WM_drag_add_ID(drag, poin, NULL);
155 }
156 }
157 else {
158 drag->poin = poin;
159 }
160 drag->value = value;
161
162 return drag;
163 }
164
WM_event_drag_image(wmDrag * drag,ImBuf * imb,float scale,int sx,int sy)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
WM_drag_free(wmDrag * drag)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 BLI_freelistN(&drag->ids);
180 MEM_freeN(drag);
181 }
182
WM_drag_free_list(struct ListBase * lb)183 void WM_drag_free_list(struct ListBase *lb)
184 {
185 wmDrag *drag;
186 while ((drag = BLI_pophead(lb))) {
187 WM_drag_free(drag);
188 }
189 }
190
dropbox_active(bContext * C,ListBase * handlers,wmDrag * drag,const wmEvent * event)191 static const char *dropbox_active(bContext *C,
192 ListBase *handlers,
193 wmDrag *drag,
194 const wmEvent *event)
195 {
196 LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) {
197 if (handler_base->type == WM_HANDLER_TYPE_DROPBOX) {
198 wmEventHandler_Dropbox *handler = (wmEventHandler_Dropbox *)handler_base;
199 if (handler->dropboxes) {
200 LISTBASE_FOREACH (wmDropBox *, drop, handler->dropboxes) {
201 const char *tooltip = NULL;
202 if (drop->poll(C, drag, event, &tooltip)) {
203 /* XXX Doing translation here might not be ideal, but later we have no more
204 * access to ot (and hence op context)... */
205 return (tooltip) ? tooltip : WM_operatortype_name(drop->ot, drop->ptr);
206 }
207 }
208 }
209 }
210 }
211 return NULL;
212 }
213
214 /* return active operator name when mouse is in box */
wm_dropbox_active(bContext * C,wmDrag * drag,const wmEvent * event)215 static const char *wm_dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event)
216 {
217 wmWindow *win = CTX_wm_window(C);
218 ScrArea *area = CTX_wm_area(C);
219 ARegion *region = CTX_wm_region(C);
220 const char *name;
221
222 name = dropbox_active(C, &win->handlers, drag, event);
223 if (name) {
224 return name;
225 }
226
227 name = dropbox_active(C, &area->handlers, drag, event);
228 if (name) {
229 return name;
230 }
231
232 name = dropbox_active(C, ®ion->handlers, drag, event);
233 if (name) {
234 return name;
235 }
236
237 return NULL;
238 }
239
wm_drop_operator_options(bContext * C,wmDrag * drag,const wmEvent * event)240 static void wm_drop_operator_options(bContext *C, wmDrag *drag, const wmEvent *event)
241 {
242 wmWindow *win = CTX_wm_window(C);
243 const int winsize_x = WM_window_pixels_x(win);
244 const int winsize_y = WM_window_pixels_y(win);
245
246 /* for multiwin drags, we only do this if mouse inside */
247 if (event->x < 0 || event->y < 0 || event->x > winsize_x || event->y > winsize_y) {
248 return;
249 }
250
251 drag->opname[0] = 0;
252
253 /* check buttons (XXX todo rna and value) */
254 if (UI_but_active_drop_name(C)) {
255 BLI_strncpy(drag->opname, IFACE_("Paste name"), sizeof(drag->opname));
256 }
257 else {
258 const char *opname = wm_dropbox_active(C, drag, event);
259
260 if (opname) {
261 BLI_strncpy(drag->opname, opname, sizeof(drag->opname));
262 // WM_cursor_modal_set(win, WM_CURSOR_COPY);
263 }
264 // else
265 // WM_cursor_modal_restore(win);
266 /* unsure about cursor type, feels to be too much */
267 }
268 }
269
270 /* called in inner handler loop, region context */
wm_drags_check_ops(bContext * C,const wmEvent * event)271 void wm_drags_check_ops(bContext *C, const wmEvent *event)
272 {
273 wmWindowManager *wm = CTX_wm_manager(C);
274
275 LISTBASE_FOREACH (wmDrag *, drag, &wm->drags) {
276 wm_drop_operator_options(C, drag, event);
277 }
278 }
279
280 /* ************** IDs ***************** */
281
WM_drag_add_ID(wmDrag * drag,ID * id,ID * from_parent)282 void WM_drag_add_ID(wmDrag *drag, ID *id, ID *from_parent)
283 {
284 /* Don't drag the same ID twice. */
285 LISTBASE_FOREACH (wmDragID *, drag_id, &drag->ids) {
286 if (drag_id->id == id) {
287 if (drag_id->from_parent == NULL) {
288 drag_id->from_parent = from_parent;
289 }
290 return;
291 }
292 if (GS(drag_id->id->name) != GS(id->name)) {
293 BLI_assert(!"All dragged IDs must have the same type");
294 return;
295 }
296 }
297
298 /* Add to list. */
299 wmDragID *drag_id = MEM_callocN(sizeof(wmDragID), __func__);
300 drag_id->id = id;
301 drag_id->from_parent = from_parent;
302 BLI_addtail(&drag->ids, drag_id);
303 }
304
WM_drag_ID(const wmDrag * drag,short idcode)305 ID *WM_drag_ID(const wmDrag *drag, short idcode)
306 {
307 if (drag->type != WM_DRAG_ID) {
308 return NULL;
309 }
310
311 wmDragID *drag_id = drag->ids.first;
312 if (!drag_id) {
313 return NULL;
314 }
315
316 ID *id = drag_id->id;
317 return (idcode == 0 || GS(id->name) == idcode) ? id : NULL;
318 }
319
WM_drag_ID_from_event(const wmEvent * event,short idcode)320 ID *WM_drag_ID_from_event(const wmEvent *event, short idcode)
321 {
322 if (event->custom != EVT_DATA_DRAGDROP) {
323 return NULL;
324 }
325
326 ListBase *lb = event->customdata;
327 return WM_drag_ID(lb->first, idcode);
328 }
329
330 /* ************** draw ***************** */
331
wm_drop_operator_draw(const char * name,int x,int y)332 static void wm_drop_operator_draw(const char *name, int x, int y)
333 {
334 const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
335 const float col_fg[4] = {1.0f, 1.0f, 1.0f, 1.0f};
336 const float col_bg[4] = {0.0f, 0.0f, 0.0f, 0.2f};
337
338 UI_fontstyle_draw_simple_backdrop(fstyle, x, y, name, col_fg, col_bg);
339 }
340
wm_drag_name(wmDrag * drag)341 static const char *wm_drag_name(wmDrag *drag)
342 {
343 switch (drag->type) {
344 case WM_DRAG_ID: {
345 ID *id = WM_drag_ID(drag, 0);
346 bool single = (BLI_listbase_count_at_most(&drag->ids, 2) == 1);
347
348 if (single) {
349 return id->name + 2;
350 }
351 if (id) {
352 return BKE_idtype_idcode_to_name_plural(GS(id->name));
353 }
354 break;
355 }
356 case WM_DRAG_PATH:
357 case WM_DRAG_NAME:
358 return drag->path;
359 }
360 return "";
361 }
362
drag_rect_minmax(rcti * rect,int x1,int y1,int x2,int y2)363 static void drag_rect_minmax(rcti *rect, int x1, int y1, int x2, int y2)
364 {
365 if (rect->xmin > x1) {
366 rect->xmin = x1;
367 }
368 if (rect->xmax < x2) {
369 rect->xmax = x2;
370 }
371 if (rect->ymin > y1) {
372 rect->ymin = y1;
373 }
374 if (rect->ymax < y2) {
375 rect->ymax = y2;
376 }
377 }
378
379 /* called in wm_draw.c */
380 /* if rect set, do not draw */
wm_drags_draw(bContext * C,wmWindow * win,rcti * rect)381 void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect)
382 {
383 const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
384 wmWindowManager *wm = CTX_wm_manager(C);
385 const int winsize_y = WM_window_pixels_y(win);
386
387 int cursorx = win->eventstate->x;
388 int cursory = win->eventstate->y;
389 if (rect) {
390 rect->xmin = rect->xmax = cursorx;
391 rect->ymin = rect->ymax = cursory;
392 }
393
394 /* Should we support multi-line drag draws? Maybe not, more types mixed wont work well. */
395 GPU_blend(GPU_BLEND_ALPHA);
396 LISTBASE_FOREACH (wmDrag *, drag, &wm->drags) {
397 const uchar text_col[] = {255, 255, 255, 255};
398 int iconsize = UI_DPI_ICON_SIZE;
399 int padding = 4 * UI_DPI_FAC;
400
401 /* image or icon */
402 int x, y;
403 if (drag->imb) {
404 x = cursorx - drag->sx / 2;
405 y = cursory - drag->sy / 2;
406
407 if (rect) {
408 drag_rect_minmax(rect, x, y, x + drag->sx, y + drag->sy);
409 }
410 else {
411 float col[4] = {1.0f, 1.0f, 1.0f, 0.65f}; /* this blends texture */
412 IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
413 immDrawPixelsTexScaled(&state,
414 x,
415 y,
416 drag->imb->x,
417 drag->imb->y,
418 GPU_RGBA8,
419 false,
420 drag->imb->rect,
421 drag->scale,
422 drag->scale,
423 1.0f,
424 1.0f,
425 col);
426 }
427 }
428 else {
429 x = cursorx - 2 * padding;
430 y = cursory - 2 * UI_DPI_FAC;
431
432 if (rect) {
433 drag_rect_minmax(rect, x, y, x + iconsize, y + iconsize);
434 }
435 else {
436 UI_icon_draw_ex(x, y, drag->icon, U.inv_dpi_fac, 0.8, 0.0f, text_col, false);
437 }
438 }
439
440 /* item name */
441 if (drag->imb) {
442 x = cursorx - drag->sx / 2;
443 y = cursory - drag->sy / 2 - iconsize;
444 }
445 else {
446 x = cursorx + 10 * UI_DPI_FAC;
447 y = cursory + 1 * UI_DPI_FAC;
448 }
449
450 if (rect) {
451 int w = UI_fontstyle_string_width(fstyle, wm_drag_name(drag));
452 drag_rect_minmax(rect, x, y, x + w, y + iconsize);
453 }
454 else {
455 UI_fontstyle_draw_simple(fstyle, x, y, wm_drag_name(drag), text_col);
456 }
457
458 /* operator name with roundbox */
459 if (drag->opname[0]) {
460 if (drag->imb) {
461 x = cursorx - drag->sx / 2;
462
463 if (cursory + drag->sy / 2 + padding + iconsize < winsize_y) {
464 y = cursory + drag->sy / 2 + padding;
465 }
466 else {
467 y = cursory - drag->sy / 2 - padding - iconsize - padding - iconsize;
468 }
469 }
470 else {
471 x = cursorx - 2 * padding;
472
473 if (cursory + iconsize + iconsize < winsize_y) {
474 y = (cursory + iconsize) + padding;
475 }
476 else {
477 y = (cursory - iconsize) - padding;
478 }
479 }
480
481 if (rect) {
482 int w = UI_fontstyle_string_width(fstyle, wm_drag_name(drag));
483 drag_rect_minmax(rect, x, y, x + w, y + iconsize);
484 }
485 else {
486 wm_drop_operator_draw(drag->opname, x, y);
487 }
488 }
489 }
490 GPU_blend(GPU_BLEND_NONE);
491 }
492