style cleanup
[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
31 #include <stdio.h>
32 #include <math.h>
33 #include <string.h>
34
35 #include "MEM_guardedalloc.h"
36
37 #include "DNA_scene_types.h"
38 #include "DNA_screen_types.h"
39 #include "DNA_text_types.h" /* for UI_OT_reports_to_text */
40
41 #include "BLI_blenlib.h"
42 #include "BLI_math_color.h"
43 #include "BLI_math_vector.h"
44 #include "BLI_utildefines.h"
45
46 #include "BLF_api.h"
47 #include "BLF_translation.h"
48
49 #include "BKE_context.h"
50 #include "BKE_screen.h"
51 #include "BKE_global.h"
52 #include "BKE_text.h" /* for UI_OT_reports_to_text */
53 #include "BKE_report.h"
54
55 #include "RNA_access.h"
56 #include "RNA_define.h"
57
58 #include "BIF_gl.h"
59
60 #include "UI_interface.h"
61
62 #include "interface_intern.h"
63
64 #include "WM_api.h"
65 #include "WM_types.h"
66
67 /* only for UI_OT_editsource */
68 #include "ED_screen.h"
69 #include "BKE_main.h"
70 #include "BLI_ghash.h"
71
72 #include "ED_image.h"  /* for HDR color sampling */
73 #include "ED_node.h"   /* for HDR color sampling */
74
75 /* ********************************************************** */
76
77 typedef struct Eyedropper {
78         short do_color_management;
79
80         PointerRNA ptr;
81         PropertyRNA *prop;
82         int index;
83
84         int   accum_start; /* has mouse been presed */
85         float accum_col[3];
86         int   accum_tot;
87 } Eyedropper;
88
89 static int eyedropper_init(bContext *C, wmOperator *op)
90 {
91         Scene *scene = CTX_data_scene(C);
92         const int color_manage = scene->r.color_mgt_flag & R_COLOR_MANAGEMENT;
93
94         Eyedropper *eye;
95         
96         op->customdata = eye = MEM_callocN(sizeof(Eyedropper), "Eyedropper");
97         
98         uiContextActiveProperty(C, &eye->ptr, &eye->prop, &eye->index);
99
100         if ((eye->ptr.data == NULL) ||
101             (eye->prop == NULL) ||
102             (RNA_property_editable(&eye->ptr, eye->prop) == FALSE) ||
103             (RNA_property_array_length(&eye->ptr, eye->prop) < 3) ||
104             (RNA_property_type(eye->prop) != PROP_FLOAT))
105         {
106                 return FALSE;
107         }
108
109         eye->do_color_management = (color_manage && RNA_property_subtype(eye->prop) == PROP_COLOR);
110
111         return TRUE;
112 }
113
114 static void eyedropper_exit(bContext *C, wmOperator *op)
115 {
116         WM_cursor_restore(CTX_wm_window(C));
117         
118         if (op->customdata)
119                 MEM_freeN(op->customdata);
120         op->customdata = NULL;
121 }
122
123 static int eyedropper_cancel(bContext *C, wmOperator *op)
124 {
125         eyedropper_exit(C, op);
126         return OPERATOR_CANCELLED;
127 }
128
129 /* *** eyedropper_color_ helper functions *** */
130
131 /**
132  * \brief get the color from the screen.
133  *
134  * Special check for image or nodes where we MAY have HDR pixels which don't display.
135  */
136 static void eyedropper_color_sample_fl(bContext *C, Eyedropper *UNUSED(eye), int mx, int my, float r_col[3])
137 {
138
139         /* we could use some clever */
140         wmWindow *win = CTX_wm_window(C);
141         ScrArea *sa;
142         for (sa = win->screen->areabase.first; sa; sa = sa->next) {
143                 if (BLI_in_rcti(&sa->totrct, mx, my)) {
144                         if (sa->spacetype == SPACE_IMAGE) {
145                                 ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
146                                 if (BLI_in_rcti(&ar->winrct, mx, my)) {
147                                         SpaceImage *sima = sa->spacedata.first;
148                                         int mval[2] = {mx - ar->winrct.xmin,
149                                                        my - ar->winrct.ymin};
150
151                                         if (ED_space_image_color_sample(sima, ar, mval, r_col)) {
152                                                 return;
153                                         }
154                                 }
155                         }
156                         else if (sa->spacetype == SPACE_NODE) {
157                                 ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
158                                 if (BLI_in_rcti(&ar->winrct, mx, my)) {
159                                         SpaceNode *snode = sa->spacedata.first;
160                                         int mval[2] = {mx - ar->winrct.xmin,
161                                                        my - ar->winrct.ymin};
162
163                                         if (ED_space_node_color_sample(snode, ar, mval, r_col)) {
164                                                 return;
165                                         }
166                                 }
167                         }
168                 }
169         }
170
171         /* fallback to simple opengl picker */
172         glReadBuffer(GL_FRONT);
173         glReadPixels(mx, my, 1, 1, GL_RGB, GL_FLOAT, r_col);
174         glReadBuffer(GL_BACK);
175 }
176
177 /* sets the sample color RGB, maintaining A */
178 static void eyedropper_color_set(bContext *C, Eyedropper *eye, const float col[3])
179 {
180         float col_conv[4];
181
182         /* to maintain alpha */
183         RNA_property_float_get_array(&eye->ptr, eye->prop, col_conv);
184
185         /* convert from screen (srgb) space to linear rgb space */
186         if (eye->do_color_management) {
187                 srgb_to_linearrgb_v3_v3(col_conv, col);
188         }
189         else {
190                 copy_v3_v3(col_conv, col);
191         }
192
193         RNA_property_float_set_array(&eye->ptr, eye->prop, col_conv);
194
195         RNA_property_update(C, &eye->ptr, eye->prop);
196 }
197
198 /* set sample from accumulated values */
199 static void eyedropper_color_set_accum(bContext *C, Eyedropper *eye)
200 {
201         float col[4];
202         mul_v3_v3fl(col, eye->accum_col, 1.0f / (float)eye->accum_tot);
203         eyedropper_color_set(C, eye, col);
204 }
205
206 /* single point sample & set */
207 static void eyedropper_color_sample(bContext *C, Eyedropper *eye, int mx, int my)
208 {
209         float col[3];
210         eyedropper_color_sample_fl(C, eye, mx, my, col);
211         eyedropper_color_set(C, eye, col);
212 }
213
214 static void eyedropper_color_sample_accum(bContext *C, Eyedropper *eye, int mx, int my)
215 {
216         float col[3];
217         eyedropper_color_sample_fl(C, eye, mx, my, col);
218         /* delay linear conversion */
219         add_v3_v3(eye->accum_col, col);
220         eye->accum_tot++;
221 }
222
223 /* main modal status check */
224 static int eyedropper_modal(bContext *C, wmOperator *op, wmEvent *event)
225 {
226         Eyedropper *eye = (Eyedropper *)op->customdata;
227         
228         switch (event->type) {
229                 case ESCKEY:
230                 case RIGHTMOUSE:
231                         return eyedropper_cancel(C, op);
232                 case LEFTMOUSE:
233                         if (event->val == KM_RELEASE) {
234                                 if (eye->accum_tot == 0) {
235                                         eyedropper_color_sample(C, eye, event->x, event->y);
236                                 }
237                                 else {
238                                         eyedropper_color_set_accum(C, eye);
239                                 }
240                                 eyedropper_exit(C, op);
241                                 return OPERATOR_FINISHED;
242                         }
243                         else if (event->val == KM_PRESS) {
244                                 /* enable accum and make first sample */
245                                 eye->accum_start = TRUE;
246                                 eyedropper_color_sample_accum(C, eye, event->x, event->y);
247                         }
248                         break;
249                 case MOUSEMOVE:
250                         if (eye->accum_start) {
251                                 /* button is pressed so keep sampling */
252                                 eyedropper_color_sample_accum(C, eye, event->x, event->y);
253                                 eyedropper_color_set_accum(C, eye);
254                         }
255                         break;
256                 case SPACEKEY:
257                         if (event->val == KM_RELEASE) {
258                                 eye->accum_tot = 0;
259                                 zero_v3(eye->accum_col);
260                                 eyedropper_color_sample_accum(C, eye, event->x, event->y);
261                                 eyedropper_color_set_accum(C, eye);
262                         }
263                         break;
264         }
265         
266         return OPERATOR_RUNNING_MODAL;
267 }
268
269 /* Modal Operator init */
270 static int eyedropper_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
271 {
272         /* init */
273         if (eyedropper_init(C, op)) {
274                 WM_cursor_modal(CTX_wm_window(C), BC_EYEDROPPER_CURSOR);
275
276                 /* add temp handler */
277                 WM_event_add_modal_handler(C, op);
278                 
279                 return OPERATOR_RUNNING_MODAL;
280         }
281         else {
282                 eyedropper_exit(C, op);
283                 return OPERATOR_CANCELLED;
284         }
285 }
286
287 /* Repeat operator */
288 static int eyedropper_exec(bContext *C, wmOperator *op)
289 {
290         /* init */
291         if (eyedropper_init(C, op)) {
292                 
293                 /* do something */
294                 
295                 /* cleanup */
296                 eyedropper_exit(C, op);
297                 
298                 return OPERATOR_FINISHED;
299         }
300         else {
301                 return OPERATOR_CANCELLED;
302         }
303 }
304
305 static int eyedropper_poll(bContext *C)
306 {
307         if (!CTX_wm_window(C)) return 0;
308         else return 1;
309 }
310
311 static void UI_OT_eyedropper(wmOperatorType *ot)
312 {
313         /* identifiers */
314         ot->name = "Eyedropper";
315         ot->idname = "UI_OT_eyedropper";
316         ot->description = "Sample a color from the Blender Window to store in a property";
317         
318         /* api callbacks */
319         ot->invoke = eyedropper_invoke;
320         ot->modal = eyedropper_modal;
321         ot->cancel = eyedropper_cancel;
322         ot->exec = eyedropper_exec;
323         ot->poll = eyedropper_poll;
324         
325         /* flags */
326         ot->flag = OPTYPE_BLOCKING;
327         
328         /* properties */
329 }
330
331 /* Reset Default Theme ------------------------ */
332
333 static int reset_default_theme_exec(bContext *C, wmOperator *UNUSED(op))
334 {
335         ui_theme_init_default();
336         WM_event_add_notifier(C, NC_WINDOW, NULL);
337         
338         return OPERATOR_FINISHED;
339 }
340
341 static void UI_OT_reset_default_theme(wmOperatorType *ot)
342 {
343         /* identifiers */
344         ot->name = "Reset to Default Theme";
345         ot->idname = "UI_OT_reset_default_theme";
346         ot->description = "Reset to the default theme colors";
347         
348         /* callbacks */
349         ot->exec = reset_default_theme_exec;
350         
351         /* flags */
352         ot->flag = OPTYPE_REGISTER;
353 }
354
355 /* Copy Data Path Operator ------------------------ */
356
357 static int copy_data_path_button_poll(bContext *C)
358 {
359         PointerRNA ptr;
360         PropertyRNA *prop;
361         char *path;
362         int index;
363
364         uiContextActiveProperty(C, &ptr, &prop, &index);
365
366         if (ptr.id.data && ptr.data && prop) {
367                 path = RNA_path_from_ID_to_property(&ptr, prop);
368                 
369                 if (path) {
370                         MEM_freeN(path);
371                         return 1;
372                 }
373         }
374
375         return 0;
376 }
377
378 static int copy_data_path_button_exec(bContext *C, wmOperator *UNUSED(op))
379 {
380         PointerRNA ptr;
381         PropertyRNA *prop;
382         char *path;
383         int index;
384
385         /* try to create driver using property retrieved from UI */
386         uiContextActiveProperty(C, &ptr, &prop, &index);
387
388         if (ptr.id.data && ptr.data && prop) {
389                 path = RNA_path_from_ID_to_property(&ptr, prop);
390                 
391                 if (path) {
392                         WM_clipboard_text_set(path, FALSE);
393                         MEM_freeN(path);
394                         return OPERATOR_FINISHED;
395                 }
396         }
397
398         return OPERATOR_CANCELLED;
399 }
400
401 static void UI_OT_copy_data_path_button(wmOperatorType *ot)
402 {
403         /* identifiers */
404         ot->name = "Copy Data Path";
405         ot->idname = "UI_OT_copy_data_path_button";
406         ot->description = "Copy the RNA data path for this property to the clipboard";
407
408         /* callbacks */
409         ot->exec = copy_data_path_button_exec;
410         ot->poll = copy_data_path_button_poll;
411
412         /* flags */
413         ot->flag = OPTYPE_REGISTER;
414 }
415
416 /* Reset to Default Values Button Operator ------------------------ */
417
418 static int reset_default_button_poll(bContext *C)
419 {
420         PointerRNA ptr;
421         PropertyRNA *prop;
422         int index;
423
424         uiContextActiveProperty(C, &ptr, &prop, &index);
425         
426         return (ptr.data && prop && RNA_property_editable(&ptr, prop));
427 }
428
429 static int reset_default_button_exec(bContext *C, wmOperator *op)
430 {
431         PointerRNA ptr;
432         PropertyRNA *prop;
433         int success = 0;
434         int index, all = RNA_boolean_get(op->ptr, "all");
435
436         /* try to reset the nominated setting to its default value */
437         uiContextActiveProperty(C, &ptr, &prop, &index);
438         
439         /* if there is a valid property that is editable... */
440         if (ptr.data && prop && RNA_property_editable(&ptr, prop)) {
441                 if (RNA_property_reset(&ptr, prop, (all) ? -1 : index)) {
442                         /* perform updates required for this property */
443                         RNA_property_update(C, &ptr, prop);
444
445                         /* as if we pressed the button */
446                         uiContextActivePropertyHandle(C);
447
448                         success = 1;
449                 }
450         }
451
452         /* Since we don't want to undo _all_ edits to settings, eg window
453          * edits on the screen or on operator settings.
454          * it might be better to move undo's inline - campbell */
455         if (success) {
456                 ID *id = ptr.id.data;
457                 if (id && ID_CHECK_UNDO(id)) {
458                         /* do nothing, go ahead with undo */
459                 }
460                 else {
461                         return OPERATOR_CANCELLED;
462                 }
463         }
464         /* end hack */
465
466         return (success) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
467 }
468
469 static void UI_OT_reset_default_button(wmOperatorType *ot)
470 {
471         /* identifiers */
472         ot->name = "Reset to Default Value";
473         ot->idname = "UI_OT_reset_default_button";
474         ot->description = "Reset this property's value to its default value";
475
476         /* callbacks */
477         ot->poll = reset_default_button_poll;
478         ot->exec = reset_default_button_exec;
479
480         /* flags */
481         ot->flag = OPTYPE_UNDO;
482         
483         /* properties */
484         RNA_def_boolean(ot->srna, "all", 1, "All", "Reset to default values all elements of the array");
485 }
486
487 /* Copy To Selected Operator ------------------------ */
488
489 static int copy_to_selected_list(bContext *C, PointerRNA *ptr, ListBase *lb, int *use_path)
490 {
491         *use_path = FALSE;
492
493         if (RNA_struct_is_a(ptr->type, &RNA_EditBone))
494                 *lb = CTX_data_collection_get(C, "selected_editable_bones");
495         else if (RNA_struct_is_a(ptr->type, &RNA_PoseBone))
496                 *lb = CTX_data_collection_get(C, "selected_pose_bones");
497         else if (RNA_struct_is_a(ptr->type, &RNA_Sequence))
498                 *lb = CTX_data_collection_get(C, "selected_editable_sequences");
499         else {
500                 ID *id = ptr->id.data;
501
502                 if (id && GS(id->name) == ID_OB) {
503                         *lb = CTX_data_collection_get(C, "selected_editable_objects");
504                         *use_path = TRUE;
505                 }
506                 else
507                         return 0;
508         }
509         
510         return 1;
511 }
512
513 static int copy_to_selected_button_poll(bContext *C)
514 {
515         PointerRNA ptr, lptr, idptr;
516         PropertyRNA *prop, *lprop;
517         int index, success = 0;
518
519         uiContextActiveProperty(C, &ptr, &prop, &index);
520
521         if (ptr.data && prop) {
522                 char *path = NULL;
523                 int use_path;
524                 CollectionPointerLink *link;
525                 ListBase lb;
526
527                 if (!copy_to_selected_list(C, &ptr, &lb, &use_path))
528                         return success;
529
530                 if (!use_path || (path = RNA_path_from_ID_to_property(&ptr, prop))) {
531                         for (link = lb.first; link; link = link->next) {
532                                 if (link->ptr.data != ptr.data) {
533                                         if (use_path) {
534                                                 lprop = NULL;
535                                                 RNA_id_pointer_create(link->ptr.id.data, &idptr);
536                                                 RNA_path_resolve(&idptr, path, &lptr, &lprop);
537                                         }
538                                         else {
539                                                 lptr = link->ptr;
540                                                 lprop = prop;
541                                         }
542
543                                         if (lprop == prop) {
544                                                 if (RNA_property_editable(&lptr, prop))
545                                                         success = 1;
546                                         }
547                                 }
548                         }
549
550                         if (path)
551                                 MEM_freeN(path);
552                 }
553
554                 BLI_freelistN(&lb);
555         }
556
557         return success;
558 }
559
560 static int copy_to_selected_button_exec(bContext *C, wmOperator *op)
561 {
562         PointerRNA ptr, lptr, idptr;
563         PropertyRNA *prop, *lprop;
564         int success = 0;
565         int index, all = RNA_boolean_get(op->ptr, "all");
566
567         /* try to reset the nominated setting to its default value */
568         uiContextActiveProperty(C, &ptr, &prop, &index);
569         
570         /* if there is a valid property that is editable... */
571         if (ptr.data && prop) {
572                 char *path = NULL;
573                 int use_path;
574                 CollectionPointerLink *link;
575                 ListBase lb;
576
577                 if (!copy_to_selected_list(C, &ptr, &lb, &use_path))
578                         return success;
579
580                 if (!use_path || (path = RNA_path_from_ID_to_property(&ptr, prop))) {
581                         for (link = lb.first; link; link = link->next) {
582                                 if (link->ptr.data != ptr.data) {
583                                         if (use_path) {
584                                                 lprop = NULL;
585                                                 RNA_id_pointer_create(link->ptr.id.data, &idptr);
586                                                 RNA_path_resolve(&idptr, path, &lptr, &lprop);
587                                         }
588                                         else {
589                                                 lptr = link->ptr;
590                                                 lprop = prop;
591                                         }
592
593                                         if (lprop == prop) {
594                                                 if (RNA_property_editable(&lptr, lprop)) {
595                                                         if (RNA_property_copy(&lptr, &ptr, prop, (all) ? -1 : index)) {
596                                                                 RNA_property_update(C, &lptr, prop);
597                                                                 success = 1;
598                                                         }
599                                                 }
600                                         }
601                                 }
602                         }
603
604                         if (path)
605                                 MEM_freeN(path);
606                 }
607
608                 BLI_freelistN(&lb);
609         }
610         
611         return (success) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
612 }
613
614 static void UI_OT_copy_to_selected_button(wmOperatorType *ot)
615 {
616         /* identifiers */
617         ot->name = "Copy To Selected";
618         ot->idname = "UI_OT_copy_to_selected_button";
619         ot->description = "Copy property from this object to selected objects or bones";
620
621         /* callbacks */
622         ot->poll = copy_to_selected_button_poll;
623         ot->exec = copy_to_selected_button_exec;
624
625         /* flags */
626         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
627
628         /* properties */
629         RNA_def_boolean(ot->srna, "all", 1, "All", "Reset to default values all elements of the array");
630 }
631
632 /* Reports to Textblock Operator ------------------------ */
633
634 /* FIXME: this is just a temporary operator so that we can see all the reports somewhere 
635  * when there are too many to display...
636  */
637
638 static int reports_to_text_poll(bContext *C)
639 {
640         return CTX_wm_reports(C) != NULL;
641 }
642
643 static int reports_to_text_exec(bContext *C, wmOperator *UNUSED(op))
644 {
645         ReportList *reports = CTX_wm_reports(C);
646         Text *txt;
647         char *str;
648         
649         /* create new text-block to write to */
650         txt = BKE_text_add("Recent Reports");
651         
652         /* convert entire list to a display string, and add this to the text-block
653          *      - if commandline debug option enabled, show debug reports too
654          *      - otherwise, up to info (which is what users normally see)
655          */
656         str = BKE_reports_string(reports, (G.debug & G_DEBUG) ? RPT_DEBUG : RPT_INFO);
657
658         if (str) {
659                 BKE_text_write(txt, str);
660                 MEM_freeN(str);
661
662                 return OPERATOR_FINISHED;
663         }
664         else {
665                 return OPERATOR_CANCELLED;
666         }
667 }
668
669 static void UI_OT_reports_to_textblock(wmOperatorType *ot)
670 {
671         /* identifiers */
672         ot->name = "Reports to Text Block";
673         ot->idname = "UI_OT_reports_to_textblock";
674         ot->description = "Write the reports ";
675         
676         /* callbacks */
677         ot->poll = reports_to_text_poll;
678         ot->exec = reports_to_text_exec;
679 }
680
681 #ifdef WITH_PYTHON
682
683 /* ------------------------------------------------------------------------- */
684 /* EditSource Utility funcs and operator,
685  * note, this includes utility functions and button matching checks */
686
687 struct uiEditSourceStore {
688         uiBut but_orig;
689         GHash *hash;
690 } uiEditSourceStore;
691
692 struct uiEditSourceButStore {
693         char py_dbg_fn[FILE_MAX];
694         int py_dbg_ln;
695 } uiEditSourceButStore;
696
697 /* should only ever be set while the edit source operator is running */
698 static struct uiEditSourceStore *ui_editsource_info = NULL;
699
700 int  UI_editsource_enable_check(void)
701 {
702         return (ui_editsource_info != NULL);
703 }
704
705 static void ui_editsource_active_but_set(uiBut *but)
706 {
707         BLI_assert(ui_editsource_info == NULL);
708
709         ui_editsource_info = MEM_callocN(sizeof(uiEditSourceStore), __func__);
710         memcpy(&ui_editsource_info->but_orig, but, sizeof(uiBut));
711
712         ui_editsource_info->hash = BLI_ghash_ptr_new(__func__);
713 }
714
715 static void ui_editsource_active_but_clear(void)
716 {
717         BLI_ghash_free(ui_editsource_info->hash, NULL, (GHashValFreeFP)MEM_freeN);
718         MEM_freeN(ui_editsource_info);
719         ui_editsource_info = NULL;
720 }
721
722 static int ui_editsource_uibut_match(uiBut *but_a, uiBut *but_b)
723 {
724 #if 0
725         printf("matching buttons: '%s' == '%s'\n",
726                but_a->drawstr, but_b->drawstr);
727 #endif
728
729         /* this just needs to be a 'good-enough' comparison so we can know beyond
730          * reasonable doubt that these buttons are the same between redraws.
731          * if this fails it only means edit-source fails - campbell */
732         if (BLI_rctf_compare(&but_a->rect, &but_b->rect, FLT_EPSILON) &&
733             (but_a->type == but_b->type) &&
734             (but_a->rnaprop == but_b->rnaprop) &&
735             (but_a->optype == but_b->optype) &&
736             (but_a->unit_type == but_b->unit_type) &&
737             (strncmp(but_a->drawstr, but_b->drawstr, UI_MAX_DRAW_STR) == 0))
738         {
739                 return TRUE;
740         }
741         else {
742                 return FALSE;
743         }
744 }
745
746 void UI_editsource_active_but_test(uiBut *but)
747 {
748         extern void PyC_FileAndNum_Safe(const char **filename, int *lineno);
749
750         struct uiEditSourceButStore *but_store = MEM_callocN(sizeof(uiEditSourceButStore), __func__);
751
752         const char *fn;
753         int lineno = -1;
754
755 #if 0
756         printf("comparing buttons: '%s' == '%s'\n",
757                but->drawstr, ui_editsource_info->but_orig.drawstr);
758 #endif
759
760         PyC_FileAndNum_Safe(&fn, &lineno);
761
762         if (lineno != -1) {
763                 BLI_strncpy(but_store->py_dbg_fn, fn,
764                             sizeof(but_store->py_dbg_fn));
765                 but_store->py_dbg_ln = lineno;
766         }
767         else {
768                 but_store->py_dbg_fn[0] = '\0';
769                 but_store->py_dbg_ln = -1;
770         }
771
772         BLI_ghash_insert(ui_editsource_info->hash, but, but_store);
773 }
774
775 static int editsource_text_edit(bContext *C, wmOperator *op,
776                                 char filepath[FILE_MAX], int line)
777 {
778         struct Main *bmain = CTX_data_main(C);
779         Text *text;
780
781         for (text = bmain->text.first; text; text = text->id.next) {
782                 if (text->name && BLI_path_cmp(text->name, filepath) == 0) {
783                         break;
784                 }
785         }
786
787         if (text == NULL) {
788                 text = BKE_text_load(filepath, bmain->name);
789         }
790
791         if (text == NULL) {
792                 BKE_reportf(op->reports, RPT_WARNING,
793                             "file: '%s' can't be opened", filepath);
794                 return OPERATOR_CANCELLED;
795         }
796         else {
797                 /* naughty!, find text area to set, not good behavior
798                  * but since this is a dev tool lets allow it - campbell */
799                 ScrArea *sa = BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_TEXT, 0);
800                 if (sa) {
801                         SpaceText *st = sa->spacedata.first;
802                         st->text = text;
803                 }
804                 else {
805                         BKE_reportf(op->reports, RPT_INFO,
806                                     "See '%s' in the text editor", text->id.name + 2);
807                 }
808
809                 txt_move_toline(text, line - 1, FALSE);
810                 WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, text);
811         }
812
813         return OPERATOR_FINISHED;
814 }
815
816 static int editsource_exec(bContext *C, wmOperator *op)
817 {
818         uiBut *but = uiContextActiveButton(C);
819
820         if (but) {
821                 GHashIterator ghi;
822                 struct uiEditSourceButStore *but_store = NULL;
823
824                 ARegion *ar = CTX_wm_region(C);
825                 int ret;
826
827                 /* needed else the active button does not get tested */
828                 uiFreeActiveButtons(C, CTX_wm_screen(C));
829
830                 // printf("%s: begin\n", __func__);
831
832                 /* take care not to return before calling ui_editsource_active_but_clear */
833                 ui_editsource_active_but_set(but);
834
835                 /* redraw and get active button python info */
836                 ED_region_do_draw(C, ar);
837
838                 for (BLI_ghashIterator_init(&ghi, ui_editsource_info->hash);
839                      !BLI_ghashIterator_isDone(&ghi);
840                      BLI_ghashIterator_step(&ghi))
841                 {
842                         uiBut *but = BLI_ghashIterator_getKey(&ghi);
843                         if (but && ui_editsource_uibut_match(&ui_editsource_info->but_orig, but)) {
844                                 but_store = BLI_ghashIterator_getValue(&ghi);
845                                 break;
846                         }
847
848                 }
849
850                 if (but_store) {
851                         if (but_store->py_dbg_ln != -1) {
852                                 ret = editsource_text_edit(C, op,
853                                                            but_store->py_dbg_fn,
854                                                            but_store->py_dbg_ln);
855                         }
856                         else {
857                                 BKE_report(op->reports, RPT_ERROR,
858                                            "Active button isn't from a script, cant edit source.");
859                                 ret = OPERATOR_CANCELLED;
860                         }
861                 }
862                 else {
863                         BKE_report(op->reports, RPT_ERROR,
864                                    "Active button match can't be found.");
865                         ret = OPERATOR_CANCELLED;
866                 }
867
868
869                 ui_editsource_active_but_clear();
870
871                 // printf("%s: end\n", __func__);
872
873                 return ret;
874         }
875         else {
876                 BKE_report(op->reports, RPT_ERROR, "Active button not found");
877                 return OPERATOR_CANCELLED;
878         }
879 }
880
881 static void UI_OT_editsource(wmOperatorType *ot)
882 {
883         /* identifiers */
884         ot->name = "Edit Source";
885         ot->idname = "UI_OT_editsource";
886         ot->description = "Edit UI source code of the active button";
887
888         /* callbacks */
889         ot->exec = editsource_exec;
890 }
891
892 /* ------------------------------------------------------------------------- */
893 /* EditTranslation utility funcs and operator,
894  * Note: this includes utility functions and button matching checks.
895  *       this only works in conjunction with a py operator! */
896
897 void edittranslation_find_po_file(const char *root, const char *uilng, char *path, const size_t maxlen)
898 {
899         char t[32]; /* Should be more than enough! */
900         /* First, full lang code. */
901         sprintf(t, "%s.po", uilng);
902         BLI_join_dirfile(path, maxlen, root, uilng);
903         BLI_join_dirfile(path, maxlen, path, t);
904         if (BLI_is_file(path))
905                 return;
906         /* Now try without the second iso code part (_ES in es_ES). */
907         strncpy(t, uilng, 2);
908         strcpy(t + 2, uilng + 5); /* Because of some codes like sr_SR@latin... */
909         BLI_join_dirfile(path, maxlen, root, t);
910         sprintf(t, "%s.po", t);
911         BLI_join_dirfile(path, maxlen, path, t);
912         if (BLI_is_file(path))
913                 return;
914         path[0] = '\0';
915 }
916
917 static int edittranslation_exec(bContext *C, wmOperator *op)
918 {
919         uiBut *but = uiContextActiveButton(C);
920         int ret = OPERATOR_CANCELLED;
921
922         if (but) {
923                 PointerRNA ptr;
924                 char popath[FILE_MAX];
925                 const char *root = U.i18ndir;
926                 const char *uilng = BLF_lang_get();
927
928                 const int bufs_nbr = 10;
929                 uiStringInfo but_label = {BUT_GET_LABEL, NULL};
930                 uiStringInfo rna_label = {BUT_GET_RNA_LABEL, NULL};
931                 uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, NULL};
932                 uiStringInfo but_tip = {BUT_GET_TIP, NULL};
933                 uiStringInfo rna_tip = {BUT_GET_RNA_TIP, NULL};
934                 uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, NULL};
935                 uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, NULL};
936                 uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, NULL};
937                 uiStringInfo rna_enum = {BUT_GET_RNAENUM_IDENTIFIER, NULL};
938                 uiStringInfo rna_ctxt = {BUT_GET_RNA_LABEL_CONTEXT, NULL};
939
940                 if (!BLI_is_dir(root)) {
941                         BKE_report(op->reports, RPT_ERROR, "Please set your User Preferences' \"Translation Branches "
942                                                            "Directory\" path to a valid directory.");
943                         return OPERATOR_CANCELLED;
944                 }
945                 if (!WM_operatortype_find(EDTSRC_I18N_OP_NAME, 0)) {
946                         BKE_reportf(op->reports, RPT_ERROR, "Could not find operator \"%s\"! Please enable ui_translate addon "
947                                                             "in the User Preferences.", EDTSRC_I18N_OP_NAME);
948                         return OPERATOR_CANCELLED;
949                 }
950                 /* Try to find a valid po file for current language... */
951                 edittranslation_find_po_file(root, uilng, popath, FILE_MAX);
952                 printf("po path: %s\n", popath);
953                 if (popath[0] == '\0') {
954                         BKE_reportf(op->reports, RPT_ERROR, "No valid po found for language '%s' under %s.", uilng, root);
955                         return OPERATOR_CANCELLED;
956                 }
957
958                 uiButGetStrInfo(C, but, bufs_nbr, &but_label, &rna_label, &enum_label, &but_tip, &rna_tip, &enum_tip,
959                                 &rna_struct, &rna_prop, &rna_enum, &rna_ctxt);
960
961                 WM_operator_properties_create(&ptr, EDTSRC_I18N_OP_NAME);
962                 RNA_string_set(&ptr, "lang", uilng);
963                 RNA_string_set(&ptr, "po_file", popath);
964                 RNA_string_set(&ptr, "but_label", but_label.strinfo);
965                 RNA_string_set(&ptr, "rna_label", rna_label.strinfo);
966                 RNA_string_set(&ptr, "enum_label", enum_label.strinfo);
967                 RNA_string_set(&ptr, "but_tip", but_tip.strinfo);
968                 RNA_string_set(&ptr, "rna_tip", rna_tip.strinfo);
969                 RNA_string_set(&ptr, "enum_tip", enum_tip.strinfo);
970                 RNA_string_set(&ptr, "rna_struct", rna_struct.strinfo);
971                 RNA_string_set(&ptr, "rna_prop", rna_prop.strinfo);
972                 RNA_string_set(&ptr, "rna_enum", rna_enum.strinfo);
973                 RNA_string_set(&ptr, "rna_ctxt", rna_ctxt.strinfo);
974                 ret = WM_operator_name_call(C, EDTSRC_I18N_OP_NAME, WM_OP_INVOKE_DEFAULT, &ptr);
975
976                 /* Clean up */
977                 if (but_label.strinfo)
978                         MEM_freeN(but_label.strinfo);
979                 if (rna_label.strinfo)
980                         MEM_freeN(rna_label.strinfo);
981                 if (enum_label.strinfo)
982                         MEM_freeN(enum_label.strinfo);
983                 if (but_tip.strinfo)
984                         MEM_freeN(but_tip.strinfo);
985                 if (rna_tip.strinfo)
986                         MEM_freeN(rna_tip.strinfo);
987                 if (enum_tip.strinfo)
988                         MEM_freeN(enum_tip.strinfo);
989                 if (rna_struct.strinfo)
990                         MEM_freeN(rna_struct.strinfo);
991                 if (rna_prop.strinfo)
992                         MEM_freeN(rna_prop.strinfo);
993                 if (rna_enum.strinfo)
994                         MEM_freeN(rna_enum.strinfo);
995                 if (rna_ctxt.strinfo)
996                         MEM_freeN(rna_ctxt.strinfo);
997
998                 return ret;
999         }
1000         else {
1001                 BKE_report(op->reports, RPT_ERROR, "Active button not found");
1002                 return OPERATOR_CANCELLED;
1003         }
1004 }
1005
1006 #if 0
1007 static int edittranslation_poll(bContext *UNUSED(C))
1008 {
1009         /* We need the i18n py addon to be enabled! */
1010         return WM_operatortype_find(EDTSRC_I18N_OP_NAME, 0) ? TRUE : FALSE;
1011 }
1012 #endif
1013
1014 static void UI_OT_edittranslation_init(wmOperatorType *ot)
1015 {
1016         /* identifiers */
1017         ot->name = "Edit Translation";
1018         ot->idname = "UI_OT_edittranslation_init";
1019         ot->description = "Edit i18n in current language for the active button";
1020
1021         /* callbacks */
1022         ot->exec = edittranslation_exec;
1023 /*      ot->poll = edittranslation_poll;*/
1024 }
1025
1026 #endif /* WITH_PYTHON */
1027
1028 static int reloadtranslation_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
1029 {
1030         BLF_lang_init();
1031         BLF_cache_clear();
1032         BLF_lang_set(NULL);
1033         UI_reinit_font();
1034         return OPERATOR_FINISHED;
1035 }
1036
1037 static void UI_OT_reloadtranslation(wmOperatorType *ot)
1038 {
1039         /* identifiers */
1040         ot->name = "Reload Translation";
1041         ot->idname = "UI_OT_reloadtranslation";
1042         ot->description = "Force a full reload of UI translation";
1043
1044         /* callbacks */
1045         ot->exec = reloadtranslation_exec;
1046 }
1047
1048 /* ********************************************************* */
1049 /* Registration */
1050
1051 void UI_buttons_operatortypes(void)
1052 {
1053         WM_operatortype_append(UI_OT_eyedropper);
1054         WM_operatortype_append(UI_OT_reset_default_theme);
1055         WM_operatortype_append(UI_OT_copy_data_path_button);
1056         WM_operatortype_append(UI_OT_reset_default_button);
1057         WM_operatortype_append(UI_OT_copy_to_selected_button);
1058         WM_operatortype_append(UI_OT_reports_to_textblock);  /* XXX: temp? */
1059
1060 #ifdef WITH_PYTHON
1061         WM_operatortype_append(UI_OT_editsource);
1062         WM_operatortype_append(UI_OT_edittranslation_init);
1063 #endif
1064         WM_operatortype_append(UI_OT_reloadtranslation);
1065 }
1066