]> Pileus Git - ~andy/gtk/blob - gtk/gtktextchild.c
gtk/: fully remove gtkalias hacks
[~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   seg->byte_count = 3; /* We convert to the 0xFFFC "unknown character",
138                         * a 3-byte sequence in UTF-8
139                         */
140   seg->char_count = 1;
141
142   seg->body.pixbuf.pixbuf = pixbuf;
143
144   g_object_ref (pixbuf);
145
146   return seg;
147 }
148
149
150 static GtkTextLineSegment *
151 child_segment_cleanup_func (GtkTextLineSegment *seg,
152                             GtkTextLine        *line)
153 {
154   seg->body.child.line = line;
155
156   return seg;
157 }
158
159 static int
160 child_segment_delete_func (GtkTextLineSegment *seg,
161                            GtkTextLine       *line,
162                            gboolean           tree_gone)
163 {
164   GSList *tmp_list;
165   GSList *copy;
166
167   _gtk_text_btree_unregister_child_anchor (seg->body.child.obj);
168   
169   seg->body.child.tree = NULL;
170   seg->body.child.line = NULL;
171
172   /* avoid removing widgets while walking the list */
173   copy = g_slist_copy (seg->body.child.widgets);
174   tmp_list = copy;
175   while (tmp_list != NULL)
176     {
177       GtkWidget *child = tmp_list->data;
178
179       gtk_widget_destroy (child);
180       
181       tmp_list = g_slist_next (tmp_list);
182     }
183
184   /* On removal from the widget's parents (GtkTextView),
185    * the widget should have been removed from the anchor.
186    */
187   g_assert (seg->body.child.widgets == NULL);
188
189   g_slist_free (copy);
190   
191   _gtk_widget_segment_unref (seg);  
192   
193   return 0;
194 }
195
196 static void
197 child_segment_check_func (GtkTextLineSegment *seg,
198                           GtkTextLine        *line)
199 {
200   if (seg->next == NULL)
201     g_error ("child segment is the last segment in a line");
202
203   if (seg->byte_count != 3)
204     g_error ("child segment has byte count of %d", seg->byte_count);
205
206   if (seg->char_count != 1)
207     g_error ("child segment has char count of %d", seg->char_count);
208 }
209
210 const GtkTextLineSegmentClass gtk_text_child_type = {
211   "child-widget",                                        /* name */
212   FALSE,                                                 /* leftGravity */
213   NULL,                                                  /* splitFunc */
214   child_segment_delete_func,                             /* deleteFunc */
215   child_segment_cleanup_func,                            /* cleanupFunc */
216   NULL,                                                  /* lineChangeFunc */
217   child_segment_check_func                               /* checkFunc */
218 };
219
220 #define WIDGET_SEG_SIZE ((unsigned) (G_STRUCT_OFFSET (GtkTextLineSegment, body) \
221         + sizeof (GtkTextChildBody)))
222
223 GtkTextLineSegment *
224 _gtk_widget_segment_new (GtkTextChildAnchor *anchor)
225 {
226   GtkTextLineSegment *seg;
227
228   seg = g_malloc (WIDGET_SEG_SIZE);
229
230   seg->type = &gtk_text_child_type;
231
232   seg->next = NULL;
233
234   seg->byte_count = 3; /* We convert to the 0xFFFC "unknown character",
235                         * a 3-byte sequence in UTF-8
236                         */
237   seg->char_count = 1;
238
239   seg->body.child.obj = anchor;
240   seg->body.child.obj->segment = seg;
241   seg->body.child.widgets = NULL;
242   seg->body.child.tree = NULL;
243   seg->body.child.line = NULL;
244
245   g_object_ref (anchor);
246   
247   return seg;
248 }
249
250 void
251 _gtk_widget_segment_add    (GtkTextLineSegment *widget_segment,
252                             GtkWidget          *child)
253 {
254   g_return_if_fail (widget_segment->type == &gtk_text_child_type);
255   g_return_if_fail (widget_segment->body.child.tree != NULL);
256
257   g_object_ref (child);
258   
259   widget_segment->body.child.widgets =
260     g_slist_prepend (widget_segment->body.child.widgets,
261                      child);
262 }
263
264 void
265 _gtk_widget_segment_remove (GtkTextLineSegment *widget_segment,
266                             GtkWidget          *child)
267 {
268   g_return_if_fail (widget_segment->type == &gtk_text_child_type);
269   
270   widget_segment->body.child.widgets =
271     g_slist_remove (widget_segment->body.child.widgets,
272                     child);
273
274   g_object_unref (child);
275 }
276
277 void
278 _gtk_widget_segment_ref (GtkTextLineSegment *widget_segment)
279 {
280   g_assert (widget_segment->type == &gtk_text_child_type);
281
282   g_object_ref (widget_segment->body.child.obj);
283 }
284
285 void
286 _gtk_widget_segment_unref (GtkTextLineSegment *widget_segment)
287 {
288   g_assert (widget_segment->type == &gtk_text_child_type);
289
290   g_object_unref (widget_segment->body.child.obj);
291 }
292
293 GtkTextLayout*
294 _gtk_anchored_child_get_layout (GtkWidget *child)
295 {
296   return g_object_get_data (G_OBJECT (child), "gtk-text-child-anchor-layout");  
297 }
298
299 static void
300 _gtk_anchored_child_set_layout (GtkWidget     *child,
301                                 GtkTextLayout *layout)
302 {
303   g_object_set_data (G_OBJECT (child),
304                      I_("gtk-text-child-anchor-layout"),
305                      layout);  
306 }
307      
308 static void gtk_text_child_anchor_finalize (GObject *obj);
309
310 G_DEFINE_TYPE (GtkTextChildAnchor, gtk_text_child_anchor, G_TYPE_OBJECT)
311
312 static void
313 gtk_text_child_anchor_init (GtkTextChildAnchor *child_anchor)
314 {
315   child_anchor->segment = NULL;
316 }
317
318 static void
319 gtk_text_child_anchor_class_init (GtkTextChildAnchorClass *klass)
320 {
321   GObjectClass *object_class = G_OBJECT_CLASS (klass);
322
323   object_class->finalize = gtk_text_child_anchor_finalize;
324 }
325
326 /**
327  * gtk_text_child_anchor_new:
328  * 
329  * Creates a new #GtkTextChildAnchor. Usually you would then insert
330  * it into a #GtkTextBuffer with gtk_text_buffer_insert_child_anchor().
331  * To perform the creation and insertion in one step, use the
332  * convenience function gtk_text_buffer_create_child_anchor().
333  * 
334  * Return value: a new #GtkTextChildAnchor
335  **/
336 GtkTextChildAnchor*
337 gtk_text_child_anchor_new (void)
338 {
339   return g_object_new (GTK_TYPE_TEXT_CHILD_ANCHOR, NULL);
340 }
341
342 static void
343 gtk_text_child_anchor_finalize (GObject *obj)
344 {
345   GtkTextChildAnchor *anchor;
346   GSList *tmp_list;
347   GtkTextLineSegment *seg;
348   
349   anchor = GTK_TEXT_CHILD_ANCHOR (obj);
350
351   seg = anchor->segment;
352   
353   if (seg)
354     {
355       if (seg->body.child.tree != NULL)
356         {
357           g_warning ("Someone removed a reference to a GtkTextChildAnchor "
358                      "they didn't own; the anchor is still in the text buffer "
359                      "and the refcount is 0.");
360           return;
361         }
362       
363       tmp_list = seg->body.child.widgets;
364       while (tmp_list)
365         {
366           g_object_unref (tmp_list->data);
367           tmp_list = g_slist_next (tmp_list);
368         }
369   
370       g_slist_free (seg->body.child.widgets);
371   
372       g_free (seg);
373     }
374
375   anchor->segment = NULL;
376
377   G_OBJECT_CLASS (gtk_text_child_anchor_parent_class)->finalize (obj);
378 }
379
380 /**
381  * gtk_text_child_anchor_get_widgets:
382  * @anchor: a #GtkTextChildAnchor
383  * 
384  * Gets a list of all widgets anchored at this child anchor.
385  * The returned list should be freed with g_list_free().
386  *
387  *
388  * Return value: (element-type GtkWidget) (transfer container): list of widgets anchored at @anchor
389  **/
390 GList*
391 gtk_text_child_anchor_get_widgets (GtkTextChildAnchor *anchor)
392 {
393   GtkTextLineSegment *seg = anchor->segment;
394   GList *list = NULL;
395   GSList *iter;
396
397   CHECK_IN_BUFFER_RETURN (anchor, NULL);
398   
399   g_return_val_if_fail (seg->type == &gtk_text_child_type, NULL);
400
401   iter = seg->body.child.widgets;
402   while (iter != NULL)
403     {
404       list = g_list_prepend (list, iter->data);
405
406       iter = g_slist_next (iter);
407     }
408
409   /* Order is not relevant, so we don't need to reverse the list
410    * again.
411    */
412   return list;
413 }
414
415 /**
416  * gtk_text_child_anchor_get_deleted:
417  * @anchor: a #GtkTextChildAnchor
418  * 
419  * Determines whether a child anchor has been deleted from
420  * the buffer. Keep in mind that the child anchor will be
421  * unreferenced when removed from the buffer, so you need to
422  * hold your own reference (with g_object_ref()) if you plan
423  * to use this function &mdash; otherwise all deleted child anchors
424  * will also be finalized.
425  * 
426  * Return value: %TRUE if the child anchor has been deleted from its buffer
427  **/
428 gboolean
429 gtk_text_child_anchor_get_deleted (GtkTextChildAnchor *anchor)
430 {
431   GtkTextLineSegment *seg = anchor->segment;
432
433   CHECK_IN_BUFFER_RETURN (anchor, TRUE);
434   
435   g_return_val_if_fail (seg->type == &gtk_text_child_type, TRUE);
436
437   return seg->body.child.tree == NULL;
438 }
439
440 void
441 gtk_text_child_anchor_register_child (GtkTextChildAnchor *anchor,
442                                       GtkWidget          *child,
443                                       GtkTextLayout      *layout)
444 {
445   g_return_if_fail (GTK_IS_TEXT_CHILD_ANCHOR (anchor));
446   g_return_if_fail (GTK_IS_WIDGET (child));
447
448   CHECK_IN_BUFFER (anchor);
449   
450   _gtk_anchored_child_set_layout (child, layout);
451   
452   _gtk_widget_segment_add (anchor->segment, child);
453
454   gtk_text_child_anchor_queue_resize (anchor, layout);
455 }
456
457 void
458 gtk_text_child_anchor_unregister_child (GtkTextChildAnchor *anchor,
459                                         GtkWidget          *child)
460 {
461   g_return_if_fail (GTK_IS_TEXT_CHILD_ANCHOR (anchor));
462   g_return_if_fail (GTK_IS_WIDGET (child));
463
464   CHECK_IN_BUFFER (anchor);
465   
466   if (_gtk_anchored_child_get_layout (child))
467     {
468       gtk_text_child_anchor_queue_resize (anchor,
469                                           _gtk_anchored_child_get_layout (child));
470     }
471   
472   _gtk_anchored_child_set_layout (child, NULL);
473   
474   _gtk_widget_segment_remove (anchor->segment, child);
475 }
476
477 void
478 gtk_text_child_anchor_queue_resize (GtkTextChildAnchor *anchor,
479                                     GtkTextLayout      *layout)
480 {
481   GtkTextIter start;
482   GtkTextIter end;
483   GtkTextLineSegment *seg;
484   
485   g_return_if_fail (GTK_IS_TEXT_CHILD_ANCHOR (anchor));
486   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
487
488   CHECK_IN_BUFFER (anchor);
489   
490   seg = anchor->segment;
491
492   if (seg->body.child.tree == NULL)
493     return;
494   
495   gtk_text_buffer_get_iter_at_child_anchor (layout->buffer,
496                                             &start, anchor);
497   end = start;
498   gtk_text_iter_forward_char (&end);
499   
500   gtk_text_layout_invalidate (layout, &start, &end);
501 }
502
503 void
504 gtk_text_anchored_child_set_layout (GtkWidget     *child,
505                                     GtkTextLayout *layout)
506 {
507   g_return_if_fail (GTK_IS_WIDGET (child));
508   g_return_if_fail (layout == NULL || GTK_IS_TEXT_LAYOUT (layout));
509   
510   _gtk_anchored_child_set_layout (child, layout);
511 }