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