gimptexttool.c 19.3 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* The GIMP -- an image manipulation program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
16
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
17
 */
Sven Neumann's avatar
Sven Neumann committed
18
19
20

#include "config.h"

Elliot Lee's avatar
Elliot Lee committed
21
22
#include <stdlib.h>
#include <string.h>
Manish Singh's avatar
Manish Singh committed
23

24
#include <gtk/gtk.h>
25
#include <pango/pangoft2.h>
26

27
#include "libgimpbase/gimpbase.h"
28
29
#include "libgimpwidgets/gimpwidgets.h"

Michael Natterer's avatar
Michael Natterer committed
30
#include "tools-types.h"
Sven Neumann's avatar
Sven Neumann committed
31

32
33
34
#include "paint-funcs/paint-funcs.h"

#include "base/base-types.h"
Michael Natterer's avatar
Michael Natterer committed
35
36
#include "base/pixel-region.h"
#include "base/tile-manager.h"
37

38
39
40
41
#include "core/gimpchannel.h"
#include "core/gimpimage.h"
#include "core/gimpimage-mask.h"
#include "core/gimplayer.h"
42
#include "core/gimptoolinfo.h"
43

44
#include "widgets/gimpfontselection.h"
Michael Natterer's avatar
Michael Natterer committed
45

46
#include "display/gimpdisplay.h"
47
#include "display/gimpdisplay-foreach.h"
Michael Natterer's avatar
Michael Natterer committed
48
#include "display/gimpdisplayshell.h"
49
50

#include "gimpeditselectiontool.h"
51
#include "gimptexttool.h"
52
53
#include "tool_options.h"

54
#include "floating_sel.h"
55
56
#include "undo.h"

57
58
#include "libgimp/gimpintl.h"

59
60
61

#define DEFAULT_FONT       "sans Normal"
#define DEFAULT_FONT_SIZE  50
62

Michael Natterer's avatar
Michael Natterer committed
63

64
65
66
/*  the text tool structures  */

typedef struct _TextOptions TextOptions;
67

68
69
struct _TextOptions
{
70
  GimpToolOptions  tool_options;
71

72
  gchar           *fontname_d;
73
  GtkWidget       *font_selection;
74
75
76
77
78
79
80
81
82
83
84
85

  gdouble          size;
  gdouble          size_d;
  GtkObject       *size_w;

  gdouble          border;
  gdouble          border_d;
  GtkObject       *border_w;

  GimpUnit         unit;
  GimpUnit         unit_d;
  GtkWidget       *unit_w;
86
87
};

88

89
90
/*  local function prototypes  */

91
92
93
static void   gimp_text_tool_class_init      (GimpTextToolClass *klass);
static void   gimp_text_tool_init            (GimpTextTool      *tool);

94
static void   gimp_text_tool_finalize        (GObject         *object);
95
96
97

static void   text_tool_control              (GimpTool        *tool,
					      ToolAction       tool_action,
Michael Natterer's avatar
Michael Natterer committed
98
					      GimpDisplay     *gdisp);
99
static void   text_tool_button_press         (GimpTool        *tool,
100
101
102
                                              GimpCoords      *coords,
                                              guint32          time,
					      GdkModifierType  state,
Michael Natterer's avatar
Michael Natterer committed
103
					      GimpDisplay     *gdisp);
104
static void   text_tool_button_release       (GimpTool        *tool,
105
106
107
                                              GimpCoords      *coords,
                                              guint32          time,
					      GdkModifierType  state,
Michael Natterer's avatar
Michael Natterer committed
108
					      GimpDisplay     *gdisp);
109
static void   text_tool_cursor_update        (GimpTool        *tool,
110
111
                                              GimpCoords      *coords,
					      GdkModifierType  state,
Michael Natterer's avatar
Michael Natterer committed
112
					      GimpDisplay     *gdisp);
113

114
static void   text_tool_render               (GimpTextTool    *text_tool);
115

116
117
static GimpToolOptions * text_tool_options_new   (GimpToolInfo    *tool_info);
static void              text_tool_options_reset (GimpToolOptions *tool_options);
118

119
120

/*  local variables  */
121

122
static GimpToolClass *parent_class = NULL;
123

Elliot Lee's avatar
Elliot Lee committed
124

125
/*  public functions  */
126

127
void
128
129
gimp_text_tool_register (Gimp                     *gimp,
                         GimpToolRegisterCallback  callback)
130
{
131
132
133
134
135
136
137
138
139
140
  (* callback) (gimp,
                GIMP_TYPE_TEXT_TOOL,
                text_tool_options_new,
                FALSE,
                "gimp:text_tool",
                _("Text Tool"),
                _("Add text to the image"),
                N_("/Tools/Text"), "T",
                NULL, "tools/text.html",
                GIMP_STOCK_TOOL_TEXT);
141
142
}

143
GType
144
145
gimp_text_tool_get_type (void)
{
146
  static GType tool_type = 0;
147
148
149

  if (! tool_type)
    {
150
      static const GTypeInfo tool_info =
151
      {
152
153
154
155
        sizeof (GimpTextToolClass),
	(GBaseInitFunc) NULL,
	(GBaseFinalizeFunc) NULL,
	(GClassInitFunc) gimp_text_tool_class_init,
156
157
	NULL,           /* class_finalize */
	NULL,           /* class_data     */
158
	sizeof (GimpTextTool),
159
160
	0,              /* n_preallocs    */
	(GInstanceInitFunc) gimp_text_tool_init,
161
162
      };

163
164
      tool_type = g_type_register_static (GIMP_TYPE_TOOL,
					  "GimpTextTool", 
165
                                          &tool_info, 0);
166
167
168
169
170
    }

  return tool_type;
}

171
172
173

/*  private functions  */

174
static void
175
gimp_text_tool_class_init (GimpTextToolClass *klass)
176
{
177
178
  GObjectClass  *object_class;
  GimpToolClass *tool_class;
179

180
181
  object_class = G_OBJECT_CLASS (klass);
  tool_class   = GIMP_TOOL_CLASS (klass);
182

183
184
  parent_class = g_type_class_peek_parent (klass);

185
  object_class->finalize     = gimp_text_tool_finalize;
186
187
188
189
190
191
192
193
194
195
196
197
198
199

  tool_class->control        = text_tool_control;
  tool_class->button_press   = text_tool_button_press;
  tool_class->button_release = text_tool_button_release;
  tool_class->cursor_update  = text_tool_cursor_update;
}

static void
gimp_text_tool_init (GimpTextTool *text_tool)
{
  GimpTool *tool;

  tool = GIMP_TOOL (text_tool);
 
200
201
  text_tool->pango_context = pango_ft2_get_context ();

202
  tool->tool_cursor = GIMP_TEXT_TOOL_CURSOR;
203
204
205
206
  tool->scroll_lock = TRUE;  /* Disallow scrolling */
}

static void
207
gimp_text_tool_finalize (GObject *object)
208
{
209
210
211
  GimpTextTool *text_tool;

  text_tool = GIMP_TEXT_TOOL (object);
212

213
214
215
216
217
218
  if (text_tool->pango_context)
    {
      g_object_unref (text_tool->pango_context);
      text_tool->pango_context = NULL;
    }

219
  G_OBJECT_CLASS (parent_class)->finalize (object);
220
221
}

Elliot Lee's avatar
Elliot Lee committed
222
static void
Michael Natterer's avatar
Michael Natterer committed
223
224
225
text_tool_control (GimpTool    *tool,
		   ToolAction   action,
		   GimpDisplay *gdisp)
Elliot Lee's avatar
Elliot Lee committed
226
{
227
228
229
230
231
232
233
234
235
236
237
238
239
240
  switch (action)
    {
    case PAUSE:
      break;

    case RESUME:
      break;

    case HALT:
      break;

    default:
      break;
    }
241
242

  GIMP_TOOL_CLASS (parent_class)->control (tool, action, gdisp);
243
244
245
}

static void
246
247
248
249
250
text_tool_button_press (GimpTool        *tool,
                        GimpCoords      *coords,
                        guint32          time,
			GdkModifierType  state,
			GimpDisplay     *gdisp)
251
252
{
  GimpTextTool *text_tool;
253
  GimpLayer    *layer;
254
255

  text_tool = GIMP_TEXT_TOOL (tool);
Elliot Lee's avatar
Elliot Lee committed
256

257
  text_tool->gdisp = gdisp;
Elliot Lee's avatar
Elliot Lee committed
258
259

  tool->state = ACTIVE;
260
  tool->gdisp = gdisp;
Elliot Lee's avatar
Elliot Lee committed
261

262
263
  text_tool->click_x = coords->x;
  text_tool->click_y = coords->y;
264
265
266
267

  if ((layer = gimp_image_pick_correlate_layer (gdisp->gimage,
                                                text_tool->click_x,
                                                text_tool->click_y)))
268
    /* if there is a floating selection, and this aint it, use the move tool */
269
270
    if (gimp_layer_is_floating_sel (layer))
      {
271
	init_edit_selection (tool, gdisp, coords, EDIT_LAYER_TRANSLATE);
272
273
274
275
	return;
      }

  text_tool_render (GIMP_TEXT_TOOL (tool));
Elliot Lee's avatar
Elliot Lee committed
276
277
278
}

static void
279
280
281
282
283
text_tool_button_release (GimpTool        *tool,
                          GimpCoords      *coords,
                          guint32          time,
			  GdkModifierType  state,
			  GimpDisplay     *gdisp)
Elliot Lee's avatar
Elliot Lee committed
284
285
286
287
288
{
  tool->state = INACTIVE;
}

static void
289
290
291
292
text_tool_cursor_update (GimpTool        *tool,
                         GimpCoords      *coords,
			 GdkModifierType  state,
			 GimpDisplay     *gdisp)
Elliot Lee's avatar
Elliot Lee committed
293
{
Michael Natterer's avatar
Michael Natterer committed
294
295
296
297
  GimpDisplayShell *shell;
  GimpLayer        *layer;

  shell = GIMP_DISPLAY_SHELL (gdisp->shell);
298

299
  layer = gimp_image_pick_correlate_layer (gdisp->gimage, coords->x, coords->y);
300

301
302
303
  if (layer && gimp_layer_is_floating_sel (layer))
    {
      /* if there is a floating selection, and this aint it ... */
304

305
306
307
308
309
310
311
312
313
314
315
316
      gimp_display_shell_install_tool_cursor (shell,
                                              GDK_FLEUR,
                                              GIMP_MOVE_TOOL_CURSOR,
                                              GIMP_CURSOR_MODIFIER_NONE);
    }
  else
    {
      gimp_display_shell_install_tool_cursor (shell,
                                              GDK_XTERM,
                                              GIMP_TEXT_TOOL_CURSOR,
                                              GIMP_CURSOR_MODIFIER_NONE);
    }
Elliot Lee's avatar
Elliot Lee committed
317
318
319
}

static void
320
text_tool_render (GimpTextTool *text_tool)
Elliot Lee's avatar
Elliot Lee committed
321
{
322
  TextOptions          *options;
Michael Natterer's avatar
Michael Natterer committed
323
  GimpDisplay          *gdisp;
324
325
326
  PangoFontDescription *font_desc;
  gchar                *fontname;
  gchar                *text;
327
328
329
  gdouble               border; 
  gdouble               size;
  gdouble               factor;
Elliot Lee's avatar
Elliot Lee committed
330

331
332
  options = (TextOptions *) GIMP_TOOL (text_tool)->tool_info->tool_options;

333
  gdisp = text_tool->gdisp;
334
  
335
  font_desc = gimp_font_selection_get_font_desc 
336
    (GIMP_FONT_SELECTION (options->font_selection));
337
338
339
340
341
342

  if (!font_desc)
    {
      g_message (_("No font choosen or font invalid."));
      return;
    }
343
  
344
345
  size   = options->size;
  border = options->border;
346

347
  switch (options->unit)
348
349
350
351
    {
    case GIMP_UNIT_PIXEL:
      break;
    default:
352
353
      factor = (gdisp->gimage->xresolution /
                gimp_unit_get_factor (options->unit));
354
355
356
357
358
      size   *= factor;
      border *= factor;
      break;
    }

359
  pango_font_description_set_size (font_desc, PANGO_SCALE * MAX (1, size));
360
361
  fontname = pango_font_description_to_string (font_desc);
  pango_font_description_free (font_desc);
362

363
  text = "gimp";  /* FIXME */
Elliot Lee's avatar
Elliot Lee committed
364

365
  text_render (gdisp->gimage, gimp_image_active_drawable (gdisp->gimage),
366
	       text_tool->click_x, text_tool->click_y,
367
	       fontname, text, border,
368
               TRUE /* antialias */);
Elliot Lee's avatar
Elliot Lee committed
369

370
  g_free (fontname);
Elliot Lee's avatar
Elliot Lee committed
371

372
  gdisplays_flush ();
Elliot Lee's avatar
Elliot Lee committed
373
374
}

Manish Singh's avatar
Manish Singh committed
375
GimpLayer *
376
text_render (GimpImage    *gimage,
377
	     GimpDrawable *drawable,
378
379
	     gint          text_x,
	     gint          text_y,
380
381
	     const gchar  *fontname,
	     const gchar  *text,
382
383
	     gint          border,
	     gint          antialias)
Elliot Lee's avatar
Elliot Lee committed
384
{
385
386
387
  PangoFontDescription *font_desc;
  PangoContext         *context;
  PangoLayout          *layout;
388
  PangoRectangle        ink;
Sven Neumann's avatar
Sven Neumann committed
389
  PangoRectangle        logical;
390
  GimpImageType         layer_type;
391
  GimpLayer            *layer = NULL;
392
393
394

  g_return_val_if_fail (fontname != NULL, FALSE);
  g_return_val_if_fail (text != NULL, FALSE);
Elliot Lee's avatar
Elliot Lee committed
395

396
397
398
  if (border < 0)
    border = 0;

Elliot Lee's avatar
Elliot Lee committed
399
  /*  determine the layer type  */
Manish Singh's avatar
Manish Singh committed
400
  if (drawable)
401
    layer_type = gimp_drawable_type_with_alpha (drawable);
Elliot Lee's avatar
Elliot Lee committed
402
  else
403
    layer_type = gimp_image_base_type_with_alpha (gimage);
Elliot Lee's avatar
Elliot Lee committed
404

405
406
407
408
409
410
411
412
413
414
  font_desc = pango_font_description_from_string (fontname);
  g_return_val_if_fail (font_desc != NULL, NULL);
  if (!font_desc)
    return NULL;
  
  context = pango_ft2_get_context ();
  layout = pango_layout_new (context);
  pango_layout_set_font_description (layout, font_desc);
  pango_font_description_free (font_desc);
  pango_layout_set_text (layout, text, -1);
415

Sven Neumann's avatar
Sven Neumann committed
416
  pango_layout_get_pixel_extents (layout, &ink, &logical);
Elliot Lee's avatar
Elliot Lee committed
417

418
419
420
421
422
  g_print ("ink rect: %d x %d @ %d, %d\n", 
           ink.width, ink.height, ink.x, ink.y);
  g_print ("logical rect: %d x %d @ %d, %d\n", 
           logical.width, logical.height, logical.x, logical.y);
  
423
  if (ink.width > 0 && ink.height > 0)
Elliot Lee's avatar
Elliot Lee committed
424
    {
425
      TileManager *mask;
426
427
428
      PixelRegion  textPR;
      PixelRegion  maskPR;
      FT_Bitmap    bitmap;
429
      guchar      *black = NULL;
430
      guchar       color[MAX_CHANNELS];
431
432
      gint         width;
      gint         height;
433
434
      gint         y;

435
436
437
      bitmap.width  = ink.width;
      bitmap.rows   = ink.height;
      bitmap.pitch  = ink.width;
438
439
      if (bitmap.pitch & 3)
        bitmap.pitch += 4 - (bitmap.pitch & 3);
440
441

      bitmap.buffer = g_malloc0 (bitmap.rows * bitmap.pitch);
442
      
443
      pango_ft2_render_layout (&bitmap, layout, 
Sven Neumann's avatar
Sven Neumann committed
444
                               - ink.x,
445
                               - ink.height - ink.y);
446
     
447
448
      width  = ink.width  + 2 * border;
      height = ink.height + 2 * border;
449

450
451
      mask = tile_manager_new (width, height, 1);
      pixel_region_init (&maskPR, mask, 0, 0, width, height, TRUE);
452

453
454
      if (border)
        black = g_malloc0 (width);
455

456
457
458
      for (y = 0; y < border; y++)
        pixel_region_set_row (&maskPR, 0, y, width, black);
      for (; y < height - border; y++)
459
        {
460
461
462
463
464
465
466
          if (border)
            {
              pixel_region_set_row (&maskPR, 0, y, border, black);
              pixel_region_set_row (&maskPR, width - border, y, border, black);
            }
          pixel_region_set_row (&maskPR, border, y, bitmap.width,
                                bitmap.buffer + (y - border) * bitmap.pitch);
467
        }
468
469
      for (; y < height; y++)
        pixel_region_set_row (&maskPR, 0, y, width, black);
470

471
472
473
474
      g_free (black);
      g_free (bitmap.buffer);

      layer = gimp_layer_new (gimage, width, height, layer_type,
475
                              _("Text Layer"), OPAQUE_OPACITY, NORMAL_MODE);
Elliot Lee's avatar
Elliot Lee committed
476
477

      /*  color the layer buffer  */
478
      gimp_image_get_foreground (gimage, drawable, color);
479
480
      color[GIMP_DRAWABLE (layer)->bytes - 1] = OPAQUE_OPACITY;
      pixel_region_init (&textPR, GIMP_DRAWABLE (layer)->tiles,
481
			 0, 0, width, height, TRUE);
Elliot Lee's avatar
Elliot Lee committed
482
483
484
      color_region (&textPR, color);

      /*  apply the text mask  */
485
      pixel_region_init (&textPR, GIMP_DRAWABLE (layer)->tiles,
486
                         0, 0, width, height, TRUE);
487
      pixel_region_init (&maskPR, mask,
488
			 0, 0, width, height, FALSE);
Manish Singh's avatar
Manish Singh committed
489
      apply_mask_to_region (&textPR, &maskPR, OPAQUE_OPACITY);
Elliot Lee's avatar
Elliot Lee committed
490
491

      /*  Start a group undo  */
492
      undo_push_group_start (gimage, TEXT_UNDO);
Elliot Lee's avatar
Elliot Lee committed
493
494

      /*  Set the layer offsets  */
495
496
      GIMP_DRAWABLE (layer)->offset_x = text_x;
      GIMP_DRAWABLE (layer)->offset_y = text_y;
Elliot Lee's avatar
Elliot Lee committed
497
498
499
500
501
502

      /*  If there is a selection mask clear it--
       *  this might not always be desired, but in general,
       *  it seems like the correct behavior.
       */
      if (! gimage_mask_is_empty (gimage))
Michael Natterer's avatar
Michael Natterer committed
503
	gimp_channel_clear (gimp_image_get_mask (gimage));
Elliot Lee's avatar
Elliot Lee committed
504

505
      /*  If the drawable is NULL, create a new layer  */
506
      if (drawable == NULL)
507
	gimp_image_add_layer (gimage, layer, -1);
Elliot Lee's avatar
Elliot Lee committed
508
509
      /*  Otherwise, instantiate the text as the new floating selection */
      else
510
	floating_sel_attach (layer, drawable);
Elliot Lee's avatar
Elliot Lee committed
511
512
513

      /*  end the group undo  */
      undo_push_group_end (gimage);
514
515

      tile_manager_destroy (mask);
Elliot Lee's avatar
Elliot Lee committed
516
517
    }

518
519
  g_object_unref (layout);
  g_object_unref (context);  
Elliot Lee's avatar
Elliot Lee committed
520

Sven Neumann's avatar
Sven Neumann committed
521
  return layer;
Elliot Lee's avatar
Elliot Lee committed
522
523
}

524
gboolean
525
526
527
528
529
530
text_get_extents (const gchar *fontname,
		  const gchar *text,
		  gint        *width,
		  gint        *height,
		  gint        *ascent,
		  gint        *descent)
Elliot Lee's avatar
Elliot Lee committed
531
{
532
533
534
535
  PangoFontDescription *font_desc;
  PangoContext         *context;
  PangoLayout          *layout;
  PangoRectangle        rect;
536

537
538
  g_return_val_if_fail (fontname != NULL, FALSE);
  g_return_val_if_fail (text != NULL, FALSE);
539

540
541
  font_desc = pango_font_description_from_string (fontname);
  if (!font_desc)
Elliot Lee's avatar
Elliot Lee committed
542
    return FALSE;
543
544
545
546
547
  
  context = pango_ft2_get_context ();
  layout = pango_layout_new (context);
  pango_layout_set_font_description (layout, font_desc);
  pango_font_description_free (font_desc);
Elliot Lee's avatar
Elliot Lee committed
548

549
  pango_layout_set_text (layout, text, -1);
Elliot Lee's avatar
Elliot Lee committed
550

551
  pango_layout_get_pixel_extents (layout, &rect, NULL);
Elliot Lee's avatar
Elliot Lee committed
552

553
554
555
556
557
558
559
560
  if (width)
    *width = rect.width;
  if (height)
    *height = rect.height;
  if (ascent)
    *ascent = -rect.y;
  if (descent)
    *descent = rect.height + rect.y;
Elliot Lee's avatar
Elliot Lee committed
561

562
563
  g_object_unref (layout);
  g_object_unref (context);  
Elliot Lee's avatar
Elliot Lee committed
564

565
  return TRUE;
566
}
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594


/*  tool options stuff  */

static GimpToolOptions *
text_tool_options_new (GimpToolInfo *tool_info)
{
  TextOptions  *options;
  PangoContext *pango_context;
  GtkWidget    *vbox;
  GtkWidget    *table;
  GtkWidget    *size_spinbutton;
  GtkWidget    *border_spinbutton;

  options = g_new0 (TextOptions, 1);

  tool_options_init ((GimpToolOptions *) options, tool_info);

  ((GimpToolOptions *) options)->reset_func = text_tool_options_reset;

  options->fontname_d                   = DEFAULT_FONT;
  options->border  = options->border_d  = 0;
  options->size    = options->size_d    = DEFAULT_FONT_SIZE;
  options->unit    = options->unit_d    = GIMP_UNIT_PIXEL;

  /*  the main vbox  */
  vbox = options->tool_options.main_vbox;

595
  pango_context = pango_ft2_get_context ();
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681

  options->font_selection = gimp_font_selection_new (pango_context);

  g_object_unref (G_OBJECT (pango_context));

  gimp_font_selection_set_fontname 
    (GIMP_FONT_SELECTION (options->font_selection), DEFAULT_FONT);
  gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (options->font_selection),
                      FALSE, FALSE, 0);
  gtk_widget_show (options->font_selection);
  
  /*  the size entries */
  table = gtk_table_new (3, 2, FALSE);
  gtk_table_set_col_spacing (GTK_TABLE (table), 0, 4);
  gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (table), FALSE, FALSE, 0);

  options->size_w = gtk_adjustment_new (options->size_d, 1e-5, 32767.0,
                                        1.0, 50.0, 0.0);
  size_spinbutton = 
    gtk_spin_button_new (GTK_ADJUSTMENT (options->size_w), 1.0, 0.0);
  gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (size_spinbutton), TRUE);
  g_signal_connect (G_OBJECT (options->size_w), "value_changed",
                    G_CALLBACK (gimp_double_adjustment_update),
                    &options->size);
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
                             _("Size:"), 1.0, 0.5,
                             size_spinbutton, 1, FALSE);

  options->border_w = gtk_adjustment_new (options->border_d, 1e-5, 32767.0,
                                          1.0, 50.0, 0.0);
  border_spinbutton =
    gtk_spin_button_new (GTK_ADJUSTMENT (options->border_w), 1.0, 0.0);
  gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (border_spinbutton), TRUE);
  g_signal_connect (G_OBJECT (options->border_w), "value_changed",
                    G_CALLBACK (gimp_double_adjustment_update),
                    &options->border);
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
                             _("Border:"), 1.0, 0.5,
                             border_spinbutton, 1, FALSE);

  options->unit_w =
    gimp_unit_menu_new ("%a", options->unit_d, TRUE, FALSE, TRUE);
  g_signal_connect (G_OBJECT (options->unit_w), "unit_changed",
                    G_CALLBACK (gimp_unit_menu_update),
                    &options->unit);
  g_object_set_data (G_OBJECT (options->unit_w), "set_digits",
                     size_spinbutton);
  g_object_set_data (G_OBJECT (size_spinbutton), "set_digits",
                     border_spinbutton);
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
                             _("Unit:"), 1.0, 0.5,
                             options->unit_w, 1, FALSE);

  gtk_widget_show (table);

  return (GimpToolOptions *) options;
}

static void
text_tool_options_reset (GimpToolOptions *tool_options)
{
  TextOptions *options;
  GtkWidget   *spinbutton;

  options = (TextOptions *) tool_options;

  gimp_font_selection_set_fontname 
    (GIMP_FONT_SELECTION (options->font_selection), options->fontname_d);
  gtk_adjustment_set_value (GTK_ADJUSTMENT (options->size_w),
			    options->size_d);
  gtk_adjustment_set_value (GTK_ADJUSTMENT (options->border_w),
			    options->border_d);
  
  /* resetting the unit menu is a bit tricky ... */
  options->unit = options->unit_d;
  gimp_unit_menu_set_unit (GIMP_UNIT_MENU (options->unit_w),
                           options->unit_d);
  spinbutton =
    g_object_get_data (G_OBJECT (options->unit_w), "set_digits");
  while (spinbutton)
    {
      gtk_spin_button_set_digits (GTK_SPIN_BUTTON (spinbutton), 0);
      spinbutton =
        g_object_get_data (G_OBJECT (spinbutton), "set_digits");
    }
}