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