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