]> Pileus Git - ~andy/gtk/blob - gtk/gtktextchild.c
entrycompletion: Don't reconnect signals all the time
[~andy/gtk] / gtk / gtktextchild.c
1 /* gtktextchild.c - child pixmaps and widgets
2  *
3  * Copyright (c) 1994 The Regents of the University of California.
4  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
5  * Copyright (c) 2000      Red Hat, Inc.
6  * Tk -> Gtk port by Havoc Pennington <hp@redhat.com>
7  *
8  * This software is copyrighted by the Regents of the University of
9  * California, Sun Microsystems, Inc., and other parties.  The
10  * following terms apply to all files associated with the software
11  * unless explicitly disclaimed in individual files.
12  *
13  * The authors hereby grant permission to use, copy, modify,
14  * distribute, and license this software and its documentation for any
15  * purpose, provided that existing copyright notices are retained in
16  * all copies and that this notice is included verbatim in any
17  * distributions. No written agreement, license, or royalty fee is
18  * required for any of the authorized uses.  Modifications to this
19  * software may be copyrighted by their authors and need not follow
20  * the licensing terms described here, provided that the new terms are
21  * clearly indicated on the first page of each file where they apply.
22  *
23  * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
24  * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
25  * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
26  * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
27  * OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
30  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
31  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
32  * NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
33  * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
34  * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
35  *
36  * GOVERNMENT USE: If you are acquiring this software on behalf of the
37  * U.S. government, the Government shall have only "Restricted Rights"
38  * in the software and related documentation as defined in the Federal
39  * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2).  If you
40  * are acquiring the software on behalf of the Department of Defense,
41  * the software shall be classified as "Commercial Computer Software"
42  * and the Government shall have only "Restricted Rights" as defined
43  * in Clause 252.227-7013 (c) (1) of DFARs.  Notwithstanding the
44  * foregoing, the authors grant the U.S. Government and others acting
45  * in its behalf permission to use and distribute the software in
46  * accordance with the terms specified in this license.
47  *
48  */
49
50 #define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
51 #include "config.h"
52 #include "gtktextchild.h"
53 #include "gtktextbtree.h"
54 #include "gtktextlayout.h"
55 #include "gtkintl.h"
56
57 #define CHECK_IN_BUFFER(anchor)                                         \
58   G_STMT_START {                                                        \
59     if ((anchor)->segment == NULL)                                      \
60       {                                                                 \
61         g_warning ("%s: GtkTextChildAnchor hasn't been in a buffer yet",\
62                    G_STRFUNC);                                          \
63       }                                                                 \
64   } G_STMT_END
65
66 #define CHECK_IN_BUFFER_RETURN(anchor, val)                             \
67   G_STMT_START {                                                        \
68     if ((anchor)->segment == NULL)                                      \
69       {                                                                 \
70         g_warning ("%s: GtkTextChildAnchor hasn't been in a buffer yet",\
71                    G_STRFUNC);                                          \
72         return (val);                                                   \
73       }                                                                 \
74   } G_STMT_END
75
76 static GtkTextLineSegment *
77 pixbuf_segment_cleanup_func (GtkTextLineSegment *seg,
78                              GtkTextLine        *line)
79 {
80   /* nothing */
81   return seg;
82 }
83
84 static int
85 pixbuf_segment_delete_func (GtkTextLineSegment *seg,
86                             GtkTextLine        *line,
87                             gboolean            tree_gone)
88 {
89   if (seg->body.pixbuf.pixbuf)
90     g_object_unref (seg->body.pixbuf.pixbuf);
91
92   g_free (seg);
93
94   return 0;
95 }
96
97 static void
98 pixbuf_segment_check_func (GtkTextLineSegment *seg,
99                            GtkTextLine        *line)
100 {
101   if (seg->next == NULL)
102     g_error ("pixbuf segment is the last segment in a line");
103
104   if (seg->byte_count != 3)
105     g_error ("pixbuf segment has byte count of %d", seg->byte_count);
106
107   if (seg->char_count != 1)
108     g_error ("pixbuf segment has char count of %d", seg->char_count);
109 }
110
111
112 const GtkTextLineSegmentClass gtk_text_pixbuf_type = {
113   "pixbuf",                          /* name */
114   FALSE,                                            /* leftGravity */
115   NULL,                                          /* splitFunc */
116   pixbuf_segment_delete_func,                             /* deleteFunc */
117   pixbuf_segment_cleanup_func,                            /* cleanupFunc */
118   NULL,                                                    /* lineChangeFunc */
119   pixbuf_segment_check_func                               /* checkFunc */
120
121 };
122
123 #define PIXBUF_SEG_SIZE ((unsigned) (G_STRUCT_OFFSET (GtkTextLineSegment, body) \
124         + sizeof (GtkTextPixbuf)))
125
126 GtkTextLineSegment *
127 _gtk_pixbuf_segment_new (GdkPixbuf *pixbuf)
128 {
129   GtkTextLineSegment *seg;
130
131   seg = g_malloc (PIXBUF_SEG_SIZE);
132
133   seg->type = &gtk_text_pixbuf_type;
134
135   seg->next = NULL;
136
137   /* We convert to the 0xFFFC "unknown character",
138    * a 3-byte sequence in UTF-8.
139    */
140   seg->byte_count = GTK_TEXT_UNKNOWN_CHAR_UTF8_LEN;
141   seg->char_count = 1;
142
143   seg->body.pixbuf.pixbuf = pixbuf;
144
145   g_object_ref (pixbuf);
146
147   return seg;
148 }
149
150
151 static GtkTextLineSegment *
152 child_segment_cleanup_func (GtkTextLineSegment *seg,
153                             GtkTextLine        *line)
154 {
155   seg->body.child.line = line;
156
157   return seg;
158 }
159
160 static int
161 child_segment_delete_func (GtkTextLineSegment *seg,
162                            GtkTextLine       *line,
163                            gboolean           tree_gone)
164 {
165   GSList *tmp_list;
166   GSList *copy;
167
168   _gtk_text_btree_unregister_child_anchor (seg->body.child.obj);
169   
170   seg->body.child.tree = NULL;
171   seg->body.child.line = NULL;
172
173   /* avoid removing widgets while walking the list */
174   copy = g_slist_copy (seg->body.child.widgets);
175   tmp_list = copy;
176   while (tmp_list != NULL)
177     {
178       GtkWidget *child = tmp_list->data;
179
180       gtk_widget_destroy (child);
181       
182       tmp_list = g_slist_next (tmp_list);
183     }
184
185   /* On removal from the widget's parents (GtkTextView),
186    * the widget should have been removed from the anchor.
187    */
188   g_assert (seg->body.child.widgets == NULL);
189
190   g_slist_free (copy);
191   
192   _gtk_widget_segment_unref (seg);  
193   
194   return 0;
195 }
196
197 static void
198 child_segment_check_func (GtkTextLineSegment *seg,
199                           GtkTextLine        *line)
200 {
201   if (seg->next == NULL)
202     g_error ("child segment is the last segment in a line");
203
204   if (seg->byte_count != 3)
205     g_error ("child segment has byte count of %d", seg->byte_count);
206
207   if (seg->char_count != 1)
208     g_error ("child segment has char count of %d", seg->char_count);
209 }
210
211 const GtkTextLineSegmentClass gtk_text_child_type = {
212   "child-widget",                                        /* name */
213   FALSE,                                                 /* leftGravity */
214   NULL,                                                  /* splitFunc */
215   child_segment_delete_func,                             /* deleteFunc */
216   child_segment_cleanup_func,                            /* cleanupFunc */
217   NULL,                                                  /* lineChangeFunc */
218   child_segment_check_func                               /* checkFunc */
219 };
220
221 #define WIDGET_SEG_SIZE ((unsigned) (G_STRUCT_OFFSET (GtkTextLineSegment, body) \
222         + sizeof (GtkTextChildBody)))
223
224 GtkTextLineSegment *
225 _gtk_widget_segment_new (GtkTextChildAnchor *anchor)
226 {
227   GtkTextLineSegment *seg;
228
229   seg = g_malloc (WIDGET_SEG_SIZE);
230
231   seg->type = &gtk_text_child_type;
232
233   seg->next = NULL;
234
235   seg->byte_count = 3; /* We convert to the 0xFFFC "unknown character",
236                         * a 3-byte sequence in UTF-8
237                         */
238   seg->char_count = 1;
239
240   seg->body.child.obj = anchor;
241   seg->body.child.obj->segment = seg;
242   seg->body.child.widgets = NULL;
243   seg->body.child.tree = NULL;
244   seg->body.child.line = NULL;
245
246   g_object_ref (anchor);
247   
248   return seg;
249 }
250
251 void
252 _gtk_widget_segment_add    (GtkTextLineSegment *widget_segment,
253                             GtkWidget          *child)
254 {
255   g_return_if_fail (widget_segment->type == &gtk_text_child_type);
256   g_return_if_fail (widget_segment->body.child.tree != NULL);
257
258   g_object_ref (child);
259   
260   widget_segment->body.child.widgets =
261     g_slist_prepend (widget_segment->body.child.widgets,
262                      child);
263 }
264
265 void
266 _gtk_widget_segment_remove (GtkTextLineSegment *widget_segment,
267                             GtkWidget          *child)
268 {
269   g_return_if_fail (widget_segment->type == &gtk_text_child_type);
270   
271   widget_segment->body.child.widgets =
272     g_slist_remove (widget_segment->body.child.widgets,
273                     child);
274
275   g_object_unref (child);
276 }
277
278 void
279 _gtk_widget_segment_ref (GtkTextLineSegment *widget_segment)
280 {
281   g_assert (widget_segment->type == &gtk_text_child_type);
282
283   g_object_ref (widget_segment->body.child.obj);
284 }
285
286 void
287 _gtk_widget_segment_unref (GtkTextLineSegment *widget_segment)
288 {
289   g_assert (widget_segment->type == &gtk_text_child_type);
290
291   g_object_unref (widget_segment->body.child.obj);
292 }
293
294 GtkTextLayout*
295 _gtk_anchored_child_get_layout (GtkWidget *child)
296 {
297   return g_object_get_data (G_OBJECT (child), "gtk-text-child-anchor-layout");  
298 }
299
300 static void
301 _gtk_anchored_child_set_layout (GtkWidget     *child,
302                                 GtkTextLayout *layout)
303 {
304   g_object_set_data (G_OBJECT (child),
305                      I_("gtk-text-child-anchor-layout"),
306                      layout);  
307 }
308      
309 static void gtk_text_child_anchor_finalize (GObject *obj);
310
311 G_DEFINE_TYPE (GtkTextChildAnchor, gtk_text_child_anchor, G_TYPE_OBJECT)
312
313 static void
314 gtk_text_child_anchor_init (GtkTextChildAnchor *child_anchor)
315 {
316   child_anchor->segment = NULL;
317 }
318
319 static void
320 gtk_text_child_anchor_class_init (GtkTextChildAnchorClass *klass)
321 {
322   GObjectClass *object_class = G_OBJECT_CLASS (klass);
323
324   object_class->finalize = gtk_text_child_anchor_finalize;
325 }
326
327 /**
328  * gtk_text_child_anchor_new:
329  * 
330  * Creates a new #GtkTextChildAnchor. Usually you would then insert
331  * it into a #GtkTextBuffer with gtk_text_buffer_insert_child_anchor().
332  * To perform the creation and insertion in one step, use the
333  * convenience function gtk_text_buffer_create_child_anchor().
334  * 
335  * Return value: a new #GtkTextChildAnchor
336  **/
337 GtkTextChildAnchor*
338 gtk_text_child_anchor_new (void)
339 {
340   return g_object_new (GTK_TYPE_TEXT_CHILD_ANCHOR, NULL);
341 }
342
343 static void
344 gtk_text_child_anchor_finalize (GObject *obj)
345 {
346   GtkTextChildAnchor *anchor;
347   GSList *tmp_list;
348   GtkTextLineSegment *seg;
349   
350   anchor = GTK_TEXT_CHILD_ANCHOR (obj);
351
352   seg = anchor->segment;
353   
354   if (seg)
355     {
356       if (seg->body.child.tree != NULL)
357         {
358           g_warning ("Someone removed a reference to a GtkTextChildAnchor "
359                      "they didn't own; the anchor is still in the text buffer "
360                      "and the refcount is 0.");
361           return;
362         }
363       
364       tmp_list = seg->body.child.widgets;
365       while (tmp_list)
366         {
367           g_object_unref (tmp_list->data);
368           tmp_list = g_slist_next (tmp_list);
369         }
370   
371       g_slist_free (seg->body.child.widgets);
372   
373       g_free (seg);
374     }
375
376   anchor->segment = NULL;
377
378   G_OBJECT_CLASS (gtk_text_child_anchor_parent_class)->finalize (obj);
379 }
380
381 /**
382  * gtk_text_child_anchor_get_widgets:
383  * @anchor: a #GtkTextChildAnchor
384  * 
385  * Gets a list of all widgets anchored at this child anchor.
386  * The returned list should be freed with g_list_free().
387  *
388  *
389  * Return value: (element-type GtkWidget) (transfer container): list of widgets anchored at @anchor
390  **/
391 GList*
392 gtk_text_child_anchor_get_widgets (GtkTextChildAnchor *anchor)
393 {
394   GtkTextLineSegment *seg = anchor->segment;
395   GList *list = NULL;
396   GSList *iter;
397
398   CHECK_IN_BUFFER_RETURN (anchor, NULL);
399   
400   g_return_val_if_fail (seg->type == &gtk_text_child_type, NULL);
401
402   iter = seg->body.child.widgets;
403   while (iter != NULL)
404     {
405       list = g_list_prepend (list, iter->data);
406
407       iter = g_slist_next (iter);
408     }
409
410   /* Order is not relevant, so we don't need to reverse the list
411    * again.
412    */
413   return list;
414 }
415
416 /**
417  * gtk_text_child_anchor_get_deleted:
418  * @anchor: a #GtkTextChildAnchor
419  * 
420  * Determines whether a child anchor has been deleted from
421  * the buffer. Keep in mind that the child anchor will be
422  * unreferenced when removed from the buffer, so you need to
423  * hold your own reference (with g_object_ref()) if you plan
424  * to use this function &mdash; otherwise all deleted child anchors
425  * will also be finalized.
426  * 
427  * Return value: %TRUE if the child anchor has been deleted from its buffer
428  **/
429 gboolean
430 gtk_text_child_anchor_get_deleted (GtkTextChildAnchor *anchor)
431 {
432   GtkTextLineSegment *seg = anchor->segment;
433
434   CHECK_IN_BUFFER_RETURN (anchor, TRUE);
435   
436   g_return_val_if_fail (seg->type == &gtk_text_child_type, TRUE);
437
438   return seg->body.child.tree == NULL;
439 }
440
441 void
442 gtk_text_child_anchor_register_child (GtkTextChildAnchor *anchor,
443                                       GtkWidget          *child,
444                                       GtkTextLayout      *layout)
445 {
446   g_return_if_fail (GTK_IS_TEXT_CHILD_ANCHOR (anchor));
447   g_return_if_fail (GTK_IS_WIDGET (child));
448
449   CHECK_IN_BUFFER (anchor);
450   
451   _gtk_anchored_child_set_layout (child, layout);
452   
453   _gtk_widget_segment_add (anchor->segment, child);
454
455   gtk_text_child_anchor_queue_resize (anchor, layout);
456 }
457
458 void
459 gtk_text_child_anchor_unregister_child (GtkTextChildAnchor *anchor,
460                                         GtkWidget          *child)
461 {
462   g_return_if_fail (GTK_IS_TEXT_CHILD_ANCHOR (anchor));
463   g_return_if_fail (GTK_IS_WIDGET (child));
464
465   CHECK_IN_BUFFER (anchor);
466   
467   if (_gtk_anchored_child_get_layout (child))
468     {
469       gtk_text_child_anchor_queue_resize (anchor,
470                                           _gtk_anchored_child_get_layout (child));
471     }
472   
473   _gtk_anchored_child_set_layout (child, NULL);
474   
475   _gtk_widget_segment_remove (anchor->segment, child);
476 }
477
478 void
479 gtk_text_child_anchor_queue_resize (GtkTextChildAnchor *anchor,
480                                     GtkTextLayout      *layout)
481 {
482   GtkTextIter start;
483   GtkTextIter end;
484   GtkTextLineSegment *seg;
485   
486   g_return_if_fail (GTK_IS_TEXT_CHILD_ANCHOR (anchor));
487   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
488
489   CHECK_IN_BUFFER (anchor);
490   
491   seg = anchor->segment;
492
493   if (seg->body.child.tree == NULL)
494     return;
495   
496   gtk_text_buffer_get_iter_at_child_anchor (layout->buffer,
497                                             &start, anchor);
498   end = start;
499   gtk_text_iter_forward_char (&end);
500   
501   gtk_text_layout_invalidate (layout, &start, &end);
502 }
503
504 void
505 gtk_text_anchored_child_set_layout (GtkWidget     *child,
506                                     GtkTextLayout *layout)
507 {
508   g_return_if_fail (GTK_IS_WIDGET (child));
509   g_return_if_fail (layout == NULL || GTK_IS_TEXT_LAYOUT (layout));
510   
511   _gtk_anchored_child_set_layout (child, layout);
512 }