]> Pileus Git - ~andy/gtk/blob - gtk/gtkeditable.c
1ae0659935890537d16e301d3d8e4b5bb1f6e272
[~andy/gtk] / gtk / gtkeditable.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 #include <ctype.h>
19 #include <string.h>
20 #ifdef USE_XIM
21 #include "gdk/gdkx.h"
22 #endif
23 #include "gdk/gdkkeysyms.h"
24 #include "gdk/gdki18n.h"
25 #include "gtkeditable.h"
26 #include "gtkmain.h"
27 #include "gtkselection.h"
28 #include "gtksignal.h"
29
30 #define MIN_EDITABLE_WIDTH  150
31 #define DRAW_TIMEOUT     20
32 #define INNER_BORDER     2
33
34 enum {
35   INSERT_TEXT,
36   DELETE_TEXT,
37   UPDATE_TEXT,
38   GET_CHARS,
39   SET_SELECTION,
40   CHANGED,
41   LAST_SIGNAL
42 };
43
44
45 typedef void (*GtkEditableSignal1) (GtkObject *object,
46                                     gpointer   arg1,
47                                     gint       arg2,
48                                     gpointer   arg3,
49                                     gpointer   data);
50 typedef void (*GtkEditableSignal2) (GtkObject *object,
51                                     gint       arg1,
52                                     gint       arg2,
53                                     gpointer   data);
54
55 typedef gpointer (*GtkEditableSignal3) (GtkObject *object,
56                                         gint       arg1,
57                                         gint       arg2,
58                                         gpointer   data);
59
60
61 static void gtk_editable_marshal_signal_1     (GtkObject         *object,
62                                             GtkSignalFunc      func,
63                                             gpointer           func_data,
64                                             GtkArg            *args);
65 static void gtk_editable_marshal_signal_2     (GtkObject         *object,
66                                             GtkSignalFunc      func,
67                                             gpointer           func_data,
68                                             GtkArg            *args);
69 static void gtk_editable_marshal_signal_3     (GtkObject         *object,
70                                             GtkSignalFunc      func,
71                                             gpointer           func_data,
72                                             GtkArg            *args);
73
74 static void gtk_editable_class_init          (GtkEditableClass     *klass);
75 static void gtk_editable_init                (GtkEditable          *editable);
76 static void gtk_editable_finalize            (GtkObject         *object);
77 static gint gtk_editable_selection_clear     (GtkWidget         *widget,
78                                            GdkEventSelection *event);
79 static void gtk_editable_selection_handler   (GtkWidget    *widget,
80                                            GtkSelectionData  *selection_data,
81                                            gpointer      data);
82 static void gtk_editable_selection_received  (GtkWidget         *widget,
83                                            GtkSelectionData  *selection_data);
84
85 static void gtk_editable_set_selection    (GtkEditable          *editable,
86                                            gint               start,
87                                            gint               end);
88
89 static GtkWidgetClass *parent_class = NULL;
90 static gint editable_signals[LAST_SIGNAL] = { 0 };
91 static GdkAtom ctext_atom = GDK_NONE;
92 static GdkAtom text_atom = GDK_NONE;
93 static GdkAtom clipboard_atom = GDK_NONE;
94
95 guint
96 gtk_editable_get_type ()
97 {
98   static guint editable_type = 0;
99
100   if (!editable_type)
101     {
102       GtkTypeInfo editable_info =
103       {
104         "GtkEditable",
105         sizeof (GtkEditable),
106         sizeof (GtkEditableClass),
107         (GtkClassInitFunc) gtk_editable_class_init,
108         (GtkObjectInitFunc) gtk_editable_init,
109         (GtkArgSetFunc) NULL,
110         (GtkArgGetFunc) NULL,
111       };
112
113       editable_type = gtk_type_unique (gtk_widget_get_type (), &editable_info);
114     }
115
116   return editable_type;
117 }
118
119 static void
120 gtk_editable_class_init (GtkEditableClass *class)
121 {
122   GtkObjectClass *object_class;
123   GtkWidgetClass *widget_class;
124
125   object_class = (GtkObjectClass*) class;
126   widget_class = (GtkWidgetClass*) class;
127
128   parent_class = gtk_type_class (gtk_widget_get_type ());
129
130   editable_signals[INSERT_TEXT] =
131     gtk_signal_new ("insert_text",
132                     GTK_RUN_LAST,
133                     object_class->type,
134                     GTK_SIGNAL_OFFSET (GtkEditableClass, insert_text),
135                     gtk_editable_marshal_signal_1,
136                     GTK_TYPE_NONE, 3,
137                     GTK_TYPE_STRING, GTK_TYPE_INT,
138                     GTK_TYPE_POINTER);
139   editable_signals[DELETE_TEXT] =
140     gtk_signal_new ("delete_text",
141                     GTK_RUN_LAST,
142                     object_class->type,
143                     GTK_SIGNAL_OFFSET (GtkEditableClass, delete_text),
144                     gtk_editable_marshal_signal_2,
145                     GTK_TYPE_NONE, 2,
146                     GTK_TYPE_INT, GTK_TYPE_INT);
147   editable_signals[UPDATE_TEXT] =
148     gtk_signal_new ("update_text",
149                     GTK_RUN_LAST,
150                     object_class->type,
151                     GTK_SIGNAL_OFFSET (GtkEditableClass, update_text),
152                     gtk_editable_marshal_signal_2,
153                     GTK_TYPE_NONE, 2,
154                     GTK_TYPE_INT, GTK_TYPE_INT);
155   editable_signals[GET_CHARS] =
156     gtk_signal_new ("get_chars",
157                     GTK_RUN_LAST,
158                     object_class->type,
159                     GTK_SIGNAL_OFFSET (GtkEditableClass, get_chars),
160                     gtk_editable_marshal_signal_3,
161                     GTK_TYPE_POINTER, 2,
162                     GTK_TYPE_INT, GTK_TYPE_INT);
163   editable_signals[SET_SELECTION] =
164     gtk_signal_new ("set_selection",
165                     GTK_RUN_LAST,
166                     object_class->type,
167                     GTK_SIGNAL_OFFSET (GtkEditableClass, set_selection),
168                     gtk_editable_marshal_signal_2,
169                     GTK_TYPE_NONE, 2,
170                     GTK_TYPE_INT, GTK_TYPE_INT);
171   editable_signals[CHANGED] =
172     gtk_signal_new ("changed",
173                     GTK_RUN_LAST,
174                     object_class->type,
175                     GTK_SIGNAL_OFFSET (GtkEditableClass, changed),
176                     gtk_signal_default_marshaller,
177                     GTK_TYPE_NONE, 0);
178
179   gtk_object_class_add_signals (object_class, editable_signals, LAST_SIGNAL);
180
181   object_class->finalize = gtk_editable_finalize;
182
183   widget_class->selection_clear_event = gtk_editable_selection_clear;
184   widget_class->selection_received = gtk_editable_selection_received;
185
186   class->insert_text = NULL;
187   class->delete_text = NULL;
188   class->update_text = NULL;
189   class->get_chars = NULL;
190   class->set_selection = NULL;
191   class->changed = NULL;
192 }
193
194 static void
195 gtk_editable_init (GtkEditable *editable)
196 {
197   GTK_WIDGET_SET_FLAGS (editable, GTK_CAN_FOCUS);
198
199   editable->selection_start_pos = 0;
200   editable->selection_end_pos = 0;
201   editable->has_selection = FALSE;
202   editable->editable = 1;
203   editable->clipboard_text = NULL;
204
205 #ifdef USE_XIM
206   editable->ic = NULL;
207 #endif
208
209   if (!clipboard_atom)
210     clipboard_atom = gdk_atom_intern ("CLIPBOARD", FALSE);
211
212   gtk_selection_add_handler (GTK_WIDGET(editable), GDK_SELECTION_PRIMARY,
213                              GDK_TARGET_STRING, gtk_editable_selection_handler,
214                              NULL);
215   gtk_selection_add_handler (GTK_WIDGET(editable), clipboard_atom,
216                              GDK_TARGET_STRING, gtk_editable_selection_handler,
217                              NULL);
218
219   if (!text_atom)
220     text_atom = gdk_atom_intern ("TEXT", FALSE);
221
222   gtk_selection_add_handler (GTK_WIDGET(editable), GDK_SELECTION_PRIMARY,
223                              text_atom,
224                              gtk_editable_selection_handler,
225                              NULL);
226   gtk_selection_add_handler (GTK_WIDGET(editable), clipboard_atom,
227                              text_atom,
228                              gtk_editable_selection_handler,
229                              NULL);
230
231   if (!ctext_atom)
232     ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);
233
234   gtk_selection_add_handler (GTK_WIDGET(editable), GDK_SELECTION_PRIMARY,
235                              ctext_atom,
236                              gtk_editable_selection_handler,
237                              NULL);
238   gtk_selection_add_handler (GTK_WIDGET(editable), clipboard_atom,
239                              ctext_atom,
240                              gtk_editable_selection_handler,
241                              NULL);
242 }
243
244 static void
245 gtk_editable_marshal_signal_1 (GtkObject      *object,
246                             GtkSignalFunc   func,
247                             gpointer        func_data,
248                             GtkArg         *args)
249 {
250   GtkEditableSignal1 rfunc;
251
252   rfunc = (GtkEditableSignal1) func;
253
254   (* rfunc) (object, GTK_VALUE_STRING (args[0]), GTK_VALUE_INT (args[1]),
255              GTK_VALUE_POINTER (args[2]), func_data);
256 }
257
258 static void
259 gtk_editable_marshal_signal_2 (GtkObject      *object,
260                             GtkSignalFunc   func,
261                             gpointer        func_data,
262                             GtkArg         *args)
263 {
264   GtkEditableSignal2 rfunc;
265
266   rfunc = (GtkEditableSignal2) func;
267
268   (* rfunc) (object, GTK_VALUE_INT (args[0]), GTK_VALUE_INT (args[1]),
269              func_data);
270 }
271
272 static void
273 gtk_editable_marshal_signal_3 (GtkObject      *object,
274                                GtkSignalFunc   func,
275                                gpointer        func_data,
276                                GtkArg         *args)
277 {
278   GtkEditableSignal3 rfunc;
279   gpointer *return_val;
280
281   rfunc = (GtkEditableSignal3) func;
282
283   return_val = GTK_RETLOC_POINTER (args[2]);
284   
285   *return_val = (* rfunc) (object, 
286                            GTK_VALUE_INT (args[0]), 
287                            GTK_VALUE_INT (args[1]),
288                            func_data);
289 }
290
291 static void
292 gtk_editable_finalize (GtkObject *object)
293 {
294   GtkEditable *editable;
295
296   g_return_if_fail (object != NULL);
297   g_return_if_fail (GTK_IS_EDITABLE (object));
298
299   editable = GTK_EDITABLE (object);
300
301 #ifdef USE_XIM
302   if (editable->ic)
303     {
304       gdk_ic_destroy (editable->ic);
305       editable->ic = NULL;
306     }
307 #endif
308
309   (* GTK_OBJECT_CLASS (parent_class)->finalize) (object);
310 }
311
312 void
313 gtk_editable_insert_text (GtkEditable *editable,
314                           const gchar *new_text,
315                           gint         new_text_length,
316                           gint        *position)
317 {
318   gchar buf[64];
319   gchar *text;
320
321   g_return_if_fail (editable != NULL);
322   g_return_if_fail (GTK_IS_EDITABLE (editable));
323
324   if (new_text_length <= 64)
325     text = buf;
326   else
327     text = g_new (gchar, new_text_length);
328
329   strncpy (text, new_text, new_text_length);
330
331   gtk_signal_emit (GTK_OBJECT (editable), editable_signals[INSERT_TEXT],
332                    text, new_text_length, position);
333   gtk_signal_emit (GTK_OBJECT (editable), editable_signals[CHANGED]);
334
335   if (new_text_length > 64)
336     g_free (text);
337 }
338
339 void
340 gtk_editable_delete_text (GtkEditable *editable,
341                           gint         start_pos,
342                           gint         end_pos)
343 {
344   g_return_if_fail (editable != NULL);
345   g_return_if_fail (GTK_IS_EDITABLE (editable));
346
347   gtk_signal_emit (GTK_OBJECT (editable), editable_signals[DELETE_TEXT],
348                    start_pos, end_pos);
349   gtk_signal_emit (GTK_OBJECT (editable), editable_signals[CHANGED]);
350 }
351
352 static void
353 gtk_editable_update_text (GtkEditable *editable,
354                        gint      start_pos,
355                        gint      end_pos)
356 {
357   g_return_if_fail (editable != NULL);
358   g_return_if_fail (GTK_IS_EDITABLE (editable));
359
360   gtk_signal_emit (GTK_OBJECT (editable), editable_signals[UPDATE_TEXT],
361                    start_pos, end_pos);
362 }
363
364 gchar *    
365 gtk_editable_get_chars      (GtkEditable      *editable,
366                              gint              start,
367                              gint              end)
368 {
369   gchar *retval = NULL;
370   
371   g_return_val_if_fail (editable != NULL, NULL);
372   g_return_val_if_fail (GTK_IS_EDITABLE (editable), NULL);
373
374   gtk_signal_emit (GTK_OBJECT (editable), editable_signals[GET_CHARS],
375                    start, end, &retval);
376
377   return retval;
378 }
379
380 static void
381 gtk_editable_set_selection (GtkEditable *editable,
382                             gint      start_pos,
383                             gint      end_pos)
384 {
385   g_return_if_fail (editable != NULL);
386   g_return_if_fail (GTK_IS_EDITABLE (editable));
387
388   gtk_signal_emit (GTK_OBJECT (editable), editable_signals[SET_SELECTION],
389                    start_pos, end_pos);
390 }
391
392 static gint
393 gtk_editable_selection_clear (GtkWidget         *widget,
394                            GdkEventSelection *event)
395 {
396   GtkEditable *editable;
397
398   g_return_val_if_fail (widget != NULL, FALSE);
399   g_return_val_if_fail (GTK_IS_EDITABLE (widget), FALSE);
400   g_return_val_if_fail (event != NULL, FALSE);
401
402   /* Let the selection handling code know that the selection
403    * has been changed, since we've overriden the default handler */
404   gtk_selection_clear (widget, event);
405
406   editable = GTK_EDITABLE (widget);
407
408   if (event->selection == GDK_SELECTION_PRIMARY)
409     {
410       if (editable->has_selection)
411         {
412           editable->has_selection = FALSE;
413           gtk_editable_update_text (editable, editable->selection_start_pos,
414                                     editable->selection_end_pos);
415         }
416     }
417   else if (event->selection == clipboard_atom)
418     {
419       g_free (editable->clipboard_text);
420       editable->clipboard_text = NULL;
421     }
422
423   return FALSE;
424 }
425
426 static void
427 gtk_editable_selection_handler (GtkWidget        *widget,
428                                 GtkSelectionData *selection_data,
429                                 gpointer          data)
430 {
431   GtkEditable *editable;
432   gint selection_start_pos;
433   gint selection_end_pos;
434
435   gchar *str;
436   gint length;
437
438   g_return_if_fail (widget != NULL);
439   g_return_if_fail (GTK_IS_EDITABLE (widget));
440
441   editable = GTK_EDITABLE (widget);
442
443   if (selection_data->selection == GDK_SELECTION_PRIMARY)
444     {
445       selection_start_pos = MIN (editable->selection_start_pos, editable->selection_end_pos);
446       selection_end_pos = MAX (editable->selection_start_pos, editable->selection_end_pos);
447       str = gtk_editable_get_chars(editable, 
448                                    selection_start_pos, 
449                                    selection_end_pos);
450       length = selection_end_pos - selection_start_pos;
451     }
452   else                          /* CLIPBOARD */
453     {
454       if (!editable->clipboard_text)
455         return;                 /* Refuse */
456
457       str = editable->clipboard_text;
458       length = strlen (editable->clipboard_text);
459     }
460   
461   if (selection_data->target == GDK_SELECTION_TYPE_STRING)
462     {
463       gtk_selection_data_set (selection_data,
464                               GDK_SELECTION_TYPE_STRING,
465                               8*sizeof(gchar), str, length);
466     }
467   else if (selection_data->target == text_atom ||
468            selection_data->target == ctext_atom)
469     {
470       guchar *text;
471       gchar c;
472       GdkAtom encoding;
473       gint format;
474       gint new_length;
475
476       c = str[length];
477       str[length] = '\0';
478       gdk_string_to_compound_text (str, &encoding, &format, &text, &new_length);
479       gtk_selection_data_set (selection_data, encoding, format, text, new_length);
480       gdk_free_compound_text (text);
481       str[length] = c;
482     }
483
484   if (str != editable->clipboard_text)
485     g_free (str);
486 }
487
488 static void
489 gtk_editable_selection_received  (GtkWidget         *widget,
490                                   GtkSelectionData  *selection_data)
491 {
492   GtkEditable *editable;
493   gint reselect;
494   gint old_pos;
495   gint tmp_pos;
496   enum {INVALID, STRING, CTEXT} type;
497
498   g_return_if_fail (widget != NULL);
499   g_return_if_fail (GTK_IS_EDITABLE (widget));
500
501   editable = GTK_EDITABLE (widget);
502
503   if (selection_data->type == GDK_TARGET_STRING)
504     type = STRING;
505   else if (selection_data->type == ctext_atom)
506     type = CTEXT;
507   else
508     type = INVALID;
509
510   if (type == INVALID || selection_data->length < 0)
511     {
512     /* avoid infinite loop */
513     if (selection_data->target != GDK_TARGET_STRING)
514       gtk_selection_convert (widget, selection_data->selection,
515                              GDK_TARGET_STRING, GDK_CURRENT_TIME);
516     return;
517   }
518
519   reselect = FALSE;
520
521   if ((editable->selection_start_pos != editable->selection_end_pos) && 
522       (!editable->has_selection || 
523        (selection_data->selection == clipboard_atom)))
524     {
525       reselect = TRUE;
526
527       /* Don't want to call gtk_editable_delete_selection here if we are going
528        * to reclaim the selection to avoid extra server traffic */
529       if (editable->has_selection)
530         {
531           gtk_editable_delete_text (editable,
532                                  MIN (editable->selection_start_pos, editable->selection_end_pos),
533                                  MAX (editable->selection_start_pos, editable->selection_end_pos));
534         }
535       else
536         gtk_editable_delete_selection (editable);
537     }
538
539   tmp_pos = old_pos = editable->current_pos;
540
541   switch (type)
542     {
543     case STRING:
544       selection_data->data[selection_data->length] = 0;
545       gtk_editable_insert_text (editable, selection_data->data,
546                                 strlen (selection_data->data), &tmp_pos);
547       editable->current_pos = tmp_pos;
548       break;
549     case CTEXT:
550       {
551         gchar **list;
552         gint count;
553         gint i;
554
555         count = gdk_text_property_to_text_list (selection_data->type,
556                                                 selection_data->format, 
557                                                 selection_data->data,
558                                                 selection_data->length,
559                                                 &list);
560         for (i=0; i<count; i++) 
561           {
562             gtk_editable_insert_text (editable, list[i], strlen (list[i]), &tmp_pos);
563             editable->current_pos = tmp_pos;
564           }
565         if (count > 0)
566           gdk_free_text_list (list);
567       }
568       break;
569     case INVALID:               /* quiet compiler */
570       break;
571     }
572
573   if (reselect)
574     gtk_editable_set_selection (editable, old_pos, editable->current_pos);
575 }
576
577 void
578 gtk_editable_delete_selection (GtkEditable *editable)
579 {
580   guint start;
581   guint end;
582
583   if (!editable->editable)
584     return;
585
586   start = editable->selection_start_pos;
587   end = editable->selection_end_pos;
588
589   editable->selection_start_pos = 0;
590   editable->selection_end_pos = 0;
591
592   if (start != end)
593     gtk_editable_delete_text (editable, MIN (start, end), MAX (start,end));
594
595   if (editable->has_selection)
596     {
597       editable->has_selection = FALSE;
598       if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == GTK_WIDGET (editable)->window)
599         gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, GDK_CURRENT_TIME);
600     }
601 }
602
603 void
604 gtk_editable_claim_selection (GtkEditable *editable, 
605                               gboolean  claim, 
606                               guint32   time)
607 {
608   g_return_if_fail (GTK_WIDGET_REALIZED (editable));
609
610   editable->has_selection = FALSE;
611   
612   if (claim)
613     {
614       if (gtk_selection_owner_set (GTK_WIDGET(editable), GDK_SELECTION_PRIMARY, time))
615         editable->has_selection = TRUE;
616     }
617   else
618     {
619       if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == 
620           GTK_WIDGET(editable)->window)
621         gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, time);
622     }
623 }
624
625 void
626 gtk_editable_select_region (GtkEditable *editable,
627                             gint         start,
628                             gint         end)
629 {
630   if (GTK_WIDGET_REALIZED (editable))
631     gtk_editable_claim_selection (editable, start != end, GDK_CURRENT_TIME);
632
633   gtk_editable_set_selection (editable, start, end);
634 }
635
636 void
637 gtk_editable_cut_clipboard (GtkEditable *editable, guint32 time)
638 {
639   gtk_editable_copy_clipboard (editable, time);
640   gtk_editable_delete_selection (editable);
641 }
642
643 void
644 gtk_editable_copy_clipboard (GtkEditable *editable, guint32 time)
645 {
646   gint selection_start_pos; 
647   gint selection_end_pos;
648
649   selection_start_pos = MIN (editable->selection_start_pos, editable->selection_end_pos);
650   selection_end_pos = MAX (editable->selection_start_pos, editable->selection_end_pos);
651  
652   if (selection_start_pos != selection_end_pos)
653     {
654       if (gtk_selection_owner_set (GTK_WIDGET (editable),
655                                    clipboard_atom,
656                                    time))
657         editable->clipboard_text = gtk_editable_get_chars (editable,
658                                                            selection_start_pos,
659                                                            selection_end_pos);
660     }
661 }
662
663 void
664 gtk_editable_paste_clipboard (GtkEditable *editable, guint32 time)
665 {
666   if (editable->editable)
667     gtk_selection_convert (GTK_WIDGET(editable), 
668                            clipboard_atom, ctext_atom, time);
669 }
670
671 void
672 gtk_editable_changed (GtkEditable *editable)
673 {
674   gtk_signal_emit (GTK_OBJECT (editable), editable_signals[CHANGED]);
675 }