d4e3bd41197658db79e9120dadbd9a0811c07fbc
[blender.git] / source / blender / editors / interface / interface_ops.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) 2009 Blender Foundation.
19  * All rights reserved.
20  * 
21  * Contributor(s): Blender Foundation, Joshua Leung
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/editors/interface/interface_ops.c
27  *  \ingroup edinterface
28  */
29
30 #include <string.h>
31
32 #include "MEM_guardedalloc.h"
33
34 #include "DNA_screen_types.h"
35 #include "DNA_text_types.h" /* for UI_OT_reports_to_text */
36 #include "DNA_object_types.h" /* for OB_DATA_SUPPORT_ID */
37
38 #include "BLI_blenlib.h"
39 #include "BLI_math_color.h"
40
41 #include "BLF_api.h"
42 #include "BLT_lang.h"
43
44 #include "BKE_context.h"
45 #include "BKE_screen.h"
46 #include "BKE_global.h"
47 #include "BKE_node.h"
48 #include "BKE_text.h" /* for UI_OT_reports_to_text */
49 #include "BKE_report.h"
50
51 #include "RNA_access.h"
52 #include "RNA_define.h"
53
54 #include "UI_interface.h"
55
56 #include "interface_intern.h"
57
58 #include "WM_api.h"
59 #include "WM_types.h"
60
61 #include "ED_paint.h"
62
63 /* only for UI_OT_editsource */
64 #include "ED_screen.h"
65 #include "BKE_main.h"
66 #include "BLI_ghash.h"
67
68 /* Reset Default Theme ------------------------ */
69
70 static int reset_default_theme_exec(bContext *C, wmOperator *UNUSED(op))
71 {
72         ui_theme_init_default();
73         ui_style_init_default();
74         WM_event_add_notifier(C, NC_WINDOW, NULL);
75         
76         return OPERATOR_FINISHED;
77 }
78
79 static void UI_OT_reset_default_theme(wmOperatorType *ot)
80 {
81         /* identifiers */
82         ot->name = "Reset to Default Theme";
83         ot->idname = "UI_OT_reset_default_theme";
84         ot->description = "Reset to the default theme colors";
85         
86         /* callbacks */
87         ot->exec = reset_default_theme_exec;
88         
89         /* flags */
90         ot->flag = OPTYPE_REGISTER;
91 }
92
93 /* Copy Data Path Operator ------------------------ */
94
95 static int copy_data_path_button_poll(bContext *C)
96 {
97         PointerRNA ptr;
98         PropertyRNA *prop;
99         char *path;
100         int index;
101
102         UI_context_active_but_prop_get(C, &ptr, &prop, &index);
103
104         if (ptr.id.data && ptr.data && prop) {
105                 path = RNA_path_from_ID_to_property(&ptr, prop);
106                 
107                 if (path) {
108                         MEM_freeN(path);
109                         return 1;
110                 }
111         }
112
113         return 0;
114 }
115
116 static int copy_data_path_button_exec(bContext *C, wmOperator *UNUSED(op))
117 {
118         PointerRNA ptr;
119         PropertyRNA *prop;
120         char *path;
121         int index;
122
123         /* try to create driver using property retrieved from UI */
124         UI_context_active_but_prop_get(C, &ptr, &prop, &index);
125
126         if (ptr.id.data && ptr.data && prop) {
127                 path = RNA_path_from_ID_to_property(&ptr, prop);
128                 
129                 if (path) {
130                         WM_clipboard_text_set(path, false);
131                         MEM_freeN(path);
132                         return OPERATOR_FINISHED;
133                 }
134         }
135
136         return OPERATOR_CANCELLED;
137 }
138
139 static void UI_OT_copy_data_path_button(wmOperatorType *ot)
140 {
141         /* identifiers */
142         ot->name = "Copy Data Path";
143         ot->idname = "UI_OT_copy_data_path_button";
144         ot->description = "Copy the RNA data path for this property to the clipboard";
145
146         /* callbacks */
147         ot->exec = copy_data_path_button_exec;
148         ot->poll = copy_data_path_button_poll;
149
150         /* flags */
151         ot->flag = OPTYPE_REGISTER;
152 }
153
154 static int copy_python_command_button_poll(bContext *C)
155 {
156         uiBut *but = UI_context_active_but_get(C);
157
158         if (but && (but->optype != NULL)) {
159                 return 1;
160         }
161
162         return 0;
163 }
164
165 static int copy_python_command_button_exec(bContext *C, wmOperator *UNUSED(op))
166 {
167         uiBut *but = UI_context_active_but_get(C);
168
169         if (but && (but->optype != NULL)) {
170                 PointerRNA *opptr;
171                 char *str;
172                 opptr = UI_but_operator_ptr_get(but); /* allocated when needed, the button owns it */
173
174                 str = WM_operator_pystring_ex(C, NULL, false, true, but->optype, opptr);
175
176                 WM_clipboard_text_set(str, 0);
177
178                 MEM_freeN(str);
179
180                 return OPERATOR_FINISHED;
181         }
182
183         return OPERATOR_CANCELLED;
184 }
185
186 static void UI_OT_copy_python_command_button(wmOperatorType *ot)
187 {
188         /* identifiers */
189         ot->name = "Copy Python Command";
190         ot->idname = "UI_OT_copy_python_command_button";
191         ot->description = "Copy the Python command matching this button";
192
193         /* callbacks */
194         ot->exec = copy_python_command_button_exec;
195         ot->poll = copy_python_command_button_poll;
196
197         /* flags */
198         ot->flag = OPTYPE_REGISTER;
199 }
200
201 /* Reset to Default Values Button Operator ------------------------ */
202
203 static int operator_button_property_finish(bContext *C, PointerRNA *ptr, PropertyRNA *prop)
204 {
205         ID *id = ptr->id.data;
206
207         /* perform updates required for this property */
208         RNA_property_update(C, ptr, prop);
209
210         /* as if we pressed the button */
211         UI_context_active_but_prop_handle(C);
212
213         /* Since we don't want to undo _all_ edits to settings, eg window
214          * edits on the screen or on operator settings.
215          * it might be better to move undo's inline - campbell */
216         if (id && ID_CHECK_UNDO(id)) {
217                 /* do nothing, go ahead with undo */
218                 return OPERATOR_FINISHED;
219         }
220         else {
221                 return OPERATOR_CANCELLED;
222         }
223 }
224
225 static int reset_default_button_poll(bContext *C)
226 {
227         PointerRNA ptr;
228         PropertyRNA *prop;
229         int index;
230
231         UI_context_active_but_prop_get(C, &ptr, &prop, &index);
232         
233         return (ptr.data && prop && RNA_property_editable(&ptr, prop));
234 }
235
236 static int reset_default_button_exec(bContext *C, wmOperator *op)
237 {
238         PointerRNA ptr;
239         PropertyRNA *prop;
240         int index;
241         const bool all = RNA_boolean_get(op->ptr, "all");
242
243         /* try to reset the nominated setting to its default value */
244         UI_context_active_but_prop_get(C, &ptr, &prop, &index);
245         
246         /* if there is a valid property that is editable... */
247         if (ptr.data && prop && RNA_property_editable(&ptr, prop)) {
248                 if (RNA_property_reset(&ptr, prop, (all) ? -1 : index))
249                         return operator_button_property_finish(C, &ptr, prop);
250         }
251
252         return OPERATOR_CANCELLED;
253 }
254
255 static void UI_OT_reset_default_button(wmOperatorType *ot)
256 {
257         /* identifiers */
258         ot->name = "Reset to Default Value";
259         ot->idname = "UI_OT_reset_default_button";
260         ot->description = "Reset this property's value to its default value";
261
262         /* callbacks */
263         ot->poll = reset_default_button_poll;
264         ot->exec = reset_default_button_exec;
265
266         /* flags */
267         ot->flag = OPTYPE_UNDO;
268         
269         /* properties */
270         RNA_def_boolean(ot->srna, "all", 1, "All", "Reset to default values all elements of the array");
271 }
272
273 /* Unset Property Button Operator ------------------------ */
274
275 static int unset_property_button_exec(bContext *C, wmOperator *UNUSED(op))
276 {
277         PointerRNA ptr;
278         PropertyRNA *prop;
279         int index;
280
281         /* try to unset the nominated property */
282         UI_context_active_but_prop_get(C, &ptr, &prop, &index);
283
284         /* if there is a valid property that is editable... */
285         if (ptr.data && prop && RNA_property_editable(&ptr, prop) &&
286             /* RNA_property_is_idprop(prop) && */
287             RNA_property_is_set(&ptr, prop))
288         {
289                 RNA_property_unset(&ptr, prop);
290                 return operator_button_property_finish(C, &ptr, prop);
291         }
292
293         return OPERATOR_CANCELLED;
294 }
295
296 static void UI_OT_unset_property_button(wmOperatorType *ot)
297 {
298         /* identifiers */
299         ot->name = "Unset property";
300         ot->idname = "UI_OT_unset_property_button";
301         ot->description = "Clear the property and use default or generated value in operators";
302
303         /* callbacks */
304         ot->poll = ED_operator_regionactive;
305         ot->exec = unset_property_button_exec;
306
307         /* flags */
308         ot->flag = OPTYPE_UNDO;
309 }
310
311 /* Copy To Selected Operator ------------------------ */
312
313 bool UI_context_copy_to_selected_list(
314         bContext *C, PointerRNA *ptr, PropertyRNA *prop,
315         ListBase *r_lb, bool *r_use_path_from_id, char **r_path)
316 {
317         *r_use_path_from_id = false;
318         *r_path = NULL;
319
320         if (RNA_struct_is_a(ptr->type, &RNA_EditBone)) {
321                 *r_lb = CTX_data_collection_get(C, "selected_editable_bones");
322         }
323         else if (RNA_struct_is_a(ptr->type, &RNA_PoseBone)) {
324                 *r_lb = CTX_data_collection_get(C, "selected_pose_bones");
325         }
326         else if (RNA_struct_is_a(ptr->type, &RNA_Bone)) {
327                 ListBase lb;
328                 lb = CTX_data_collection_get(C, "selected_pose_bones");
329
330                 if (!BLI_listbase_is_empty(&lb)) {
331                         CollectionPointerLink *link;
332                         for (link = lb.first; link; link = link->next) {
333                                 bPoseChannel *pchan = link->ptr.data;
334                                 RNA_pointer_create(link->ptr.id.data, &RNA_Bone, pchan->bone, &link->ptr);
335                         }
336                 }
337
338                 *r_lb = lb;
339         }
340         else if (RNA_struct_is_a(ptr->type, &RNA_Sequence)) {
341                 *r_lb = CTX_data_collection_get(C, "selected_editable_sequences");
342         }
343         else if (RNA_struct_is_a(ptr->type, &RNA_Node) ||
344                  RNA_struct_is_a(ptr->type, &RNA_NodeSocket))
345         {
346                 ListBase lb = {NULL, NULL};
347                 char *path = NULL;
348                 bNode *node = NULL;
349
350                 /* Get the node we're editing */
351                 if (RNA_struct_is_a(ptr->type, &RNA_NodeSocket)) {
352                         bNodeTree *ntree = ptr->id.data;
353                         bNodeSocket *sock = ptr->data;
354                         if (nodeFindNode(ntree, sock, &node, NULL)) {
355                                 if ((path = RNA_path_resolve_from_type_to_property(ptr, prop, &RNA_Node)) != NULL) {
356                                         /* we're good! */
357                                 }
358                                 else {
359                                         node = NULL;
360                                 }
361                         }
362                 }
363                 else {
364                         node = ptr->data;
365                 }
366
367                 /* Now filter by type */
368                 if (node) {
369                         CollectionPointerLink *link, *link_next;
370                         lb = CTX_data_collection_get(C, "selected_nodes");
371
372                         for (link = lb.first; link; link = link_next) {
373                                 bNode *node_data = link->ptr.data;
374                                 link_next = link->next;
375
376                                 if (node_data->type != node->type) {
377                                         BLI_remlink(&lb, link);
378                                         MEM_freeN(link);
379                                 }
380                         }
381                 }
382
383                 *r_lb = lb;
384                 *r_path = path;
385         }
386         else if (ptr->id.data) {
387                 ID *id = ptr->id.data;
388
389                 if (GS(id->name) == ID_OB) {
390                         *r_lb = CTX_data_collection_get(C, "selected_editable_objects");
391                         *r_use_path_from_id = true;
392                         *r_path = RNA_path_from_ID_to_property(ptr, prop);
393                 }
394                 else if (OB_DATA_SUPPORT_ID(GS(id->name))) {
395                         /* check we're using the active object */
396                         const short id_code = GS(id->name);
397                         ListBase lb = CTX_data_collection_get(C, "selected_editable_objects");
398                         char *path = RNA_path_from_ID_to_property(ptr, prop);
399
400                         /* de-duplicate obdata */
401                         if (!BLI_listbase_is_empty(&lb)) {
402                                 CollectionPointerLink *link, *link_next;
403
404                                 for (link = lb.first; link; link = link->next) {
405                                         Object *ob = link->ptr.id.data;
406                                         if (ob->data) {
407                                                 ID *id_data = ob->data;
408                                                 id_data->tag |= LIB_TAG_DOIT;
409                                         }
410                                 }
411
412                                 for (link = lb.first; link; link = link_next) {
413                                         Object *ob = link->ptr.id.data;
414                                         ID *id_data = ob->data;
415                                         link_next = link->next;
416
417                                         if ((id_data == NULL) ||
418                                             (id_data->tag & LIB_TAG_DOIT) == 0 ||
419                                             (id_data->lib) ||
420                                             (GS(id_data->name) != id_code))
421                                         {
422                                                 BLI_remlink(&lb, link);
423                                                 MEM_freeN(link);
424                                         }
425                                         else {
426                                                 /* avoid prepending 'data' to the path */
427                                                 RNA_id_pointer_create(id_data, &link->ptr);
428                                         }
429
430                                         if (id_data) {
431                                                 id_data->tag &= ~LIB_TAG_DOIT;
432                                         }
433                                 }
434                         }
435
436                         *r_lb = lb;
437                         *r_path = path;
438                 }
439                 else if (GS(id->name) == ID_SCE) {
440                         /* Sequencer's ID is scene :/ */
441                         /* Try to recursively find an RNA_Sequence ancestor, to handle situations like T41062... */
442                         if ((*r_path = RNA_path_resolve_from_type_to_property(ptr, prop, &RNA_Sequence)) != NULL) {
443                                 *r_lb = CTX_data_collection_get(C, "selected_editable_sequences");
444                         }
445                 }
446                 return (*r_path != NULL);
447         }
448         else {
449                 return false;
450         }
451
452         return true;
453 }
454
455 /**
456  * called from both exec & poll
457  *
458  * \note: normally we wouldn't call a loop from within a poll function,
459  * However this is a special case, and for regular poll calls, getting
460  * the context from the button will fail early.
461  */
462 static bool copy_to_selected_button(bContext *C, bool all, bool poll)
463 {
464         PointerRNA ptr, lptr, idptr;
465         PropertyRNA *prop, *lprop;
466         bool success = false;
467         int index;
468
469         /* try to reset the nominated setting to its default value */
470         UI_context_active_but_prop_get(C, &ptr, &prop, &index);
471
472         /* if there is a valid property that is editable... */
473         if (ptr.data && prop) {
474                 char *path = NULL;
475                 bool use_path_from_id;
476                 CollectionPointerLink *link;
477                 ListBase lb;
478
479                 if (!UI_context_copy_to_selected_list(C, &ptr, prop, &lb, &use_path_from_id, &path))
480                         return success;
481
482                 for (link = lb.first; link; link = link->next) {
483                         if (link->ptr.data != ptr.data) {
484                                 if (use_path_from_id) {
485                                         /* Path relative to ID. */
486                                         lprop = NULL;
487                                         RNA_id_pointer_create(link->ptr.id.data, &idptr);
488                                         RNA_path_resolve_property(&idptr, path, &lptr, &lprop);
489                                 }
490                                 else if (path) {
491                                         /* Path relative to elements from list. */
492                                         lprop = NULL;
493                                         RNA_path_resolve_property(&link->ptr, path, &lptr, &lprop);
494                                 }
495                                 else {
496                                         lptr = link->ptr;
497                                         lprop = prop;
498                                 }
499
500                                 if (lptr.data == ptr.data) {
501                                         /* lptr might not be the same as link->ptr! */
502                                         continue;
503                                 }
504
505                                 if (lprop == prop) {
506                                         if (RNA_property_editable(&lptr, lprop)) {
507                                                 if (poll) {
508                                                         success = true;
509                                                         break;
510                                                 }
511                                                 else {
512                                                         if (RNA_property_copy(&lptr, &ptr, prop, (all) ? -1 : index)) {
513                                                                 RNA_property_update(C, &lptr, prop);
514                                                                 success = true;
515                                                         }
516                                                 }
517                                         }
518                                 }
519                         }
520                 }
521
522                 MEM_SAFE_FREE(path);
523                 BLI_freelistN(&lb);
524         }
525
526         return success;
527 }
528
529 static int copy_to_selected_button_poll(bContext *C)
530 {
531         return copy_to_selected_button(C, false, true);
532 }
533
534 static int copy_to_selected_button_exec(bContext *C, wmOperator *op)
535 {
536         bool success;
537
538         const bool all = RNA_boolean_get(op->ptr, "all");
539
540         success = copy_to_selected_button(C, all, false);
541
542         return (success) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
543 }
544
545 static void UI_OT_copy_to_selected_button(wmOperatorType *ot)
546 {
547         /* identifiers */
548         ot->name = "Copy To Selected";
549         ot->idname = "UI_OT_copy_to_selected_button";
550         ot->description = "Copy property from this object to selected objects or bones";
551
552         /* callbacks */
553         ot->poll = copy_to_selected_button_poll;
554         ot->exec = copy_to_selected_button_exec;
555
556         /* flags */
557         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
558
559         /* properties */
560         RNA_def_boolean(ot->srna, "all", 1, "All", "Reset to default values all elements of the array");
561 }
562
563 /* Reports to Textblock Operator ------------------------ */
564
565 /* FIXME: this is just a temporary operator so that we can see all the reports somewhere 
566  * when there are too many to display...
567  */
568
569 static int reports_to_text_poll(bContext *C)
570 {
571         return CTX_wm_reports(C) != NULL;
572 }
573
574 static int reports_to_text_exec(bContext *C, wmOperator *UNUSED(op))
575 {
576         ReportList *reports = CTX_wm_reports(C);
577         Main *bmain = CTX_data_main(C);
578         Text *txt;
579         char *str;
580         
581         /* create new text-block to write to */
582         txt = BKE_text_add(bmain, "Recent Reports");
583         
584         /* convert entire list to a display string, and add this to the text-block
585          *      - if commandline debug option enabled, show debug reports too
586          *      - otherwise, up to info (which is what users normally see)
587          */
588         str = BKE_reports_string(reports, (G.debug & G_DEBUG) ? RPT_DEBUG : RPT_INFO);
589
590         if (str) {
591                 BKE_text_write(txt, str);
592                 MEM_freeN(str);
593
594                 return OPERATOR_FINISHED;
595         }
596         else {
597                 return OPERATOR_CANCELLED;
598         }
599 }
600
601 static void UI_OT_reports_to_textblock(wmOperatorType *ot)
602 {
603         /* identifiers */
604         ot->name = "Reports to Text Block";
605         ot->idname = "UI_OT_reports_to_textblock";
606         ot->description = "Write the reports ";
607         
608         /* callbacks */
609         ot->poll = reports_to_text_poll;
610         ot->exec = reports_to_text_exec;
611 }
612
613 #ifdef WITH_PYTHON
614
615 /* ------------------------------------------------------------------------- */
616 /* EditSource Utility funcs and operator,
617  * note, this includes utility functions and button matching checks */
618
619 typedef struct uiEditSourceStore {
620         uiBut but_orig;
621         GHash *hash;
622 } uiEditSourceStore;
623
624 typedef struct uiEditSourceButStore {
625         char py_dbg_fn[FILE_MAX];
626         int py_dbg_ln;
627 } uiEditSourceButStore;
628
629 /* should only ever be set while the edit source operator is running */
630 static struct uiEditSourceStore *ui_editsource_info = NULL;
631
632 bool UI_editsource_enable_check(void)
633 {
634         return (ui_editsource_info != NULL);
635 }
636
637 static void ui_editsource_active_but_set(uiBut *but)
638 {
639         BLI_assert(ui_editsource_info == NULL);
640
641         ui_editsource_info = MEM_callocN(sizeof(uiEditSourceStore), __func__);
642         memcpy(&ui_editsource_info->but_orig, but, sizeof(uiBut));
643
644         ui_editsource_info->hash = BLI_ghash_ptr_new(__func__);
645 }
646
647 static void ui_editsource_active_but_clear(void)
648 {
649         BLI_ghash_free(ui_editsource_info->hash, NULL, MEM_freeN);
650         MEM_freeN(ui_editsource_info);
651         ui_editsource_info = NULL;
652 }
653
654 static bool ui_editsource_uibut_match(uiBut *but_a, uiBut *but_b)
655 {
656 #if 0
657         printf("matching buttons: '%s' == '%s'\n",
658                but_a->drawstr, but_b->drawstr);
659 #endif
660
661         /* this just needs to be a 'good-enough' comparison so we can know beyond
662          * reasonable doubt that these buttons are the same between redraws.
663          * if this fails it only means edit-source fails - campbell */
664         if (BLI_rctf_compare(&but_a->rect, &but_b->rect, FLT_EPSILON) &&
665             (but_a->type == but_b->type) &&
666             (but_a->rnaprop == but_b->rnaprop) &&
667             (but_a->optype == but_b->optype) &&
668             (but_a->unit_type == but_b->unit_type) &&
669             STREQLEN(but_a->drawstr, but_b->drawstr, UI_MAX_DRAW_STR))
670         {
671                 return true;
672         }
673         else {
674                 return false;
675         }
676 }
677
678 void UI_editsource_active_but_test(uiBut *but)
679 {
680         extern void PyC_FileAndNum_Safe(const char **filename, int *lineno);
681
682         struct uiEditSourceButStore *but_store = MEM_callocN(sizeof(uiEditSourceButStore), __func__);
683
684         const char *fn;
685         int lineno = -1;
686
687 #if 0
688         printf("comparing buttons: '%s' == '%s'\n",
689                but->drawstr, ui_editsource_info->but_orig.drawstr);
690 #endif
691
692         PyC_FileAndNum_Safe(&fn, &lineno);
693
694         if (lineno != -1) {
695                 BLI_strncpy(but_store->py_dbg_fn, fn,
696                             sizeof(but_store->py_dbg_fn));
697                 but_store->py_dbg_ln = lineno;
698         }
699         else {
700                 but_store->py_dbg_fn[0] = '\0';
701                 but_store->py_dbg_ln = -1;
702         }
703
704         BLI_ghash_insert(ui_editsource_info->hash, but, but_store);
705 }
706
707 static int editsource_text_edit(
708         bContext *C, wmOperator *op,
709         char filepath[FILE_MAX], int line)
710 {
711         struct Main *bmain = CTX_data_main(C);
712         Text *text;
713
714         for (text = bmain->text.first; text; text = text->id.next) {
715                 if (text->name && BLI_path_cmp(text->name, filepath) == 0) {
716                         break;
717                 }
718         }
719
720         if (text == NULL) {
721                 text = BKE_text_load(bmain, filepath, bmain->name);
722         }
723
724         if (text == NULL) {
725                 BKE_reportf(op->reports, RPT_WARNING, "File '%s' cannot be opened", filepath);
726                 return OPERATOR_CANCELLED;
727         }
728         else {
729                 /* naughty!, find text area to set, not good behavior
730                  * but since this is a dev tool lets allow it - campbell */
731                 ScrArea *sa = BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_TEXT, 0);
732                 if (sa) {
733                         SpaceText *st = sa->spacedata.first;
734                         st->text = text;
735                 }
736                 else {
737                         BKE_reportf(op->reports, RPT_INFO, "See '%s' in the text editor", text->id.name + 2);
738                 }
739
740                 txt_move_toline(text, line - 1, false);
741                 WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, text);
742         }
743
744         return OPERATOR_FINISHED;
745 }
746
747 static int editsource_exec(bContext *C, wmOperator *op)
748 {
749         uiBut *but = UI_context_active_but_get(C);
750
751         if (but) {
752                 GHashIterator ghi;
753                 struct uiEditSourceButStore *but_store = NULL;
754
755                 ARegion *ar = CTX_wm_region(C);
756                 int ret;
757
758                 /* needed else the active button does not get tested */
759                 UI_screen_free_active_but(C, CTX_wm_screen(C));
760
761                 // printf("%s: begin\n", __func__);
762
763                 /* take care not to return before calling ui_editsource_active_but_clear */
764                 ui_editsource_active_but_set(but);
765
766                 /* redraw and get active button python info */
767                 ED_region_do_draw(C, ar);
768                 ar->do_draw = false;
769
770                 for (BLI_ghashIterator_init(&ghi, ui_editsource_info->hash);
771                      BLI_ghashIterator_done(&ghi) == false;
772                      BLI_ghashIterator_step(&ghi))
773                 {
774                         uiBut *but_key = BLI_ghashIterator_getKey(&ghi);
775                         if (but_key && ui_editsource_uibut_match(&ui_editsource_info->but_orig, but_key)) {
776                                 but_store = BLI_ghashIterator_getValue(&ghi);
777                                 break;
778                         }
779
780                 }
781
782                 if (but_store) {
783                         if (but_store->py_dbg_ln != -1) {
784                                 ret = editsource_text_edit(C, op,
785                                                            but_store->py_dbg_fn,
786                                                            but_store->py_dbg_ln);
787                         }
788                         else {
789                                 BKE_report(op->reports, RPT_ERROR, "Active button is not from a script, cannot edit source");
790                                 ret = OPERATOR_CANCELLED;
791                         }
792                 }
793                 else {
794                         BKE_report(op->reports, RPT_ERROR, "Active button match cannot be found");
795                         ret = OPERATOR_CANCELLED;
796                 }
797
798
799                 ui_editsource_active_but_clear();
800
801                 // printf("%s: end\n", __func__);
802
803                 return ret;
804         }
805         else {
806                 BKE_report(op->reports, RPT_ERROR, "Active button not found");
807                 return OPERATOR_CANCELLED;
808         }
809 }
810
811 static void UI_OT_editsource(wmOperatorType *ot)
812 {
813         /* identifiers */
814         ot->name = "Edit Source";
815         ot->idname = "UI_OT_editsource";
816         ot->description = "Edit UI source code of the active button";
817
818         /* callbacks */
819         ot->exec = editsource_exec;
820 }
821
822 /* ------------------------------------------------------------------------- */
823
824 /**
825  * EditTranslation utility funcs and operator,
826  * \note: this includes utility functions and button matching checks.
827  * this only works in conjunction with a py operator!
828  */
829 static void edittranslation_find_po_file(const char *root, const char *uilng, char *path, const size_t maxlen)
830 {
831         char tstr[32]; /* Should be more than enough! */
832
833         /* First, full lang code. */
834         BLI_snprintf(tstr, sizeof(tstr), "%s.po", uilng);
835         BLI_join_dirfile(path, maxlen, root, uilng);
836         BLI_path_append(path, maxlen, tstr);
837         if (BLI_is_file(path))
838                 return;
839
840         /* Now try without the second iso code part (_ES in es_ES). */
841         {
842                 const char *tc = NULL;
843                 size_t szt = 0;
844                 tstr[0] = '\0';
845
846                 tc = strchr(uilng, '_');
847                 if (tc) {
848                         szt = tc - uilng;
849                         if (szt < sizeof(tstr)) /* Paranoid, should always be true! */
850                                 BLI_strncpy(tstr, uilng, szt + 1); /* +1 for '\0' char! */
851                 }
852                 if (tstr[0]) {
853                         /* Because of some codes like sr_SR@latin... */
854                         tc = strchr(uilng, '@');
855                         if (tc)
856                                 BLI_strncpy(tstr + szt, tc, sizeof(tstr) - szt);
857
858                         BLI_join_dirfile(path, maxlen, root, tstr);
859                         strcat(tstr, ".po");
860                         BLI_path_append(path, maxlen, tstr);
861                         if (BLI_is_file(path))
862                                 return;
863                 }
864         }
865
866         /* Else no po file! */
867         path[0] = '\0';
868 }
869
870 static int edittranslation_exec(bContext *C, wmOperator *op)
871 {
872         uiBut *but = UI_context_active_but_get(C);
873         int ret = OPERATOR_CANCELLED;
874
875         if (but) {
876                 wmOperatorType *ot;
877                 PointerRNA ptr;
878                 char popath[FILE_MAX];
879                 const char *root = U.i18ndir;
880                 const char *uilng = BLT_lang_get();
881
882                 uiStringInfo but_label = {BUT_GET_LABEL, NULL};
883                 uiStringInfo rna_label = {BUT_GET_RNA_LABEL, NULL};
884                 uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, NULL};
885                 uiStringInfo but_tip = {BUT_GET_TIP, NULL};
886                 uiStringInfo rna_tip = {BUT_GET_RNA_TIP, NULL};
887                 uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, NULL};
888                 uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, NULL};
889                 uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, NULL};
890                 uiStringInfo rna_enum = {BUT_GET_RNAENUM_IDENTIFIER, NULL};
891                 uiStringInfo rna_ctxt = {BUT_GET_RNA_LABEL_CONTEXT, NULL};
892
893                 if (!BLI_is_dir(root)) {
894                         BKE_report(op->reports, RPT_ERROR, "Please set your User Preferences' 'Translation Branches "
895                                                            "Directory' path to a valid directory");
896                         return OPERATOR_CANCELLED;
897                 }
898                 ot = WM_operatortype_find(EDTSRC_I18N_OP_NAME, 0);
899                 if (ot == NULL) {
900                         BKE_reportf(op->reports, RPT_ERROR, "Could not find operator '%s'! Please enable ui_translate addon "
901                                                             "in the User Preferences", EDTSRC_I18N_OP_NAME);
902                         return OPERATOR_CANCELLED;
903                 }
904                 /* Try to find a valid po file for current language... */
905                 edittranslation_find_po_file(root, uilng, popath, FILE_MAX);
906 /*              printf("po path: %s\n", popath);*/
907                 if (popath[0] == '\0') {
908                         BKE_reportf(op->reports, RPT_ERROR, "No valid po found for language '%s' under %s", uilng, root);
909                         return OPERATOR_CANCELLED;
910                 }
911
912                 UI_but_string_info_get(C, but, &but_label, &rna_label, &enum_label, &but_tip, &rna_tip, &enum_tip,
913                                 &rna_struct, &rna_prop, &rna_enum, &rna_ctxt, NULL);
914
915                 WM_operator_properties_create_ptr(&ptr, ot);
916                 RNA_string_set(&ptr, "lang", uilng);
917                 RNA_string_set(&ptr, "po_file", popath);
918                 RNA_string_set(&ptr, "but_label", but_label.strinfo);
919                 RNA_string_set(&ptr, "rna_label", rna_label.strinfo);
920                 RNA_string_set(&ptr, "enum_label", enum_label.strinfo);
921                 RNA_string_set(&ptr, "but_tip", but_tip.strinfo);
922                 RNA_string_set(&ptr, "rna_tip", rna_tip.strinfo);
923                 RNA_string_set(&ptr, "enum_tip", enum_tip.strinfo);
924                 RNA_string_set(&ptr, "rna_struct", rna_struct.strinfo);
925                 RNA_string_set(&ptr, "rna_prop", rna_prop.strinfo);
926                 RNA_string_set(&ptr, "rna_enum", rna_enum.strinfo);
927                 RNA_string_set(&ptr, "rna_ctxt", rna_ctxt.strinfo);
928                 ret = WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr);
929
930                 /* Clean up */
931                 if (but_label.strinfo)
932                         MEM_freeN(but_label.strinfo);
933                 if (rna_label.strinfo)
934                         MEM_freeN(rna_label.strinfo);
935                 if (enum_label.strinfo)
936                         MEM_freeN(enum_label.strinfo);
937                 if (but_tip.strinfo)
938                         MEM_freeN(but_tip.strinfo);
939                 if (rna_tip.strinfo)
940                         MEM_freeN(rna_tip.strinfo);
941                 if (enum_tip.strinfo)
942                         MEM_freeN(enum_tip.strinfo);
943                 if (rna_struct.strinfo)
944                         MEM_freeN(rna_struct.strinfo);
945                 if (rna_prop.strinfo)
946                         MEM_freeN(rna_prop.strinfo);
947                 if (rna_enum.strinfo)
948                         MEM_freeN(rna_enum.strinfo);
949                 if (rna_ctxt.strinfo)
950                         MEM_freeN(rna_ctxt.strinfo);
951
952                 return ret;
953         }
954         else {
955                 BKE_report(op->reports, RPT_ERROR, "Active button not found");
956                 return OPERATOR_CANCELLED;
957         }
958 }
959
960 static void UI_OT_edittranslation_init(wmOperatorType *ot)
961 {
962         /* identifiers */
963         ot->name = "Edit Translation";
964         ot->idname = "UI_OT_edittranslation_init";
965         ot->description = "Edit i18n in current language for the active button";
966
967         /* callbacks */
968         ot->exec = edittranslation_exec;
969 }
970
971 #endif /* WITH_PYTHON */
972
973 static int reloadtranslation_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
974 {
975         BLT_lang_init();
976         BLF_cache_clear();
977         BLT_lang_set(NULL);
978         UI_reinit_font();
979         return OPERATOR_FINISHED;
980 }
981
982 static void UI_OT_reloadtranslation(wmOperatorType *ot)
983 {
984         /* identifiers */
985         ot->name = "Reload Translation";
986         ot->idname = "UI_OT_reloadtranslation";
987         ot->description = "Force a full reload of UI translation";
988
989         /* callbacks */
990         ot->exec = reloadtranslation_exec;
991 }
992
993 int UI_drop_color_poll(struct bContext *C, wmDrag *drag, const wmEvent *UNUSED(event))
994 {
995         /* should only return true for regions that include buttons, for now
996          * return true always */
997         if (drag->type == WM_DRAG_COLOR) {
998                 SpaceImage *sima = CTX_wm_space_image(C);
999                 ARegion *ar = CTX_wm_region(C);
1000
1001                 if (UI_but_active_drop_color(C))
1002                         return 1;
1003
1004                 if (sima && (sima->mode == SI_MODE_PAINT) &&
1005                     sima->image && (ar && ar->regiontype == RGN_TYPE_WINDOW))
1006                 {
1007                         return 1;
1008                 }
1009         }
1010
1011         return 0;
1012 }
1013
1014 void UI_drop_color_copy(wmDrag *drag, wmDropBox *drop)
1015 {
1016         uiDragColorHandle *drag_info = drag->poin;
1017
1018         RNA_float_set_array(drop->ptr, "color", drag_info->color);
1019         RNA_boolean_set(drop->ptr, "gamma", drag_info->gamma_corrected);
1020 }
1021
1022 static int drop_color_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1023 {
1024         ARegion *ar = CTX_wm_region(C);
1025         uiBut *but = NULL;
1026         float color[4];
1027         bool gamma;
1028
1029         RNA_float_get_array(op->ptr, "color", color);
1030         gamma = RNA_boolean_get(op->ptr, "gamma");
1031
1032         /* find button under mouse, check if it has RNA color property and
1033          * if it does copy the data */
1034         but = ui_but_find_active_in_region(ar);
1035
1036         if (but && but->type == UI_BTYPE_COLOR && but->rnaprop) {
1037                 const int color_len = RNA_property_array_length(&but->rnapoin, but->rnaprop);
1038                 BLI_assert(color_len <= 4);
1039
1040                 /* keep alpha channel as-is */
1041                 if (color_len == 4) {
1042                         color[3] = RNA_property_float_get_index(&but->rnapoin, but->rnaprop, 3);
1043                 }
1044
1045                 if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) {
1046                         if (!gamma)
1047                                 ui_block_cm_to_display_space_v3(but->block, color);
1048                         RNA_property_float_set_array(&but->rnapoin, but->rnaprop, color);
1049                         RNA_property_update(C, &but->rnapoin, but->rnaprop);
1050                 }
1051                 else if (RNA_property_subtype(but->rnaprop) == PROP_COLOR) {
1052                         if (gamma)
1053                                 ui_block_cm_to_scene_linear_v3(but->block, color);
1054                         RNA_property_float_set_array(&but->rnapoin, but->rnaprop, color);
1055                         RNA_property_update(C, &but->rnapoin, but->rnaprop);
1056                 }
1057         }
1058         else {
1059                 if (gamma) {
1060                         srgb_to_linearrgb_v3_v3(color, color);
1061                 }
1062
1063                 ED_imapaint_bucket_fill(C, color, op);
1064         }
1065
1066         ED_region_tag_redraw(ar);
1067
1068         return OPERATOR_FINISHED;
1069 }
1070
1071
1072 static void UI_OT_drop_color(wmOperatorType *ot)
1073 {
1074         ot->name = "Drop Color";
1075         ot->idname = "UI_OT_drop_color";
1076         ot->description = "Drop colors to buttons";
1077
1078         ot->invoke = drop_color_invoke;
1079         ot->flag = OPTYPE_INTERNAL;
1080
1081         RNA_def_float_color(ot->srna, "color", 3, NULL, 0.0, FLT_MAX, "Color", "Source color", 0.0, 1.0);
1082         RNA_def_boolean(ot->srna, "gamma", 0, "Gamma Corrected", "The source color is gamma corrected ");
1083 }
1084
1085
1086
1087 /* ********************************************************* */
1088 /* Registration */
1089
1090 void ED_operatortypes_ui(void)
1091 {
1092         WM_operatortype_append(UI_OT_reset_default_theme);
1093         WM_operatortype_append(UI_OT_copy_data_path_button);
1094         WM_operatortype_append(UI_OT_copy_python_command_button);
1095         WM_operatortype_append(UI_OT_reset_default_button);
1096         WM_operatortype_append(UI_OT_unset_property_button);
1097         WM_operatortype_append(UI_OT_copy_to_selected_button);
1098         WM_operatortype_append(UI_OT_reports_to_textblock);  /* XXX: temp? */
1099         WM_operatortype_append(UI_OT_drop_color);
1100 #ifdef WITH_PYTHON
1101         WM_operatortype_append(UI_OT_editsource);
1102         WM_operatortype_append(UI_OT_edittranslation_init);
1103 #endif
1104         WM_operatortype_append(UI_OT_reloadtranslation);
1105
1106         /* external */
1107         WM_operatortype_append(UI_OT_eyedropper_color);
1108         WM_operatortype_append(UI_OT_eyedropper_id);
1109         WM_operatortype_append(UI_OT_eyedropper_depth);
1110 }
1111
1112 /**
1113  * \brief User Interface Keymap
1114  *
1115  * For now only modal maps here, since UI uses special ui-handlers instead of operators.
1116  */
1117 void ED_keymap_ui(wmKeyConfig *keyconf)
1118 {
1119         WM_keymap_find(keyconf, "User Interface", 0, 0);
1120 }