1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * GimpTextTool
5  * Copyright (C) 2002-2010  Sven Neumann <sven@gimp.org>
6  *                          Daniel Eddeland <danedde@svn.gnome.org>
7  *                          Michael Natterer <mitch@gimp.org>
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
21  */
22 
23 #include "config.h"
24 
25 #include <gegl.h>
26 #include <gtk/gtk.h>
27 
28 #include "libgimpbase/gimpbase.h"
29 #include "libgimpconfig/gimpconfig.h"
30 #include "libgimpwidgets/gimpwidgets.h"
31 
32 #include "tools-types.h"
33 
34 #include "core/gimp.h"
35 #include "core/gimpasyncset.h"
36 #include "core/gimpcontext.h"
37 #include "core/gimpdatafactory.h"
38 #include "core/gimperror.h"
39 #include "core/gimpimage.h"
40 #include "core/gimp-palettes.h"
41 #include "core/gimpimage-pick-item.h"
42 #include "core/gimpimage-undo.h"
43 #include "core/gimpimage-undo-push.h"
44 #include "core/gimplayer-floating-selection.h"
45 #include "core/gimpmarshal.h"
46 #include "core/gimptoolinfo.h"
47 #include "core/gimpundostack.h"
48 
49 #include "text/gimptext.h"
50 #include "text/gimptext-vectors.h"
51 #include "text/gimptextlayer.h"
52 #include "text/gimptextlayout.h"
53 #include "text/gimptextundo.h"
54 
55 #include "vectors/gimpstroke.h"
56 #include "vectors/gimpvectors.h"
57 #include "vectors/gimpvectors-warp.h"
58 
59 #include "widgets/gimpdialogfactory.h"
60 #include "widgets/gimpdockcontainer.h"
61 #include "widgets/gimphelp-ids.h"
62 #include "widgets/gimpmenufactory.h"
63 #include "widgets/gimptextbuffer.h"
64 #include "widgets/gimpuimanager.h"
65 #include "widgets/gimpviewabledialog.h"
66 
67 #include "display/gimpcanvasgroup.h"
68 #include "display/gimpdisplay.h"
69 #include "display/gimpdisplayshell.h"
70 #include "display/gimptoolrectangle.h"
71 
72 #include "gimptextoptions.h"
73 #include "gimptexttool.h"
74 #include "gimptexttool-editor.h"
75 #include "gimptoolcontrol.h"
76 
77 #include "gimp-intl.h"
78 
79 
80 #define TEXT_UNDO_TIMEOUT 3
81 
82 
83 /*  local function prototypes  */
84 
85 static void      gimp_text_tool_constructed     (GObject           *object);
86 static void      gimp_text_tool_finalize        (GObject           *object);
87 
88 static void      gimp_text_tool_control         (GimpTool          *tool,
89                                                  GimpToolAction     action,
90                                                  GimpDisplay       *display);
91 static void      gimp_text_tool_button_press    (GimpTool          *tool,
92                                                  const GimpCoords  *coords,
93                                                  guint32            time,
94                                                  GdkModifierType    state,
95                                                  GimpButtonPressType  press_type,
96                                                  GimpDisplay       *display);
97 static void      gimp_text_tool_button_release  (GimpTool          *tool,
98                                                  const GimpCoords  *coords,
99                                                  guint32            time,
100                                                  GdkModifierType    state,
101                                                  GimpButtonReleaseType release_type,
102                                                  GimpDisplay       *display);
103 static void      gimp_text_tool_motion          (GimpTool          *tool,
104                                                  const GimpCoords  *coords,
105                                                  guint32            time,
106                                                  GdkModifierType    state,
107                                                  GimpDisplay       *display);
108 static gboolean  gimp_text_tool_key_press       (GimpTool          *tool,
109                                                  GdkEventKey       *kevent,
110                                                  GimpDisplay       *display);
111 static gboolean  gimp_text_tool_key_release     (GimpTool          *tool,
112                                                  GdkEventKey       *kevent,
113                                                  GimpDisplay       *display);
114 static void      gimp_text_tool_oper_update     (GimpTool          *tool,
115                                                  const GimpCoords  *coords,
116                                                  GdkModifierType    state,
117                                                  gboolean           proximity,
118                                                  GimpDisplay       *display);
119 static void      gimp_text_tool_cursor_update   (GimpTool          *tool,
120                                                  const GimpCoords  *coords,
121                                                  GdkModifierType    state,
122                                                  GimpDisplay       *display);
123 static GimpUIManager * gimp_text_tool_get_popup (GimpTool          *tool,
124                                                  const GimpCoords  *coords,
125                                                  GdkModifierType    state,
126                                                  GimpDisplay       *display,
127                                                  const gchar      **ui_path);
128 
129 static void      gimp_text_tool_draw            (GimpDrawTool      *draw_tool);
130 static void      gimp_text_tool_draw_selection  (GimpDrawTool      *draw_tool);
131 
132 static gboolean  gimp_text_tool_start           (GimpTextTool      *text_tool,
133                                                  GimpDisplay       *display,
134                                                  GimpLayer         *layer,
135                                                  GError           **error);
136 static void      gimp_text_tool_halt            (GimpTextTool      *text_tool);
137 
138 static void      gimp_text_tool_frame_item      (GimpTextTool      *text_tool);
139 
140 static void      gimp_text_tool_rectangle_response
141                                                 (GimpToolRectangle *rectangle,
142                                                  gint               response_id,
143                                                  GimpTextTool      *text_tool);
144 static void      gimp_text_tool_rectangle_change_complete
145                                                 (GimpToolRectangle *rectangle,
146                                                  GimpTextTool      *text_tool);
147 
148 static void      gimp_text_tool_connect         (GimpTextTool      *text_tool,
149                                                  GimpTextLayer     *layer,
150                                                  GimpText          *text);
151 
152 static void      gimp_text_tool_layer_notify    (GimpTextLayer     *layer,
153                                                  const GParamSpec  *pspec,
154                                                  GimpTextTool      *text_tool);
155 static void      gimp_text_tool_proxy_notify    (GimpText          *text,
156                                                  const GParamSpec  *pspec,
157                                                  GimpTextTool      *text_tool);
158 
159 static void      gimp_text_tool_text_notify     (GimpText          *text,
160                                                  const GParamSpec  *pspec,
161                                                  GimpTextTool      *text_tool);
162 static void      gimp_text_tool_text_changed    (GimpText          *text,
163                                                  GimpTextTool      *text_tool);
164 
165 static void
166     gimp_text_tool_fonts_async_set_empty_notify (GimpAsyncSet      *async_set,
167                                                  GParamSpec        *pspec,
168                                                  GimpTextTool      *text_tool);
169 
170 static void      gimp_text_tool_apply_list      (GimpTextTool      *text_tool,
171                                                  GList             *pspecs);
172 
173 static void      gimp_text_tool_create_layer    (GimpTextTool      *text_tool,
174                                                  GimpText          *text);
175 
176 static void      gimp_text_tool_layer_changed   (GimpImage         *image,
177                                                  GimpTextTool      *text_tool);
178 static void      gimp_text_tool_set_image       (GimpTextTool      *text_tool,
179                                                  GimpImage         *image);
180 static gboolean  gimp_text_tool_set_drawable    (GimpTextTool      *text_tool,
181                                                  GimpDrawable      *drawable,
182                                                  gboolean           confirm);
183 
184 static void      gimp_text_tool_block_drawing   (GimpTextTool      *text_tool);
185 static void      gimp_text_tool_unblock_drawing (GimpTextTool      *text_tool);
186 
187 static void    gimp_text_tool_buffer_begin_edit (GimpTextBuffer    *buffer,
188                                                  GimpTextTool      *text_tool);
189 static void    gimp_text_tool_buffer_end_edit   (GimpTextBuffer    *buffer,
190                                                  GimpTextTool      *text_tool);
191 
192 static void    gimp_text_tool_buffer_color_applied
193                                                 (GimpTextBuffer    *buffer,
194                                                  const GimpRGB     *color,
195                                                  GimpTextTool      *text_tool);
196 
197 
G_DEFINE_TYPE(GimpTextTool,gimp_text_tool,GIMP_TYPE_DRAW_TOOL)198 G_DEFINE_TYPE (GimpTextTool, gimp_text_tool, GIMP_TYPE_DRAW_TOOL)
199 
200 #define parent_class gimp_text_tool_parent_class
201 
202 
203 void
204 gimp_text_tool_register (GimpToolRegisterCallback  callback,
205                          gpointer                  data)
206 {
207   (* callback) (GIMP_TYPE_TEXT_TOOL,
208                 GIMP_TYPE_TEXT_OPTIONS,
209                 gimp_text_options_gui,
210                 GIMP_CONTEXT_PROP_MASK_FOREGROUND |
211                 GIMP_CONTEXT_PROP_MASK_FONT       |
212                 GIMP_CONTEXT_PROP_MASK_PALETTE /* for the color popup's palette tab */,
213                 "gimp-text-tool",
214                 _("Text"),
215                 _("Text Tool: Create or edit text layers"),
216                 N_("Te_xt"), "T",
217                 NULL, GIMP_HELP_TOOL_TEXT,
218                 GIMP_ICON_TOOL_TEXT,
219                 data);
220 }
221 
222 static void
gimp_text_tool_class_init(GimpTextToolClass * klass)223 gimp_text_tool_class_init (GimpTextToolClass *klass)
224 {
225   GObjectClass      *object_class    = G_OBJECT_CLASS (klass);
226   GimpToolClass     *tool_class      = GIMP_TOOL_CLASS (klass);
227   GimpDrawToolClass *draw_tool_class = GIMP_DRAW_TOOL_CLASS (klass);
228 
229   object_class->constructed    = gimp_text_tool_constructed;
230   object_class->finalize       = gimp_text_tool_finalize;
231 
232   tool_class->control          = gimp_text_tool_control;
233   tool_class->button_press     = gimp_text_tool_button_press;
234   tool_class->motion           = gimp_text_tool_motion;
235   tool_class->button_release   = gimp_text_tool_button_release;
236   tool_class->key_press        = gimp_text_tool_key_press;
237   tool_class->key_release      = gimp_text_tool_key_release;
238   tool_class->oper_update      = gimp_text_tool_oper_update;
239   tool_class->cursor_update    = gimp_text_tool_cursor_update;
240   tool_class->get_popup        = gimp_text_tool_get_popup;
241 
242   draw_tool_class->draw        = gimp_text_tool_draw;
243 }
244 
245 static void
gimp_text_tool_init(GimpTextTool * text_tool)246 gimp_text_tool_init (GimpTextTool *text_tool)
247 {
248   GimpTool *tool = GIMP_TOOL (text_tool);
249 
250   text_tool->buffer = gimp_text_buffer_new ();
251 
252   g_signal_connect (text_tool->buffer, "begin-user-action",
253                     G_CALLBACK (gimp_text_tool_buffer_begin_edit),
254                     text_tool);
255   g_signal_connect (text_tool->buffer, "end-user-action",
256                     G_CALLBACK (gimp_text_tool_buffer_end_edit),
257                     text_tool);
258   g_signal_connect (text_tool->buffer, "color-applied",
259                     G_CALLBACK (gimp_text_tool_buffer_color_applied),
260                     text_tool);
261 
262   text_tool->handle_rectangle_change_complete = TRUE;
263 
264   gimp_text_tool_editor_init (text_tool);
265 
266   gimp_tool_control_set_scroll_lock          (tool->control, TRUE);
267   gimp_tool_control_set_handle_empty_image   (tool->control, TRUE);
268   gimp_tool_control_set_wants_click          (tool->control, TRUE);
269   gimp_tool_control_set_wants_double_click   (tool->control, TRUE);
270   gimp_tool_control_set_wants_triple_click   (tool->control, TRUE);
271   gimp_tool_control_set_wants_all_key_events (tool->control, TRUE);
272   gimp_tool_control_set_active_modifiers     (tool->control,
273                                               GIMP_TOOL_ACTIVE_MODIFIERS_SEPARATE);
274   gimp_tool_control_set_precision            (tool->control,
275                                               GIMP_CURSOR_PRECISION_PIXEL_BORDER);
276   gimp_tool_control_set_tool_cursor          (tool->control,
277                                               GIMP_TOOL_CURSOR_TEXT);
278   gimp_tool_control_set_action_object_1      (tool->control,
279                                               "context/context-font-select-set");
280 }
281 
282 static void
gimp_text_tool_constructed(GObject * object)283 gimp_text_tool_constructed (GObject *object)
284 {
285   GimpTextTool    *text_tool = GIMP_TEXT_TOOL (object);
286   GimpTextOptions *options   = GIMP_TEXT_TOOL_GET_OPTIONS (text_tool);
287   GimpTool        *tool      = GIMP_TOOL (text_tool);
288   GimpAsyncSet    *async_set;
289 
290   G_OBJECT_CLASS (parent_class)->constructed (object);
291 
292   text_tool->proxy = g_object_new (GIMP_TYPE_TEXT, NULL);
293 
294   gimp_text_options_connect_text (options, text_tool->proxy);
295 
296   g_signal_connect_object (text_tool->proxy, "notify",
297                            G_CALLBACK (gimp_text_tool_proxy_notify),
298                            text_tool, 0);
299 
300   async_set =
301     gimp_data_factory_get_async_set (tool->tool_info->gimp->font_factory);
302 
303   g_signal_connect_object (async_set,
304                            "notify::empty",
305                            G_CALLBACK (gimp_text_tool_fonts_async_set_empty_notify),
306                            text_tool, 0);
307 }
308 
309 static void
gimp_text_tool_finalize(GObject * object)310 gimp_text_tool_finalize (GObject *object)
311 {
312   GimpTextTool *text_tool = GIMP_TEXT_TOOL (object);
313 
314   g_clear_object (&text_tool->proxy);
315   g_clear_object (&text_tool->buffer);
316   g_clear_object (&text_tool->ui_manager);
317 
318   gimp_text_tool_editor_finalize (text_tool);
319 
320   G_OBJECT_CLASS (parent_class)->finalize (object);
321 }
322 
323 static void
gimp_text_tool_remove_empty_text_layer(GimpTextTool * text_tool)324 gimp_text_tool_remove_empty_text_layer (GimpTextTool *text_tool)
325 {
326   GimpTextLayer *text_layer = text_tool->layer;
327 
328   if (text_layer && text_layer->auto_rename)
329     {
330       GimpText *text = gimp_text_layer_get_text (text_layer);
331 
332       if (text && text->box_mode == GIMP_TEXT_BOX_DYNAMIC &&
333           (! text->text || text->text[0] == '\0') &&
334           (! text->markup || text->markup[0] == '\0'))
335         {
336           GimpImage *image = gimp_item_get_image (GIMP_ITEM (text_layer));
337 
338           if (text_tool->image == image)
339             g_signal_handlers_block_by_func (image,
340                                              gimp_text_tool_layer_changed,
341                                              text_tool);
342 
343           gimp_image_remove_layer (image, GIMP_LAYER (text_layer), TRUE, NULL);
344           gimp_image_flush (image);
345 
346           if (text_tool->image == image)
347             g_signal_handlers_unblock_by_func (image,
348                                                gimp_text_tool_layer_changed,
349                                                text_tool);
350         }
351     }
352 }
353 
354 static void
gimp_text_tool_control(GimpTool * tool,GimpToolAction action,GimpDisplay * display)355 gimp_text_tool_control (GimpTool       *tool,
356                         GimpToolAction  action,
357                         GimpDisplay    *display)
358 {
359   GimpTextTool *text_tool = GIMP_TEXT_TOOL (tool);
360 
361   switch (action)
362     {
363     case GIMP_TOOL_ACTION_PAUSE:
364     case GIMP_TOOL_ACTION_RESUME:
365       break;
366 
367     case GIMP_TOOL_ACTION_HALT:
368       gimp_text_tool_halt (text_tool);
369       break;
370 
371     case GIMP_TOOL_ACTION_COMMIT:
372       break;
373     }
374 
375   GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
376 }
377 
378 static void
gimp_text_tool_button_press(GimpTool * tool,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpButtonPressType press_type,GimpDisplay * display)379 gimp_text_tool_button_press (GimpTool            *tool,
380                              const GimpCoords    *coords,
381                              guint32              time,
382                              GdkModifierType      state,
383                              GimpButtonPressType  press_type,
384                              GimpDisplay         *display)
385 {
386   GimpTextTool      *text_tool = GIMP_TEXT_TOOL (tool);
387   GimpImage         *image     = gimp_display_get_image (display);
388   GimpText          *text      = text_tool->text;
389   GimpToolRectangle *rectangle;
390 
391   gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
392 
393   if (tool->display && tool->display != display)
394     gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
395 
396   if (! text_tool->widget)
397     {
398       GError *error = NULL;
399 
400       if (! gimp_text_tool_start (text_tool, display, NULL, &error))
401         {
402           gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
403 
404           gimp_tool_message_literal (tool, display, error->message);
405 
406           g_clear_error (&error);
407 
408           return;
409         }
410 
411       gimp_tool_widget_hover (text_tool->widget, coords, state, TRUE);
412 
413       /* HACK: force CREATING on a newly created rectangle; otherwise,
414        * the above binding of properties would cause the rectangle to
415        * start with the size from tool options.
416        */
417       gimp_tool_rectangle_set_function (GIMP_TOOL_RECTANGLE (text_tool->widget),
418                                         GIMP_TOOL_RECTANGLE_CREATING);
419     }
420 
421   rectangle = GIMP_TOOL_RECTANGLE (text_tool->widget);
422 
423   if (press_type == GIMP_BUTTON_PRESS_NORMAL)
424     {
425       gimp_tool_control_activate (tool->control);
426 
427       /* clicking anywhere while a preedit is going on aborts the
428        * preedit, this is ugly but at least leaves everything in
429        * a consistent state
430        */
431       if (text_tool->preedit_active)
432         gimp_text_tool_abort_im_context (text_tool);
433       else
434         gimp_text_tool_reset_im_context (text_tool);
435 
436       text_tool->selecting = FALSE;
437 
438       if (gimp_tool_rectangle_point_in_rectangle (rectangle,
439                                                   coords->x,
440                                                   coords->y) &&
441           ! text_tool->moving)
442         {
443           gimp_tool_rectangle_set_function (rectangle,
444                                             GIMP_TOOL_RECTANGLE_DEAD);
445         }
446       else if (gimp_tool_widget_button_press (text_tool->widget, coords,
447                                               time, state, press_type))
448         {
449           text_tool->grab_widget = text_tool->widget;
450         }
451 
452       /*  bail out now if the user user clicked on a handle of an
453        *  existing rectangle, but not inside an existing framed layer
454        */
455       if (gimp_tool_rectangle_get_function (rectangle) !=
456           GIMP_TOOL_RECTANGLE_CREATING)
457         {
458           if (text_tool->layer)
459             {
460               GimpItem *item = GIMP_ITEM (text_tool->layer);
461               gdouble   x    = coords->x - gimp_item_get_offset_x (item);
462               gdouble   y    = coords->y - gimp_item_get_offset_y (item);
463 
464               if (x < 0 || x >= gimp_item_get_width  (item) ||
465                   y < 0 || y >= gimp_item_get_height (item))
466                 {
467                   gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
468                   return;
469                 }
470             }
471           else
472             {
473               gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
474               return;
475             }
476         }
477 
478       /* if the the click is not related to the currently edited text
479        * layer in any way, try to pick a text layer
480        */
481       if (! text_tool->moving &&
482           gimp_tool_rectangle_get_function (rectangle) ==
483           GIMP_TOOL_RECTANGLE_CREATING)
484         {
485           GimpTextLayer *text_layer;
486 
487           text_layer = gimp_image_pick_text_layer (image, coords->x, coords->y);
488 
489           if (text_layer && text_layer != text_tool->layer)
490             {
491               if (text_tool->image == image)
492                 g_signal_handlers_block_by_func (image,
493                                                  gimp_text_tool_layer_changed,
494                                                  text_tool);
495 
496               gimp_image_set_active_layer (image, GIMP_LAYER (text_layer));
497 
498               if (text_tool->image == image)
499                 g_signal_handlers_unblock_by_func (image,
500                                                    gimp_text_tool_layer_changed,
501                                                    text_tool);
502             }
503         }
504     }
505 
506   if (gimp_image_coords_in_active_pickable (image, coords, FALSE, FALSE, FALSE))
507     {
508       GimpDrawable *drawable = gimp_image_get_active_drawable (image);
509       GimpItem     *item     = GIMP_ITEM (drawable);
510       gdouble       x        = coords->x - gimp_item_get_offset_x (item);
511       gdouble       y        = coords->y - gimp_item_get_offset_y (item);
512 
513       /*  did the user click on a text layer?  */
514       if (gimp_text_tool_set_drawable (text_tool, drawable, TRUE))
515         {
516           if (press_type == GIMP_BUTTON_PRESS_NORMAL)
517             {
518               /*  if we clicked on a text layer while the tool was idle
519                *  (didn't show a rectangle), frame the layer and switch to
520                *  selecting instead of drawing a new rectangle
521                */
522               if (gimp_tool_rectangle_get_function (rectangle) ==
523                   GIMP_TOOL_RECTANGLE_CREATING)
524                 {
525                   gimp_tool_rectangle_set_function (rectangle,
526                                                     GIMP_TOOL_RECTANGLE_DEAD);
527 
528                   gimp_text_tool_frame_item (text_tool);
529                 }
530 
531               if (text_tool->text && text_tool->text != text)
532                 {
533                   gimp_text_tool_editor_start (text_tool);
534                 }
535             }
536 
537           if (text_tool->text && ! text_tool->moving)
538             {
539               text_tool->selecting = TRUE;
540 
541               gimp_text_tool_editor_button_press (text_tool, x, y, press_type);
542             }
543           else
544             {
545               text_tool->selecting = FALSE;
546             }
547 
548           gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
549 
550           return;
551         }
552     }
553 
554   if (press_type == GIMP_BUTTON_PRESS_NORMAL)
555     {
556       /*  create a new text layer  */
557       text_tool->text_box_fixed = FALSE;
558 
559       /* make sure the text tool has an image, even if the user didn't click
560        * inside the active drawable, in particular, so that the text style
561        * editor picks the correct resolution.
562        */
563       gimp_text_tool_set_image (text_tool, image);
564 
565       gimp_text_tool_connect (text_tool, NULL, NULL);
566       gimp_text_tool_editor_start (text_tool);
567     }
568 
569   gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
570 }
571 
572 static void
gimp_text_tool_button_release(GimpTool * tool,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpButtonReleaseType release_type,GimpDisplay * display)573 gimp_text_tool_button_release (GimpTool              *tool,
574                                const GimpCoords      *coords,
575                                guint32                time,
576                                GdkModifierType        state,
577                                GimpButtonReleaseType  release_type,
578                                GimpDisplay           *display)
579 {
580   GimpTextTool      *text_tool = GIMP_TEXT_TOOL (tool);
581   GimpToolRectangle *rectangle = GIMP_TOOL_RECTANGLE (text_tool->widget);
582 
583   gimp_tool_control_halt (tool->control);
584 
585   if (text_tool->selecting)
586     {
587       /*  we are in a selection process (user has initially clicked on
588        *  an existing text layer), so finish the selection process and
589        *  ignore rectangle-change-complete.
590        */
591 
592       /*  need to block "end-user-action" on the text buffer, because
593        *  GtkTextBuffer considers copying text to the clipboard an
594        *  undo-relevant user action, which is clearly a bug, but what
595        *  can we do...
596        */
597       g_signal_handlers_block_by_func (text_tool->buffer,
598                                        gimp_text_tool_buffer_begin_edit,
599                                        text_tool);
600       g_signal_handlers_block_by_func (text_tool->buffer,
601                                        gimp_text_tool_buffer_end_edit,
602                                        text_tool);
603 
604       gimp_text_tool_editor_button_release (text_tool);
605 
606       g_signal_handlers_unblock_by_func (text_tool->buffer,
607                                          gimp_text_tool_buffer_end_edit,
608                                          text_tool);
609       g_signal_handlers_unblock_by_func (text_tool->buffer,
610                                          gimp_text_tool_buffer_begin_edit,
611                                          text_tool);
612 
613       text_tool->selecting = FALSE;
614 
615       text_tool->handle_rectangle_change_complete = FALSE;
616 
617       /*  there is no cancelling of selections yet  */
618       if (release_type == GIMP_BUTTON_RELEASE_CANCEL)
619         release_type = GIMP_BUTTON_RELEASE_NORMAL;
620     }
621   else if (text_tool->moving)
622     {
623       /*  the user has moved the text layer with Alt-drag, fall
624        *  through and let rectangle-change-complete do its job of
625        *  setting text layer's new position.
626        */
627     }
628   else if (gimp_tool_rectangle_get_function (rectangle) ==
629            GIMP_TOOL_RECTANGLE_DEAD)
630     {
631       /*  the user clicked in dead space (like between the corner and
632        *  edge handles, so completely ignore that.
633        */
634 
635       text_tool->handle_rectangle_change_complete = FALSE;
636     }
637   else if (release_type == GIMP_BUTTON_RELEASE_CANCEL)
638     {
639       /*  user has canceled the rectangle resizing, fall through
640        *  and let the rectangle handle restoring the previous size
641        */
642     }
643   else
644     {
645       gdouble x1, y1;
646       gdouble x2, y2;
647 
648       /*  otherwise the user has clicked outside of any text layer in
649        *  order to create a new text, fall through and let
650        *  rectangle-change-complete do its job of setting the new text
651        *  layer's size.
652        */
653 
654       g_object_get (rectangle,
655                     "x1", &x1,
656                     "y1", &y1,
657                     "x2", &x2,
658                     "y2", &y2,
659                     NULL);
660 
661       if (release_type == GIMP_BUTTON_RELEASE_CLICK ||
662           (x2 - x1) < 3                             ||
663           (y2 - y1) < 3)
664         {
665           /*  unless the rectangle is unreasonably small to hold any
666            *  real text (the user has eitherjust clicked or just made
667            *  a rectangle of a few pixels), so set the text box to
668            *  dynamic and ignore rectangle-change-complete.
669            */
670 
671           g_object_set (text_tool->proxy,
672                         "box-mode", GIMP_TEXT_BOX_DYNAMIC,
673                         NULL);
674 
675           text_tool->handle_rectangle_change_complete = FALSE;
676         }
677     }
678 
679   if (text_tool->grab_widget)
680     {
681       gimp_tool_widget_button_release (text_tool->grab_widget,
682                                        coords, time, state, release_type);
683       text_tool->grab_widget = NULL;
684     }
685 
686   text_tool->handle_rectangle_change_complete = TRUE;
687 }
688 
689 static void
gimp_text_tool_motion(GimpTool * tool,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpDisplay * display)690 gimp_text_tool_motion (GimpTool         *tool,
691                        const GimpCoords *coords,
692                        guint32           time,
693                        GdkModifierType   state,
694                        GimpDisplay      *display)
695 {
696   GimpTextTool *text_tool = GIMP_TEXT_TOOL (tool);
697 
698   if (! text_tool->selecting)
699     {
700       if (text_tool->grab_widget)
701         {
702           gimp_tool_widget_motion (text_tool->grab_widget,
703                                    coords, time, state);
704         }
705     }
706   else
707     {
708       GimpItem *item = GIMP_ITEM (text_tool->layer);
709       gdouble   x    = coords->x - gimp_item_get_offset_x (item);
710       gdouble   y    = coords->y - gimp_item_get_offset_y (item);
711 
712       gimp_text_tool_editor_motion (text_tool, x, y);
713     }
714 }
715 
716 static gboolean
gimp_text_tool_key_press(GimpTool * tool,GdkEventKey * kevent,GimpDisplay * display)717 gimp_text_tool_key_press (GimpTool    *tool,
718                           GdkEventKey *kevent,
719                           GimpDisplay *display)
720 {
721   GimpTextTool *text_tool = GIMP_TEXT_TOOL (tool);
722 
723   if (display == tool->display)
724     return gimp_text_tool_editor_key_press (text_tool, kevent);
725 
726   return FALSE;
727 }
728 
729 static gboolean
gimp_text_tool_key_release(GimpTool * tool,GdkEventKey * kevent,GimpDisplay * display)730 gimp_text_tool_key_release (GimpTool    *tool,
731                             GdkEventKey *kevent,
732                             GimpDisplay *display)
733 {
734   GimpTextTool *text_tool = GIMP_TEXT_TOOL (tool);
735 
736   if (display == tool->display)
737     return gimp_text_tool_editor_key_release (text_tool, kevent);
738 
739   return FALSE;
740 }
741 
742 static void
gimp_text_tool_oper_update(GimpTool * tool,const GimpCoords * coords,GdkModifierType state,gboolean proximity,GimpDisplay * display)743 gimp_text_tool_oper_update (GimpTool         *tool,
744                             const GimpCoords *coords,
745                             GdkModifierType   state,
746                             gboolean          proximity,
747                             GimpDisplay      *display)
748 {
749   GimpTextTool      *text_tool = GIMP_TEXT_TOOL (tool);
750   GimpToolRectangle *rectangle = GIMP_TOOL_RECTANGLE (text_tool->widget);
751 
752   GIMP_TOOL_CLASS (parent_class)->oper_update (tool, coords, state,
753                                                proximity, display);
754 
755   text_tool->moving = (text_tool->widget &&
756                        gimp_tool_rectangle_get_function (rectangle) ==
757                        GIMP_TOOL_RECTANGLE_MOVING &&
758                        (state & GDK_MOD1_MASK));
759 }
760 
761 static void
gimp_text_tool_cursor_update(GimpTool * tool,const GimpCoords * coords,GdkModifierType state,GimpDisplay * display)762 gimp_text_tool_cursor_update (GimpTool         *tool,
763                               const GimpCoords *coords,
764                               GdkModifierType   state,
765                               GimpDisplay      *display)
766 {
767   GimpTextTool      *text_tool = GIMP_TEXT_TOOL (tool);
768   GimpToolRectangle *rectangle = GIMP_TOOL_RECTANGLE (text_tool->widget);
769 
770   if (rectangle && tool->display == display)
771     {
772       if (gimp_tool_rectangle_point_in_rectangle (rectangle,
773                                                   coords->x,
774                                                   coords->y) &&
775           ! text_tool->moving)
776         {
777           gimp_tool_set_cursor (tool, display,
778                                 (GimpCursorType) GDK_XTERM,
779                                 gimp_tool_control_get_tool_cursor (tool->control),
780                                 GIMP_CURSOR_MODIFIER_NONE);
781         }
782       else
783         {
784           GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state,
785                                                          display);
786         }
787     }
788   else
789     {
790       GimpAsyncSet *async_set;
791 
792       async_set =
793         gimp_data_factory_get_async_set (tool->tool_info->gimp->font_factory);
794 
795       if (gimp_async_set_is_empty (async_set))
796         {
797           GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state,
798                                                          display);
799         }
800       else
801         {
802           gimp_tool_set_cursor (tool, display,
803                                 gimp_tool_control_get_cursor (tool->control),
804                                 gimp_tool_control_get_tool_cursor (tool->control),
805                                 GIMP_CURSOR_MODIFIER_BAD);
806         }
807     }
808 }
809 
810 static GimpUIManager *
gimp_text_tool_get_popup(GimpTool * tool,const GimpCoords * coords,GdkModifierType state,GimpDisplay * display,const gchar ** ui_path)811 gimp_text_tool_get_popup (GimpTool         *tool,
812                           const GimpCoords *coords,
813                           GdkModifierType   state,
814                           GimpDisplay      *display,
815                           const gchar     **ui_path)
816 {
817   GimpTextTool      *text_tool = GIMP_TEXT_TOOL (tool);
818   GimpToolRectangle *rectangle = GIMP_TOOL_RECTANGLE (text_tool->widget);
819 
820   if (rectangle &&
821       gimp_tool_rectangle_point_in_rectangle (rectangle,
822                                               coords->x,
823                                               coords->y))
824     {
825       if (! text_tool->ui_manager)
826         {
827           GimpDisplayShell  *shell = gimp_display_get_shell (tool->display);
828           GimpImageWindow   *image_window;
829           GimpDialogFactory *dialog_factory;
830           GtkWidget         *im_menu;
831           GList             *children;
832 
833           image_window   = gimp_display_shell_get_window (shell);
834           dialog_factory = gimp_dock_container_get_dialog_factory (GIMP_DOCK_CONTAINER (image_window));
835 
836           text_tool->ui_manager =
837             gimp_menu_factory_manager_new (gimp_dialog_factory_get_menu_factory (dialog_factory),
838                                            "<TextTool>",
839                                            text_tool, FALSE);
840 
841           im_menu = gimp_ui_manager_get_widget (text_tool->ui_manager,
842                                                 "/text-tool-popup/text-tool-input-methods-menu");
843 
844           if (GTK_IS_MENU_ITEM (im_menu))
845             im_menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (im_menu));
846 
847           /*  hide the generated "empty" item  */
848           children = gtk_container_get_children (GTK_CONTAINER (im_menu));
849           while (children)
850             {
851               gtk_widget_hide (children->data);
852               children = g_list_remove (children, children->data);
853             }
854 
855           gtk_im_multicontext_append_menuitems (GTK_IM_MULTICONTEXT (text_tool->im_context),
856                                                 GTK_MENU_SHELL (im_menu));
857         }
858 
859       gimp_ui_manager_update (text_tool->ui_manager, text_tool);
860 
861       *ui_path = "/text-tool-popup";
862 
863       return text_tool->ui_manager;
864     }
865 
866   return NULL;
867 }
868 
869 static void
gimp_text_tool_draw(GimpDrawTool * draw_tool)870 gimp_text_tool_draw (GimpDrawTool *draw_tool)
871 {
872   GimpTextTool *text_tool = GIMP_TEXT_TOOL (draw_tool);
873 
874   GIMP_DRAW_TOOL_CLASS (parent_class)->draw (draw_tool);
875 
876   if (! text_tool->text  ||
877       ! text_tool->layer ||
878       ! text_tool->layer->text)
879     {
880       gimp_text_tool_editor_update_im_cursor (text_tool);
881 
882       return;
883     }
884 
885   gimp_text_tool_ensure_layout (text_tool);
886 
887   if (gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (text_tool->buffer)))
888     {
889       /* If the text buffer has a selection, highlight the selected letters */
890 
891       gimp_text_tool_draw_selection (draw_tool);
892     }
893   else
894     {
895       /* If the text buffer has no selection, draw the text cursor */
896 
897       GimpCanvasItem   *item;
898       PangoRectangle    cursor_rect;
899       gint              off_x, off_y;
900       gboolean          overwrite;
901       GimpTextDirection direction;
902 
903       gimp_text_tool_editor_get_cursor_rect (text_tool,
904                                              text_tool->overwrite_mode,
905                                              &cursor_rect);
906 
907       gimp_item_get_offset (GIMP_ITEM (text_tool->layer), &off_x, &off_y);
908       cursor_rect.x += off_x;
909       cursor_rect.y += off_y;
910 
911       overwrite = text_tool->overwrite_mode && cursor_rect.width != 0;
912 
913       direction = gimp_text_tool_get_direction (text_tool);
914 
915       item = gimp_draw_tool_add_text_cursor (draw_tool, &cursor_rect,
916                                              overwrite, direction);
917       gimp_canvas_item_set_highlight (item, TRUE);
918     }
919 
920   gimp_text_tool_editor_update_im_cursor (text_tool);
921 }
922 
923 static void
gimp_text_tool_draw_selection(GimpDrawTool * draw_tool)924 gimp_text_tool_draw_selection (GimpDrawTool *draw_tool)
925 {
926   GimpTextTool     *text_tool = GIMP_TEXT_TOOL (draw_tool);
927   GtkTextBuffer    *buffer    = GTK_TEXT_BUFFER (text_tool->buffer);
928   GimpCanvasGroup  *group;
929   PangoLayout      *layout;
930   gint              offset_x;
931   gint              offset_y;
932   gint              width;
933   gint              height;
934   gint              off_x, off_y;
935   PangoLayoutIter  *iter;
936   GtkTextIter       sel_start, sel_end;
937   gint              min, max;
938   gint              i;
939   GimpTextDirection direction;
940 
941   group = gimp_draw_tool_add_stroke_group (draw_tool);
942   gimp_canvas_item_set_highlight (GIMP_CANVAS_ITEM (group), TRUE);
943 
944   gtk_text_buffer_get_selection_bounds (buffer, &sel_start, &sel_end);
945 
946   min = gimp_text_buffer_get_iter_index (text_tool->buffer, &sel_start, TRUE);
947   max = gimp_text_buffer_get_iter_index (text_tool->buffer, &sel_end, TRUE);
948 
949   layout = gimp_text_layout_get_pango_layout (text_tool->layout);
950 
951   gimp_text_layout_get_offsets (text_tool->layout, &offset_x, &offset_y);
952 
953   gimp_text_layout_get_size (text_tool->layout, &width, &height);
954 
955   gimp_item_get_offset (GIMP_ITEM (text_tool->layer), &off_x, &off_y);
956   offset_x += off_x;
957   offset_y += off_y;
958 
959   direction = gimp_text_tool_get_direction (text_tool);
960 
961   iter = pango_layout_get_iter (layout);
962 
963   gimp_draw_tool_push_group (draw_tool, group);
964 
965   do
966     {
967       if (! pango_layout_iter_get_run (iter))
968         continue;
969 
970       i = pango_layout_iter_get_index (iter);
971 
972       if (i >= min && i < max)
973         {
974           PangoRectangle rect;
975           gint           ytop, ybottom;
976 
977           pango_layout_iter_get_char_extents (iter, &rect);
978           pango_layout_iter_get_line_yrange (iter, &ytop, &ybottom);
979 
980           rect.y      = ytop;
981           rect.height = ybottom - ytop;
982 
983           pango_extents_to_pixels (&rect, NULL);
984 
985           gimp_text_layout_transform_rect (text_tool->layout, &rect);
986 
987           switch (direction)
988             {
989             case GIMP_TEXT_DIRECTION_LTR:
990             case GIMP_TEXT_DIRECTION_RTL:
991               rect.x += offset_x;
992               rect.y += offset_y;
993               gimp_draw_tool_add_rectangle (draw_tool, FALSE,
994                                             rect.x, rect.y,
995                                             rect.width, rect.height);
996               break;
997             case GIMP_TEXT_DIRECTION_TTB_RTL:
998             case GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT:
999               rect.y = offset_x - rect.y + width;
1000               rect.x = offset_y + rect.x;
1001               gimp_draw_tool_add_rectangle (draw_tool, FALSE,
1002                                             rect.y, rect.x,
1003                                             -rect.height, rect.width);
1004               break;
1005             case GIMP_TEXT_DIRECTION_TTB_LTR:
1006             case GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT:
1007               rect.y = offset_x + rect.y;
1008               rect.x = offset_y - rect.x + height;
1009               gimp_draw_tool_add_rectangle (draw_tool, FALSE,
1010                                             rect.y, rect.x,
1011                                             rect.height, -rect.width);
1012               break;
1013             }
1014         }
1015     }
1016   while (pango_layout_iter_next_char (iter));
1017 
1018   gimp_draw_tool_pop_group (draw_tool);
1019 
1020   pango_layout_iter_free (iter);
1021 }
1022 
1023 static gboolean
gimp_text_tool_start(GimpTextTool * text_tool,GimpDisplay * display,GimpLayer * layer,GError ** error)1024 gimp_text_tool_start (GimpTextTool  *text_tool,
1025                       GimpDisplay   *display,
1026                       GimpLayer     *layer,
1027                       GError       **error)
1028 {
1029   GimpTool         *tool  = GIMP_TOOL (text_tool);
1030   GimpDisplayShell *shell = gimp_display_get_shell (display);
1031   GimpToolWidget   *widget;
1032   GimpAsyncSet     *async_set;
1033 
1034   async_set =
1035     gimp_data_factory_get_async_set (tool->tool_info->gimp->font_factory);
1036 
1037   if (! gimp_async_set_is_empty (async_set))
1038     {
1039       g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
1040                            _("Fonts are still loading"));
1041 
1042       return FALSE;
1043     }
1044 
1045   tool->display = display;
1046 
1047   text_tool->widget = widget = gimp_tool_rectangle_new (shell);
1048 
1049   g_object_set (widget,
1050                 "force-narrow-mode", TRUE,
1051                 "status-title",      _("Text box: "),
1052                 NULL);
1053 
1054   gimp_draw_tool_set_widget (GIMP_DRAW_TOOL (tool), widget);
1055 
1056   g_signal_connect (widget, "response",
1057                     G_CALLBACK (gimp_text_tool_rectangle_response),
1058                     text_tool);
1059   g_signal_connect (widget, "change-complete",
1060                     G_CALLBACK (gimp_text_tool_rectangle_change_complete),
1061                     text_tool);
1062 
1063   gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), display);
1064 
1065   if (layer)
1066     {
1067       gimp_text_tool_frame_item (text_tool);
1068       gimp_text_tool_editor_start (text_tool);
1069       gimp_text_tool_editor_position (text_tool);
1070     }
1071 
1072   return TRUE;
1073 }
1074 
1075 static void
gimp_text_tool_halt(GimpTextTool * text_tool)1076 gimp_text_tool_halt (GimpTextTool *text_tool)
1077 {
1078   GimpTool *tool = GIMP_TOOL (text_tool);
1079 
1080   gimp_text_tool_editor_halt (text_tool);
1081   gimp_text_tool_clear_layout (text_tool);
1082   gimp_text_tool_set_drawable (text_tool, NULL, FALSE);
1083 
1084   if (gimp_draw_tool_is_active (GIMP_DRAW_TOOL (tool)))
1085     gimp_draw_tool_stop (GIMP_DRAW_TOOL (tool));
1086 
1087   gimp_draw_tool_set_widget (GIMP_DRAW_TOOL (tool), NULL);
1088   g_clear_object (&text_tool->widget);
1089 
1090   tool->display  = NULL;
1091   tool->drawable = NULL;
1092 }
1093 
1094 static void
gimp_text_tool_frame_item(GimpTextTool * text_tool)1095 gimp_text_tool_frame_item (GimpTextTool *text_tool)
1096 {
1097   g_return_if_fail (GIMP_IS_LAYER (text_tool->layer));
1098 
1099   text_tool->handle_rectangle_change_complete = FALSE;
1100 
1101   gimp_tool_rectangle_frame_item (GIMP_TOOL_RECTANGLE (text_tool->widget),
1102                                   GIMP_ITEM (text_tool->layer));
1103 
1104   text_tool->handle_rectangle_change_complete = TRUE;
1105 }
1106 
1107 static void
gimp_text_tool_rectangle_response(GimpToolRectangle * rectangle,gint response_id,GimpTextTool * text_tool)1108 gimp_text_tool_rectangle_response (GimpToolRectangle *rectangle,
1109                                    gint               response_id,
1110                                    GimpTextTool      *text_tool)
1111 {
1112   GimpTool *tool = GIMP_TOOL (text_tool);
1113 
1114   /* this happens when a newly created rectangle gets canceled,
1115    * we have to shut down the tool
1116    */
1117   if (response_id == GIMP_TOOL_WIDGET_RESPONSE_CANCEL)
1118     gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display);
1119 }
1120 
1121 static void
gimp_text_tool_rectangle_change_complete(GimpToolRectangle * rectangle,GimpTextTool * text_tool)1122 gimp_text_tool_rectangle_change_complete (GimpToolRectangle *rectangle,
1123                                           GimpTextTool      *text_tool)
1124 {
1125   gimp_text_tool_editor_position (text_tool);
1126 
1127   if (text_tool->handle_rectangle_change_complete)
1128     {
1129       GimpItem *item = GIMP_ITEM (text_tool->layer);
1130       gdouble   x1, y1;
1131       gdouble   x2, y2;
1132 
1133       if (! item)
1134         {
1135           /* we can't set properties for the text layer, because it
1136            * isn't created until some text has been inserted, so we
1137            * need to make a special note that will remind us what to
1138            * do when we actually create the layer
1139            */
1140           text_tool->text_box_fixed = TRUE;
1141 
1142           return;
1143         }
1144 
1145       g_object_get (rectangle,
1146                     "x1", &x1,
1147                     "y1", &y1,
1148                     "x2", &x2,
1149                     "y2", &y2,
1150                     NULL);
1151 
1152       if ((x2 - x1) != gimp_item_get_width  (item) ||
1153           (y2 - y1) != gimp_item_get_height (item))
1154         {
1155           GimpUnit  box_unit = text_tool->proxy->box_unit;
1156           gdouble   xres, yres;
1157           gboolean  push_undo = TRUE;
1158           GimpUndo *undo;
1159 
1160           gimp_image_get_resolution (text_tool->image, &xres, &yres);
1161 
1162           g_object_set (text_tool->proxy,
1163                         "box-mode",   GIMP_TEXT_BOX_FIXED,
1164                         "box-width",  gimp_pixels_to_units (x2 - x1,
1165                                                             box_unit, xres),
1166                         "box-height", gimp_pixels_to_units (y2 - y1,
1167                                                             box_unit, yres),
1168                         NULL);
1169 
1170           undo = gimp_image_undo_can_compress (text_tool->image,
1171                                                GIMP_TYPE_UNDO_STACK,
1172                                                GIMP_UNDO_GROUP_TEXT);
1173 
1174           if (undo &&
1175               gimp_undo_get_age (undo) <= TEXT_UNDO_TIMEOUT &&
1176               g_object_get_data (G_OBJECT (undo), "reshape-text-layer") == (gpointer) item)
1177             push_undo = FALSE;
1178 
1179           if (push_undo)
1180             {
1181               gimp_image_undo_group_start (text_tool->image, GIMP_UNDO_GROUP_TEXT,
1182                                            _("Reshape Text Layer"));
1183 
1184               undo = gimp_image_undo_can_compress (text_tool->image, GIMP_TYPE_UNDO_STACK,
1185                                                    GIMP_UNDO_GROUP_TEXT);
1186 
1187               if (undo)
1188                 g_object_set_data (G_OBJECT (undo), "reshape-text-layer",
1189                                    (gpointer) item);
1190             }
1191 
1192           gimp_text_tool_block_drawing (text_tool);
1193 
1194           gimp_item_translate (item,
1195                                x1 - gimp_item_get_offset_x (item),
1196                                y1 - gimp_item_get_offset_y (item),
1197                                push_undo);
1198           gimp_text_tool_apply (text_tool, push_undo);
1199 
1200           gimp_text_tool_unblock_drawing (text_tool);
1201 
1202           if (push_undo)
1203             gimp_image_undo_group_end (text_tool->image);
1204         }
1205       else if (x1 != gimp_item_get_offset_x (item) ||
1206                y1 != gimp_item_get_offset_y (item))
1207         {
1208           gimp_text_tool_block_drawing (text_tool);
1209 
1210           gimp_text_tool_apply (text_tool, TRUE);
1211 
1212           gimp_item_translate (item,
1213                                x1 - gimp_item_get_offset_x (item),
1214                                y1 - gimp_item_get_offset_y (item),
1215                                TRUE);
1216 
1217           gimp_text_tool_unblock_drawing (text_tool);
1218 
1219           gimp_image_flush (text_tool->image);
1220         }
1221     }
1222 }
1223 
1224 static void
gimp_text_tool_connect(GimpTextTool * text_tool,GimpTextLayer * layer,GimpText * text)1225 gimp_text_tool_connect (GimpTextTool  *text_tool,
1226                         GimpTextLayer *layer,
1227                         GimpText      *text)
1228 {
1229   GimpTool *tool = GIMP_TOOL (text_tool);
1230 
1231   g_return_if_fail (text == NULL || (layer != NULL && layer->text == text));
1232 
1233   if (text_tool->text != text)
1234     {
1235       GimpTextOptions *options = GIMP_TEXT_TOOL_GET_OPTIONS (tool);
1236 
1237       g_signal_handlers_block_by_func (text_tool->buffer,
1238                                        gimp_text_tool_buffer_begin_edit,
1239                                        text_tool);
1240       g_signal_handlers_block_by_func (text_tool->buffer,
1241                                        gimp_text_tool_buffer_end_edit,
1242                                        text_tool);
1243 
1244       if (text_tool->text)
1245         {
1246           g_signal_handlers_disconnect_by_func (text_tool->text,
1247                                                 gimp_text_tool_text_notify,
1248                                                 text_tool);
1249           g_signal_handlers_disconnect_by_func (text_tool->text,
1250                                                 gimp_text_tool_text_changed,
1251                                                 text_tool);
1252 
1253           if (text_tool->pending)
1254             gimp_text_tool_apply (text_tool, TRUE);
1255 
1256           g_clear_object (&text_tool->text);
1257 
1258           g_object_set (text_tool->proxy,
1259                         "text",   NULL,
1260                         "markup", NULL,
1261                         NULL);
1262           gimp_text_buffer_set_text (text_tool->buffer, NULL);
1263 
1264           gimp_text_tool_clear_layout (text_tool);
1265         }
1266 
1267       gimp_context_define_property (GIMP_CONTEXT (options),
1268                                     GIMP_CONTEXT_PROP_FOREGROUND,
1269                                     text != NULL);
1270 
1271       if (text)
1272         {
1273           if (text->unit != text_tool->proxy->unit)
1274             gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (options->size_entry),
1275                                       text->unit);
1276 
1277           gimp_config_sync (G_OBJECT (text), G_OBJECT (text_tool->proxy), 0);
1278 
1279           if (text->markup)
1280             gimp_text_buffer_set_markup (text_tool->buffer, text->markup);
1281           else
1282             gimp_text_buffer_set_text (text_tool->buffer, text->text);
1283 
1284           gimp_text_tool_clear_layout (text_tool);
1285 
1286           text_tool->text = g_object_ref (text);
1287 
1288           g_signal_connect (text, "notify",
1289                             G_CALLBACK (gimp_text_tool_text_notify),
1290                             text_tool);
1291           g_signal_connect (text, "changed",
1292                             G_CALLBACK (gimp_text_tool_text_changed),
1293                             text_tool);
1294         }
1295 
1296       g_signal_handlers_unblock_by_func (text_tool->buffer,
1297                                          gimp_text_tool_buffer_end_edit,
1298                                          text_tool);
1299       g_signal_handlers_unblock_by_func (text_tool->buffer,
1300                                          gimp_text_tool_buffer_begin_edit,
1301                                          text_tool);
1302     }
1303 
1304   if (text_tool->layer != layer)
1305     {
1306       if (text_tool->layer)
1307         {
1308           g_signal_handlers_disconnect_by_func (text_tool->layer,
1309                                                 gimp_text_tool_layer_notify,
1310                                                 text_tool);
1311 
1312           /*  don't try to remove the layer if it is not attached,
1313            *  which can happen if we got here because the layer was
1314            *  somehow deleted from the image (like by the user in the
1315            *  layers dialog).
1316            */
1317           if (gimp_item_is_attached (GIMP_ITEM (text_tool->layer)))
1318             gimp_text_tool_remove_empty_text_layer (text_tool);
1319         }
1320 
1321       text_tool->layer = layer;
1322 
1323       if (layer)
1324         {
1325           g_signal_connect_object (text_tool->layer, "notify",
1326                                    G_CALLBACK (gimp_text_tool_layer_notify),
1327                                    text_tool, 0);
1328         }
1329     }
1330 }
1331 
1332 static void
gimp_text_tool_layer_notify(GimpTextLayer * layer,const GParamSpec * pspec,GimpTextTool * text_tool)1333 gimp_text_tool_layer_notify (GimpTextLayer    *layer,
1334                              const GParamSpec *pspec,
1335                              GimpTextTool     *text_tool)
1336 {
1337   GimpTool *tool = GIMP_TOOL (text_tool);
1338 
1339   if (! strcmp (pspec->name, "modified"))
1340     {
1341       if (layer->modified)
1342         gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display);
1343     }
1344   else if (! strcmp (pspec->name, "text"))
1345     {
1346       if (! layer->text)
1347         gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display);
1348     }
1349   else if (! strcmp (pspec->name, "offset-x") ||
1350            ! strcmp (pspec->name, "offset-y"))
1351     {
1352       if (gimp_item_is_attached (GIMP_ITEM (layer)))
1353         {
1354           gimp_text_tool_block_drawing (text_tool);
1355 
1356           gimp_text_tool_frame_item (text_tool);
1357 
1358           gimp_text_tool_unblock_drawing (text_tool);
1359         }
1360     }
1361 }
1362 
1363 static gboolean
gimp_text_tool_apply_idle(GimpTextTool * text_tool)1364 gimp_text_tool_apply_idle (GimpTextTool *text_tool)
1365 {
1366   text_tool->idle_id = 0;
1367 
1368   gimp_text_tool_apply (text_tool, TRUE);
1369 
1370   gimp_text_tool_unblock_drawing (text_tool);
1371 
1372   return G_SOURCE_REMOVE;
1373 }
1374 
1375 static void
gimp_text_tool_proxy_notify(GimpText * text,const GParamSpec * pspec,GimpTextTool * text_tool)1376 gimp_text_tool_proxy_notify (GimpText         *text,
1377                              const GParamSpec *pspec,
1378                              GimpTextTool     *text_tool)
1379 {
1380   if (! text_tool->text)
1381     return;
1382 
1383   if ((pspec->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE &&
1384       pspec->owner_type == GIMP_TYPE_TEXT)
1385     {
1386       if (text_tool->preedit_active)
1387         {
1388           /* if there is a preedit going on, don't queue pending
1389            * changes to be idle-applied with undo; instead, flush the
1390            * pending queue (happens only when preedit starts), and
1391            * apply the changes to text_tool->text directly. Preedit
1392            * will *always* end by removing the preedit string, and if
1393            * the preedit was committed, it will insert the resulting
1394            * text, which will not trigger this if() any more.
1395            */
1396 
1397           GList *list = NULL;
1398 
1399          /* if there are pending changes, apply them before applying
1400            * preedit stuff directly (bypassing undo)
1401            */
1402           if (text_tool->pending)
1403             {
1404               gimp_text_tool_block_drawing (text_tool);
1405               gimp_text_tool_apply (text_tool, TRUE);
1406               gimp_text_tool_unblock_drawing (text_tool);
1407             }
1408 
1409           gimp_text_tool_block_drawing (text_tool);
1410 
1411           list = g_list_append (list, (gpointer) pspec);
1412           gimp_text_tool_apply_list (text_tool, list);
1413           g_list_free (list);
1414 
1415           gimp_text_tool_frame_item (text_tool);
1416 
1417           gimp_image_flush (gimp_item_get_image (GIMP_ITEM (text_tool->layer)));
1418 
1419           gimp_text_tool_unblock_drawing (text_tool);
1420         }
1421       else
1422         {
1423           /* else queue the property change for normal processing,
1424            * including undo
1425            */
1426 
1427           text_tool->pending = g_list_append (text_tool->pending,
1428                                               (gpointer) pspec);
1429 
1430           if (! text_tool->idle_id)
1431             {
1432               gimp_text_tool_block_drawing (text_tool);
1433 
1434               text_tool->idle_id =
1435                 g_idle_add_full (G_PRIORITY_LOW,
1436                                  (GSourceFunc) gimp_text_tool_apply_idle,
1437                                  text_tool,
1438                                  NULL);
1439             }
1440         }
1441     }
1442 }
1443 
1444 static void
gimp_text_tool_text_notify(GimpText * text,const GParamSpec * pspec,GimpTextTool * text_tool)1445 gimp_text_tool_text_notify (GimpText         *text,
1446                             const GParamSpec *pspec,
1447                             GimpTextTool     *text_tool)
1448 {
1449   g_return_if_fail (text == text_tool->text);
1450 
1451   /* an undo cancels all preedit operations */
1452   if (text_tool->preedit_active)
1453     gimp_text_tool_abort_im_context (text_tool);
1454 
1455   gimp_text_tool_block_drawing (text_tool);
1456 
1457   if ((pspec->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE)
1458     {
1459       GValue value = G_VALUE_INIT;
1460 
1461       g_value_init (&value, pspec->value_type);
1462 
1463       g_object_get_property (G_OBJECT (text), pspec->name, &value);
1464 
1465       g_signal_handlers_block_by_func (text_tool->proxy,
1466                                        gimp_text_tool_proxy_notify,
1467                                        text_tool);
1468 
1469       g_object_set_property (G_OBJECT (text_tool->proxy), pspec->name, &value);
1470 
1471       g_signal_handlers_unblock_by_func (text_tool->proxy,
1472                                          gimp_text_tool_proxy_notify,
1473                                          text_tool);
1474 
1475       g_value_unset (&value);
1476     }
1477 
1478   /* if the text has changed, (probably because of an undo), we put
1479    * the new text into the text buffer
1480    */
1481   if (strcmp (pspec->name, "text")   == 0 ||
1482       strcmp (pspec->name, "markup") == 0)
1483     {
1484       g_signal_handlers_block_by_func (text_tool->buffer,
1485                                        gimp_text_tool_buffer_begin_edit,
1486                                        text_tool);
1487       g_signal_handlers_block_by_func (text_tool->buffer,
1488                                        gimp_text_tool_buffer_end_edit,
1489                                        text_tool);
1490 
1491       if (text->markup)
1492         gimp_text_buffer_set_markup (text_tool->buffer, text->markup);
1493       else
1494         gimp_text_buffer_set_text (text_tool->buffer, text->text);
1495 
1496       g_signal_handlers_unblock_by_func (text_tool->buffer,
1497                                          gimp_text_tool_buffer_end_edit,
1498                                          text_tool);
1499       g_signal_handlers_unblock_by_func (text_tool->buffer,
1500                                          gimp_text_tool_buffer_begin_edit,
1501                                          text_tool);
1502     }
1503 
1504   gimp_text_tool_unblock_drawing (text_tool);
1505 }
1506 
1507 static void
gimp_text_tool_text_changed(GimpText * text,GimpTextTool * text_tool)1508 gimp_text_tool_text_changed (GimpText     *text,
1509                              GimpTextTool *text_tool)
1510 {
1511   gimp_text_tool_block_drawing (text_tool);
1512 
1513   /* we need to redraw the rectangle in any case because whatever
1514    * changes to the text can change its size
1515    */
1516   gimp_text_tool_frame_item (text_tool);
1517 
1518   gimp_text_tool_unblock_drawing (text_tool);
1519 }
1520 
1521 static void
gimp_text_tool_fonts_async_set_empty_notify(GimpAsyncSet * async_set,GParamSpec * pspec,GimpTextTool * text_tool)1522 gimp_text_tool_fonts_async_set_empty_notify (GimpAsyncSet *async_set,
1523                                              GParamSpec   *pspec,
1524                                              GimpTextTool *text_tool)
1525 {
1526   GimpTool *tool = GIMP_TOOL (text_tool);
1527 
1528   if (! gimp_async_set_is_empty (async_set) && tool->display)
1529     gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display);
1530 }
1531 
1532 static void
gimp_text_tool_apply_list(GimpTextTool * text_tool,GList * pspecs)1533 gimp_text_tool_apply_list (GimpTextTool *text_tool,
1534                            GList        *pspecs)
1535 {
1536   GObject *src  = G_OBJECT (text_tool->proxy);
1537   GObject *dest = G_OBJECT (text_tool->text);
1538   GList   *list;
1539 
1540   g_signal_handlers_block_by_func (dest,
1541                                    gimp_text_tool_text_notify,
1542                                    text_tool);
1543   g_signal_handlers_block_by_func (dest,
1544                                    gimp_text_tool_text_changed,
1545                                    text_tool);
1546 
1547   g_object_freeze_notify (dest);
1548 
1549   for (list = pspecs; list; list = g_list_next (list))
1550     {
1551       const GParamSpec *pspec;
1552       GValue            value = G_VALUE_INIT;
1553 
1554       /*  look ahead and compress changes  */
1555       if (list->next && list->next->data == list->data)
1556         continue;
1557 
1558       pspec = list->data;
1559 
1560       g_value_init (&value, pspec->value_type);
1561 
1562       g_object_get_property (src,  pspec->name, &value);
1563       g_object_set_property (dest, pspec->name, &value);
1564 
1565       g_value_unset (&value);
1566     }
1567 
1568   g_object_thaw_notify (dest);
1569 
1570   g_signal_handlers_unblock_by_func (dest,
1571                                      gimp_text_tool_text_notify,
1572                                      text_tool);
1573   g_signal_handlers_unblock_by_func (dest,
1574                                      gimp_text_tool_text_changed,
1575                                      text_tool);
1576 }
1577 
1578 static void
gimp_text_tool_create_layer(GimpTextTool * text_tool,GimpText * text)1579 gimp_text_tool_create_layer (GimpTextTool *text_tool,
1580                              GimpText     *text)
1581 {
1582   GimpTool  *tool  = GIMP_TOOL (text_tool);
1583   GimpImage *image = gimp_display_get_image (tool->display);
1584   GimpLayer *layer;
1585   gdouble    x1, y1;
1586   gdouble    x2, y2;
1587 
1588   gimp_text_tool_block_drawing (text_tool);
1589 
1590   if (text)
1591     {
1592       text = gimp_config_duplicate (GIMP_CONFIG (text));
1593     }
1594   else
1595     {
1596       gchar *string;
1597 
1598       if (gimp_text_buffer_has_markup (text_tool->buffer))
1599         {
1600           string = gimp_text_buffer_get_markup (text_tool->buffer);
1601 
1602           g_object_set (text_tool->proxy,
1603                         "markup",   string,
1604                         "box-mode", GIMP_TEXT_BOX_DYNAMIC,
1605                         NULL);
1606         }
1607       else
1608         {
1609           string = gimp_text_buffer_get_text (text_tool->buffer);
1610 
1611           g_object_set (text_tool->proxy,
1612                         "text",     string,
1613                         "box-mode", GIMP_TEXT_BOX_DYNAMIC,
1614                         NULL);
1615         }
1616 
1617       g_free (string);
1618 
1619       text = gimp_config_duplicate (GIMP_CONFIG (text_tool->proxy));
1620     }
1621 
1622   layer = gimp_text_layer_new (image, text);
1623 
1624   g_object_unref (text);
1625 
1626   if (! layer)
1627     {
1628       gimp_text_tool_unblock_drawing (text_tool);
1629       return;
1630     }
1631 
1632   gimp_text_tool_connect (text_tool, GIMP_TEXT_LAYER (layer), text);
1633 
1634   gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TEXT,
1635                                _("Add Text Layer"));
1636 
1637   if (gimp_image_get_floating_selection (image))
1638     {
1639       g_signal_handlers_block_by_func (image,
1640                                        gimp_text_tool_layer_changed,
1641                                        text_tool);
1642 
1643       floating_sel_anchor (gimp_image_get_floating_selection (image));
1644 
1645       g_signal_handlers_unblock_by_func (image,
1646                                          gimp_text_tool_layer_changed,
1647                                          text_tool);
1648     }
1649 
1650   g_object_get (text_tool->widget,
1651                 "x1", &x1,
1652                 "y1", &y1,
1653                 "x2", &x2,
1654                 "y2", &y2,
1655                 NULL);
1656 
1657   if (text_tool->text_box_fixed == FALSE)
1658     {
1659       if (text_tool->text &&
1660           (text_tool->text->base_dir == GIMP_TEXT_DIRECTION_TTB_RTL ||
1661            text_tool->text->base_dir == GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT))
1662         {
1663           x1 -= gimp_item_get_width (GIMP_ITEM (layer));
1664         }
1665     }
1666   gimp_item_set_offset (GIMP_ITEM (layer), x1, y1);
1667 
1668   gimp_image_add_layer (image, layer,
1669                         GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE);
1670 
1671   if (text_tool->text_box_fixed)
1672     {
1673       GimpUnit box_unit = text_tool->proxy->box_unit;
1674       gdouble  xres, yres;
1675 
1676       gimp_image_get_resolution (image, &xres, &yres);
1677 
1678       g_object_set (text_tool->proxy,
1679                     "box-mode",   GIMP_TEXT_BOX_FIXED,
1680                     "box-width",  gimp_pixels_to_units (x2 - x1,
1681                                                         box_unit, xres),
1682                     "box-height", gimp_pixels_to_units (y2 - y1,
1683                                                         box_unit, yres),
1684                     NULL);
1685 
1686       gimp_text_tool_apply (text_tool, TRUE); /* unblocks drawing */
1687     }
1688   else
1689     {
1690       gimp_text_tool_frame_item (text_tool);
1691     }
1692 
1693   gimp_image_undo_group_end (image);
1694 
1695   gimp_image_flush (image);
1696 
1697   gimp_text_tool_set_drawable (text_tool, GIMP_DRAWABLE (layer), FALSE);
1698 
1699   gimp_text_tool_unblock_drawing (text_tool);
1700 }
1701 
1702 #define  RESPONSE_NEW 1
1703 
1704 static void
gimp_text_tool_confirm_response(GtkWidget * widget,gint response_id,GimpTextTool * text_tool)1705 gimp_text_tool_confirm_response (GtkWidget    *widget,
1706                                  gint          response_id,
1707                                  GimpTextTool *text_tool)
1708 {
1709   GimpTextLayer *layer = text_tool->layer;
1710 
1711   gtk_widget_destroy (widget);
1712 
1713   if (layer && layer->text)
1714     {
1715       switch (response_id)
1716         {
1717         case RESPONSE_NEW:
1718           gimp_text_tool_create_layer (text_tool, layer->text);
1719           break;
1720 
1721         case GTK_RESPONSE_ACCEPT:
1722           gimp_text_tool_connect (text_tool, layer, layer->text);
1723 
1724           /*  cause the text layer to be rerendered  */
1725           g_object_notify (G_OBJECT (text_tool->proxy), "markup");
1726 
1727           gimp_text_tool_editor_start (text_tool);
1728           break;
1729 
1730         default:
1731           break;
1732         }
1733     }
1734 }
1735 
1736 static void
gimp_text_tool_confirm_dialog(GimpTextTool * text_tool)1737 gimp_text_tool_confirm_dialog (GimpTextTool *text_tool)
1738 {
1739   GimpTool         *tool  = GIMP_TOOL (text_tool);
1740   GimpDisplayShell *shell = gimp_display_get_shell (tool->display);
1741   GtkWidget        *dialog;
1742   GtkWidget        *vbox;
1743   GtkWidget        *label;
1744 
1745   g_return_if_fail (text_tool->layer != NULL);
1746 
1747   if (text_tool->confirm_dialog)
1748     {
1749       gtk_window_present (GTK_WINDOW (text_tool->confirm_dialog));
1750       return;
1751     }
1752 
1753   dialog = gimp_viewable_dialog_new (GIMP_VIEWABLE (text_tool->layer),
1754                                      GIMP_CONTEXT (gimp_tool_get_options (tool)),
1755                                      _("Confirm Text Editing"),
1756                                      "gimp-text-tool-confirm",
1757                                      GIMP_ICON_LAYER_TEXT_LAYER,
1758                                      _("Confirm Text Editing"),
1759                                      GTK_WIDGET (shell),
1760                                      gimp_standard_help_func, NULL,
1761 
1762                                      _("Create _New Layer"), RESPONSE_NEW,
1763                                      _("_Cancel"),           GTK_RESPONSE_CANCEL,
1764                                      _("_Edit"),             GTK_RESPONSE_ACCEPT,
1765 
1766                                      NULL);
1767 
1768   gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
1769                                            RESPONSE_NEW,
1770                                            GTK_RESPONSE_ACCEPT,
1771                                            GTK_RESPONSE_CANCEL,
1772                                            -1);
1773 
1774   gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
1775 
1776   g_signal_connect (dialog, "response",
1777                     G_CALLBACK (gimp_text_tool_confirm_response),
1778                     text_tool);
1779 
1780   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
1781   gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
1782   gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
1783                       vbox, FALSE, FALSE, 0);
1784   gtk_widget_show (vbox);
1785 
1786   label = gtk_label_new (_("The layer you selected is a text layer but "
1787                            "it has been modified using other tools. "
1788                            "Editing the layer with the text tool will "
1789                            "discard these modifications."
1790                            "\n\n"
1791                            "You can edit the layer or create a new "
1792                            "text layer from its text attributes."));
1793   gtk_label_set_xalign (GTK_LABEL (label), 0.0);
1794   gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1795   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
1796   gtk_widget_show (label);
1797 
1798   gtk_widget_show (dialog);
1799 
1800   text_tool->confirm_dialog = dialog;
1801   g_signal_connect_swapped (dialog, "destroy",
1802                             G_CALLBACK (g_nullify_pointer),
1803                             &text_tool->confirm_dialog);
1804 }
1805 
1806 static void
gimp_text_tool_layer_changed(GimpImage * image,GimpTextTool * text_tool)1807 gimp_text_tool_layer_changed (GimpImage    *image,
1808                               GimpTextTool *text_tool)
1809 {
1810   GimpLayer *layer = gimp_image_get_active_layer (image);
1811 
1812   if (layer != GIMP_LAYER (text_tool->layer))
1813     {
1814       GimpTool    *tool    = GIMP_TOOL (text_tool);
1815       GimpDisplay *display = tool->display;
1816 
1817       if (display)
1818         {
1819           gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
1820 
1821           if (gimp_text_tool_set_drawable (text_tool, GIMP_DRAWABLE (layer),
1822                                            FALSE) &&
1823               GIMP_LAYER (text_tool->layer) == layer)
1824             {
1825               GError *error = NULL;
1826 
1827               if (! gimp_text_tool_start (text_tool, display, layer, &error))
1828                 {
1829                   gimp_text_tool_set_drawable (text_tool, NULL, FALSE);
1830 
1831                   gimp_tool_message_literal (tool, display, error->message);
1832 
1833                   g_clear_error (&error);
1834 
1835                   return;
1836                 }
1837             }
1838         }
1839     }
1840 }
1841 
1842 static void
gimp_text_tool_set_image(GimpTextTool * text_tool,GimpImage * image)1843 gimp_text_tool_set_image (GimpTextTool *text_tool,
1844                           GimpImage    *image)
1845 {
1846   if (text_tool->image == image)
1847     return;
1848 
1849   if (text_tool->image)
1850     {
1851       g_signal_handlers_disconnect_by_func (text_tool->image,
1852                                             gimp_text_tool_layer_changed,
1853                                             text_tool);
1854 
1855       g_object_remove_weak_pointer (G_OBJECT (text_tool->image),
1856                                     (gpointer) &text_tool->image);
1857     }
1858 
1859   text_tool->image = image;
1860 
1861   if (image)
1862     {
1863       GimpTextOptions *options = GIMP_TEXT_TOOL_GET_OPTIONS (text_tool);
1864       gdouble          xres;
1865       gdouble          yres;
1866 
1867       g_object_add_weak_pointer (G_OBJECT (text_tool->image),
1868                                  (gpointer) &text_tool->image);
1869 
1870       g_signal_connect_object (text_tool->image, "active-layer-changed",
1871                                G_CALLBACK (gimp_text_tool_layer_changed),
1872                                text_tool, 0);
1873 
1874       gimp_image_get_resolution (image, &xres, &yres);
1875       gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (options->size_entry), 0,
1876                                       yres, FALSE);
1877     }
1878 }
1879 
1880 static gboolean
gimp_text_tool_set_drawable(GimpTextTool * text_tool,GimpDrawable * drawable,gboolean confirm)1881 gimp_text_tool_set_drawable (GimpTextTool *text_tool,
1882                              GimpDrawable *drawable,
1883                              gboolean      confirm)
1884 {
1885   GimpImage *image = NULL;
1886 
1887   if (text_tool->confirm_dialog)
1888     gtk_widget_destroy (text_tool->confirm_dialog);
1889 
1890   if (drawable)
1891     image = gimp_item_get_image (GIMP_ITEM (drawable));
1892 
1893   gimp_text_tool_set_image (text_tool, image);
1894 
1895   if (GIMP_IS_TEXT_LAYER (drawable) && GIMP_TEXT_LAYER (drawable)->text)
1896     {
1897       GimpTextLayer *layer = GIMP_TEXT_LAYER (drawable);
1898 
1899       if (layer == text_tool->layer && layer->text == text_tool->text)
1900         return TRUE;
1901 
1902       if (layer->modified)
1903         {
1904           if (confirm)
1905             {
1906               gimp_text_tool_connect (text_tool, layer, NULL);
1907               gimp_text_tool_confirm_dialog (text_tool);
1908               return TRUE;
1909             }
1910         }
1911       else
1912         {
1913           gimp_text_tool_connect (text_tool, layer, layer->text);
1914           return TRUE;
1915         }
1916     }
1917 
1918   gimp_text_tool_connect (text_tool, NULL, NULL);
1919 
1920   return FALSE;
1921 }
1922 
1923 static void
gimp_text_tool_block_drawing(GimpTextTool * text_tool)1924 gimp_text_tool_block_drawing (GimpTextTool *text_tool)
1925 {
1926   if (text_tool->drawing_blocked == 0)
1927     {
1928       gimp_draw_tool_pause (GIMP_DRAW_TOOL (text_tool));
1929 
1930       gimp_text_tool_clear_layout (text_tool);
1931     }
1932 
1933   text_tool->drawing_blocked++;
1934 }
1935 
1936 static void
gimp_text_tool_unblock_drawing(GimpTextTool * text_tool)1937 gimp_text_tool_unblock_drawing (GimpTextTool *text_tool)
1938 {
1939   g_return_if_fail (text_tool->drawing_blocked > 0);
1940 
1941   text_tool->drawing_blocked--;
1942 
1943   if (text_tool->drawing_blocked == 0)
1944     gimp_draw_tool_resume (GIMP_DRAW_TOOL (text_tool));
1945 }
1946 
1947 static void
gimp_text_tool_buffer_begin_edit(GimpTextBuffer * buffer,GimpTextTool * text_tool)1948 gimp_text_tool_buffer_begin_edit (GimpTextBuffer *buffer,
1949                                   GimpTextTool   *text_tool)
1950 {
1951   gimp_text_tool_block_drawing (text_tool);
1952 }
1953 
1954 static void
gimp_text_tool_buffer_end_edit(GimpTextBuffer * buffer,GimpTextTool * text_tool)1955 gimp_text_tool_buffer_end_edit (GimpTextBuffer *buffer,
1956                                 GimpTextTool   *text_tool)
1957 {
1958   if (text_tool->text)
1959     {
1960       gchar *string;
1961 
1962       if (gimp_text_buffer_has_markup (buffer))
1963         {
1964           string = gimp_text_buffer_get_markup (buffer);
1965 
1966           g_object_set (text_tool->proxy,
1967                         "markup", string,
1968                         NULL);
1969         }
1970       else
1971         {
1972           string = gimp_text_buffer_get_text (buffer);
1973 
1974           g_object_set (text_tool->proxy,
1975                         "text", string,
1976                         NULL);
1977         }
1978 
1979       g_free (string);
1980     }
1981   else
1982     {
1983       gimp_text_tool_create_layer (text_tool, NULL);
1984     }
1985 
1986   gimp_text_tool_unblock_drawing (text_tool);
1987 }
1988 
1989 static void
gimp_text_tool_buffer_color_applied(GimpTextBuffer * buffer,const GimpRGB * color,GimpTextTool * text_tool)1990 gimp_text_tool_buffer_color_applied (GimpTextBuffer *buffer,
1991                                      const GimpRGB  *color,
1992                                      GimpTextTool   *text_tool)
1993 {
1994   gimp_palettes_add_color_history (GIMP_TOOL (text_tool)->tool_info->gimp,
1995                                    color);
1996 }
1997 
1998 
1999 /*  public functions  */
2000 
2001 void
gimp_text_tool_clear_layout(GimpTextTool * text_tool)2002 gimp_text_tool_clear_layout (GimpTextTool *text_tool)
2003 {
2004   g_clear_object (&text_tool->layout);
2005 }
2006 
2007 gboolean
gimp_text_tool_ensure_layout(GimpTextTool * text_tool)2008 gimp_text_tool_ensure_layout (GimpTextTool *text_tool)
2009 {
2010   if (! text_tool->layout && text_tool->text)
2011     {
2012       GimpImage *image = gimp_item_get_image (GIMP_ITEM (text_tool->layer));
2013       gdouble    xres;
2014       gdouble    yres;
2015       GError    *error = NULL;
2016 
2017       gimp_image_get_resolution (image, &xres, &yres);
2018 
2019       text_tool->layout = gimp_text_layout_new (text_tool->layer->text,
2020                                                 xres, yres, &error);
2021       if (error)
2022         {
2023           gimp_message_literal (image->gimp, NULL, GIMP_MESSAGE_ERROR, error->message);
2024           g_error_free (error);
2025         }
2026     }
2027 
2028   return text_tool->layout != NULL;
2029 }
2030 
2031 void
gimp_text_tool_apply(GimpTextTool * text_tool,gboolean push_undo)2032 gimp_text_tool_apply (GimpTextTool *text_tool,
2033                       gboolean      push_undo)
2034 {
2035   const GParamSpec *pspec = NULL;
2036   GimpImage        *image;
2037   GimpTextLayer    *layer;
2038   GList            *list;
2039   gboolean          undo_group = FALSE;
2040 
2041   if (text_tool->idle_id)
2042     {
2043       g_source_remove (text_tool->idle_id);
2044       text_tool->idle_id = 0;
2045 
2046       gimp_text_tool_unblock_drawing (text_tool);
2047     }
2048 
2049   g_return_if_fail (text_tool->text != NULL);
2050   g_return_if_fail (text_tool->layer != NULL);
2051 
2052   layer = text_tool->layer;
2053   image = gimp_item_get_image (GIMP_ITEM (layer));
2054 
2055   g_return_if_fail (layer->text == text_tool->text);
2056 
2057   /*  Walk over the list of changes and figure out if we are changing
2058    *  a single property or need to push a full text undo.
2059    */
2060   for (list = text_tool->pending;
2061        list && list->next && list->next->data == list->data;
2062        list = list->next)
2063     /* do nothing */;
2064 
2065   if (g_list_length (list) == 1)
2066     pspec = list->data;
2067 
2068   /*  If we are changing a single property, we don't need to push
2069    *  an undo if all of the following is true:
2070    *   - the redo stack is empty
2071    *   - the last item on the undo stack is a text undo
2072    *   - the last undo changed the same text property on the same layer
2073    *   - the last undo happened less than TEXT_UNDO_TIMEOUT seconds ago
2074    */
2075   if (pspec)
2076     {
2077       GimpUndo *undo = gimp_image_undo_can_compress (image, GIMP_TYPE_TEXT_UNDO,
2078                                                      GIMP_UNDO_TEXT_LAYER);
2079 
2080       if (undo && GIMP_ITEM_UNDO (undo)->item == GIMP_ITEM (layer))
2081         {
2082           GimpTextUndo *text_undo = GIMP_TEXT_UNDO (undo);
2083 
2084           if (text_undo->pspec == pspec)
2085             {
2086               if (gimp_undo_get_age (undo) < TEXT_UNDO_TIMEOUT)
2087                 {
2088                   GimpTool    *tool = GIMP_TOOL (text_tool);
2089                   GimpContext *context;
2090 
2091                   context = GIMP_CONTEXT (gimp_tool_get_options (tool));
2092 
2093                   push_undo = FALSE;
2094                   gimp_undo_reset_age (undo);
2095                   gimp_undo_refresh_preview (undo, context);
2096                 }
2097             }
2098         }
2099     }
2100 
2101   if (push_undo)
2102     {
2103       if (layer->modified)
2104         {
2105           undo_group = TRUE;
2106           gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_TEXT, NULL);
2107 
2108           gimp_image_undo_push_text_layer_modified (image, NULL, layer);
2109 
2110           /*  see comment in gimp_text_layer_set()  */
2111           gimp_image_undo_push_drawable_mod (image, NULL,
2112                                              GIMP_DRAWABLE (layer), TRUE);
2113         }
2114 
2115       gimp_image_undo_push_text_layer (image, NULL, layer, pspec);
2116     }
2117 
2118   gimp_text_tool_apply_list (text_tool, list);
2119 
2120   g_list_free (text_tool->pending);
2121   text_tool->pending = NULL;
2122 
2123   if (push_undo)
2124     {
2125       g_object_set (layer, "modified", FALSE, NULL);
2126 
2127       if (undo_group)
2128         gimp_image_undo_group_end (image);
2129     }
2130 
2131   gimp_text_tool_frame_item (text_tool);
2132 
2133   gimp_image_flush (image);
2134 }
2135 
2136 gboolean
gimp_text_tool_set_layer(GimpTextTool * text_tool,GimpLayer * layer)2137 gimp_text_tool_set_layer (GimpTextTool *text_tool,
2138                           GimpLayer    *layer)
2139 {
2140   g_return_val_if_fail (GIMP_IS_TEXT_TOOL (text_tool), FALSE);
2141   g_return_val_if_fail (layer == NULL || GIMP_IS_LAYER (layer), FALSE);
2142 
2143   if (layer == GIMP_LAYER (text_tool->layer))
2144     return TRUE;
2145 
2146   /*  FIXME this function works, and I have no clue why: first we set
2147    *  the drawable, then we HALT the tool and start() it without
2148    *  re-setting the drawable. Why this works perfectly anyway when
2149    *  double clicking a text layer in the layers dialog... no idea.
2150    */
2151   if (gimp_text_tool_set_drawable (text_tool, GIMP_DRAWABLE (layer), TRUE))
2152     {
2153       GimpTool    *tool = GIMP_TOOL (text_tool);
2154       GimpItem    *item = GIMP_ITEM (layer);
2155       GimpContext *context;
2156       GimpDisplay *display;
2157 
2158       context = gimp_get_user_context (tool->tool_info->gimp);
2159       display = gimp_context_get_display (context);
2160 
2161       if (! display ||
2162           gimp_display_get_image (display) != gimp_item_get_image (item))
2163         {
2164           GList *list;
2165 
2166           display = NULL;
2167 
2168           for (list = gimp_get_display_iter (tool->tool_info->gimp);
2169                list;
2170                list = g_list_next (list))
2171             {
2172               display = list->data;
2173 
2174               if (gimp_display_get_image (display) == gimp_item_get_image (item))
2175                 {
2176                   gimp_context_set_display (context, display);
2177                   break;
2178                 }
2179 
2180               display = NULL;
2181             }
2182         }
2183 
2184       if (tool->display)
2185         gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display);
2186 
2187       if (display)
2188         {
2189           GError *error = NULL;
2190 
2191           if (! gimp_text_tool_start (text_tool, display, layer, &error))
2192             {
2193               gimp_text_tool_set_drawable (text_tool, NULL, FALSE);
2194 
2195               gimp_tool_message_literal (tool, display, error->message);
2196 
2197               g_clear_error (&error);
2198 
2199               return FALSE;
2200             }
2201 
2202           tool->drawable = GIMP_DRAWABLE (layer);
2203         }
2204     }
2205 
2206   return TRUE;
2207 }
2208 
2209 gboolean
gimp_text_tool_get_has_text_selection(GimpTextTool * text_tool)2210 gimp_text_tool_get_has_text_selection (GimpTextTool *text_tool)
2211 {
2212   GtkTextBuffer *buffer = GTK_TEXT_BUFFER (text_tool->buffer);
2213 
2214   return gtk_text_buffer_get_has_selection (buffer);
2215 }
2216 
2217 void
gimp_text_tool_delete_selection(GimpTextTool * text_tool)2218 gimp_text_tool_delete_selection (GimpTextTool *text_tool)
2219 {
2220   GtkTextBuffer *buffer = GTK_TEXT_BUFFER (text_tool->buffer);
2221 
2222   if (gtk_text_buffer_get_has_selection (buffer))
2223     {
2224       gtk_text_buffer_delete_selection (buffer, TRUE, TRUE);
2225     }
2226 }
2227 
2228 void
gimp_text_tool_cut_clipboard(GimpTextTool * text_tool)2229 gimp_text_tool_cut_clipboard (GimpTextTool *text_tool)
2230 {
2231   GimpDisplayShell *shell;
2232   GtkClipboard     *clipboard;
2233 
2234   g_return_if_fail (GIMP_IS_TEXT_TOOL (text_tool));
2235 
2236   shell = gimp_display_get_shell (GIMP_TOOL (text_tool)->display);
2237 
2238   clipboard = gtk_widget_get_clipboard (GTK_WIDGET (shell),
2239                                         GDK_SELECTION_CLIPBOARD);
2240 
2241   gtk_text_buffer_cut_clipboard (GTK_TEXT_BUFFER (text_tool->buffer),
2242                                  clipboard, TRUE);
2243 }
2244 
2245 void
gimp_text_tool_copy_clipboard(GimpTextTool * text_tool)2246 gimp_text_tool_copy_clipboard (GimpTextTool *text_tool)
2247 {
2248   GimpDisplayShell *shell;
2249   GtkClipboard     *clipboard;
2250 
2251   g_return_if_fail (GIMP_IS_TEXT_TOOL (text_tool));
2252 
2253   shell = gimp_display_get_shell (GIMP_TOOL (text_tool)->display);
2254 
2255   clipboard = gtk_widget_get_clipboard (GTK_WIDGET (shell),
2256                                         GDK_SELECTION_CLIPBOARD);
2257 
2258   /*  need to block "end-user-action" on the text buffer, because
2259    *  GtkTextBuffer considers copying text to the clipboard an
2260    *  undo-relevant user action, which is clearly a bug, but what
2261    *  can we do...
2262    */
2263   g_signal_handlers_block_by_func (text_tool->buffer,
2264                                    gimp_text_tool_buffer_begin_edit,
2265                                    text_tool);
2266   g_signal_handlers_block_by_func (text_tool->buffer,
2267                                    gimp_text_tool_buffer_end_edit,
2268                                    text_tool);
2269 
2270   gtk_text_buffer_copy_clipboard (GTK_TEXT_BUFFER (text_tool->buffer),
2271                                   clipboard);
2272 
2273   g_signal_handlers_unblock_by_func (text_tool->buffer,
2274                                      gimp_text_tool_buffer_end_edit,
2275                                      text_tool);
2276   g_signal_handlers_unblock_by_func (text_tool->buffer,
2277                                      gimp_text_tool_buffer_begin_edit,
2278                                      text_tool);
2279 }
2280 
2281 void
gimp_text_tool_paste_clipboard(GimpTextTool * text_tool)2282 gimp_text_tool_paste_clipboard (GimpTextTool *text_tool)
2283 {
2284   GimpDisplayShell *shell;
2285   GtkClipboard     *clipboard;
2286 
2287   g_return_if_fail (GIMP_IS_TEXT_TOOL (text_tool));
2288 
2289   shell = gimp_display_get_shell (GIMP_TOOL (text_tool)->display);
2290 
2291   clipboard = gtk_widget_get_clipboard (GTK_WIDGET (shell),
2292                                         GDK_SELECTION_CLIPBOARD);
2293 
2294   gtk_text_buffer_paste_clipboard (GTK_TEXT_BUFFER (text_tool->buffer),
2295                                    clipboard, NULL, TRUE);
2296 }
2297 
2298 void
gimp_text_tool_create_vectors(GimpTextTool * text_tool)2299 gimp_text_tool_create_vectors (GimpTextTool *text_tool)
2300 {
2301   GimpVectors *vectors;
2302 
2303   g_return_if_fail (GIMP_IS_TEXT_TOOL (text_tool));
2304 
2305   if (! text_tool->text || ! text_tool->image)
2306     return;
2307 
2308   vectors = gimp_text_vectors_new (text_tool->image, text_tool->text);
2309 
2310   if (text_tool->layer)
2311     {
2312       gint x, y;
2313 
2314       gimp_item_get_offset (GIMP_ITEM (text_tool->layer), &x, &y);
2315       gimp_item_translate (GIMP_ITEM (vectors), x, y, FALSE);
2316     }
2317 
2318   gimp_image_add_vectors (text_tool->image, vectors,
2319                           GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE);
2320 
2321   gimp_image_flush (text_tool->image);
2322 }
2323 
2324 void
gimp_text_tool_create_vectors_warped(GimpTextTool * text_tool)2325 gimp_text_tool_create_vectors_warped (GimpTextTool *text_tool)
2326 {
2327   GimpVectors       *vectors0;
2328   GimpVectors       *vectors;
2329   gdouble            box_width;
2330   gdouble            box_height;
2331   GimpTextDirection  dir;
2332   gdouble            offset = 0.0;
2333 
2334   g_return_if_fail (GIMP_IS_TEXT_TOOL (text_tool));
2335 
2336   if (! text_tool->text || ! text_tool->image || ! text_tool->layer)
2337     return;
2338 
2339   box_width  = gimp_item_get_width  (GIMP_ITEM (text_tool->layer));
2340   box_height = gimp_item_get_height (GIMP_ITEM (text_tool->layer));
2341 
2342   vectors0 = gimp_image_get_active_vectors (text_tool->image);
2343   if (! vectors0)
2344     return;
2345 
2346   vectors = gimp_text_vectors_new (text_tool->image, text_tool->text);
2347 
2348   offset = 0;
2349   dir = gimp_text_tool_get_direction (text_tool);
2350   switch (dir)
2351     {
2352     case GIMP_TEXT_DIRECTION_LTR:
2353     case GIMP_TEXT_DIRECTION_RTL:
2354       offset = 0.5 * box_height;
2355       break;
2356     case GIMP_TEXT_DIRECTION_TTB_RTL:
2357     case GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT:
2358     case GIMP_TEXT_DIRECTION_TTB_LTR:
2359     case GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT:
2360       {
2361         GimpStroke *stroke = NULL;
2362 
2363         while ((stroke = gimp_vectors_stroke_get_next (vectors, stroke)))
2364           {
2365             gimp_stroke_rotate (stroke, 0, 0, 270);
2366             gimp_stroke_translate (stroke, 0, box_width);
2367           }
2368       }
2369       offset = 0.5 * box_width;
2370       break;
2371     }
2372 
2373   gimp_vectors_warp_vectors (vectors0, vectors, offset);
2374 
2375   gimp_item_set_visible (GIMP_ITEM (vectors), TRUE, FALSE);
2376 
2377   gimp_image_add_vectors (text_tool->image, vectors,
2378                           GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE);
2379 
2380   gimp_image_flush (text_tool->image);
2381 }
2382 
2383 GimpTextDirection
gimp_text_tool_get_direction(GimpTextTool * text_tool)2384 gimp_text_tool_get_direction  (GimpTextTool *text_tool)
2385 {
2386   GimpTextOptions *options = GIMP_TEXT_TOOL_GET_OPTIONS (text_tool);
2387   return options->base_dir;
2388 }
2389