]> Pileus Git - ~andy/gtk/blob - gtk/gtklabel.c
da5ee65f0ef6a9c9d26ca015709e6a4363327138
[~andy/gtk] / gtk / gtklabel.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
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 #include <string.h>
20 #include "gtklabel.h"
21
22
23 enum {
24   ARG_0,
25   ARG_LABEL,
26   ARG_JUSTIFY
27 };
28
29 static void gtk_label_class_init   (GtkLabelClass  *klass);
30 static void gtk_label_init         (GtkLabel       *label);
31 static void gtk_label_set_arg      (GtkLabel       *label,
32                                     GtkArg         *arg,
33                                     guint           arg_id);
34 static void gtk_label_get_arg      (GtkLabel       *label,
35                                     GtkArg         *arg,
36                                     guint           arg_id);
37 static void gtk_label_finalize     (GtkObject      *object);
38 static void gtk_label_size_request (GtkWidget      *widget,
39                                     GtkRequisition *requisition);
40 static gint gtk_label_expose       (GtkWidget      *widget,
41                                     GdkEventExpose *event);
42 static void gtk_label_state_changed (GtkWidget      *widget,
43                                      guint           previous_state);
44 static void gtk_label_style_set     (GtkWidget      *widget,
45                                      GtkStyle       *previous_style);
46
47
48
49 static GtkMiscClass *parent_class = NULL;
50
51
52 GtkType
53 gtk_label_get_type (void)
54 {
55   static GtkType label_type = 0;
56   
57   if (!label_type)
58     {
59       GtkTypeInfo label_info =
60       {
61         "GtkLabel",
62         sizeof (GtkLabel),
63         sizeof (GtkLabelClass),
64         (GtkClassInitFunc) gtk_label_class_init,
65         (GtkObjectInitFunc) gtk_label_init,
66         (GtkArgSetFunc) gtk_label_set_arg,
67         (GtkArgGetFunc) gtk_label_get_arg,
68       };
69       
70       label_type = gtk_type_unique (gtk_misc_get_type (), &label_info);
71       gtk_type_set_chunk_alloc (label_type, 32);
72     }
73   
74   return label_type;
75 }
76
77 static void
78 gtk_label_class_init (GtkLabelClass *class)
79 {
80   GtkObjectClass *object_class;
81   GtkWidgetClass *widget_class;
82   
83   object_class = (GtkObjectClass*) class;
84   widget_class = (GtkWidgetClass*) class;
85   
86   parent_class = gtk_type_class (gtk_misc_get_type ());
87   
88   gtk_object_add_arg_type ("GtkLabel::label", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_LABEL);
89   gtk_object_add_arg_type ("GtkLabel::justify", GTK_TYPE_JUSTIFICATION, GTK_ARG_READWRITE, ARG_JUSTIFY);
90   
91   object_class->finalize = gtk_label_finalize;
92   
93   widget_class->size_request = gtk_label_size_request;
94   widget_class->expose_event = gtk_label_expose;
95   widget_class->style_set    = gtk_label_style_set;
96   widget_class->state_changed = gtk_label_state_changed;
97 }
98
99 static void
100 gtk_label_set_arg (GtkLabel       *label,
101                    GtkArg         *arg,
102                    guint           arg_id)
103 {
104   switch (arg_id)
105     {
106     case ARG_LABEL:
107       gtk_label_set (label, GTK_VALUE_STRING (*arg));
108       break;
109     case ARG_JUSTIFY:
110       gtk_label_set_justify (label, GTK_VALUE_ENUM (*arg));
111       break;
112     default:
113       break;
114     }
115 }
116
117 static void
118 gtk_label_get_arg (GtkLabel       *label,
119                    GtkArg         *arg,
120                    guint           arg_id)
121 {
122   switch (arg_id)
123     {
124     case ARG_LABEL:
125       GTK_VALUE_STRING (*arg) = g_strdup (label->label);
126       break;
127     case ARG_JUSTIFY:
128       GTK_VALUE_ENUM (*arg) = label->jtype;
129       break;
130     default:
131       arg->type = GTK_TYPE_INVALID;
132       break;
133     }
134 }
135
136 static void
137 gtk_label_init (GtkLabel *label)
138 {
139   GTK_WIDGET_SET_FLAGS (label, GTK_NO_WINDOW);
140   
141   label->label = NULL;
142   label->row = NULL;
143   label->max_width = 0;
144   label->jtype = GTK_JUSTIFY_CENTER;
145   label->needs_clear = FALSE;
146   
147   gtk_label_set (label, "");
148 }
149
150 GtkWidget*
151 gtk_label_new (const gchar *str)
152 {
153   GtkLabel *label;
154   
155   g_return_val_if_fail (str != NULL, NULL);
156   
157   label = gtk_type_new (gtk_label_get_type ());
158   
159   gtk_label_set (label, str);
160   
161   return GTK_WIDGET (label);
162 }
163
164 void
165 gtk_label_set (GtkLabel    *label,
166                const gchar *str)
167 {
168   char* p;
169   
170   g_return_if_fail (label != NULL);
171   g_return_if_fail (GTK_IS_LABEL (label));
172   g_return_if_fail (str != NULL);
173   
174   if (label->label)
175     g_free (label->label);
176   label->label = g_strdup (str);
177   
178   if (label->row)
179     g_slist_free (label->row);
180   label->row = NULL;
181   label->row = g_slist_append (label->row, label->label);
182   p = label->label;
183   while ((p = strchr(p, '\n')))
184     label->row = g_slist_append (label->row, ++p);
185   
186   if (GTK_WIDGET_VISIBLE (label))
187     {
188       if (GTK_WIDGET_MAPPED (label))
189         gdk_window_clear_area (GTK_WIDGET (label)->window,
190                                GTK_WIDGET (label)->allocation.x,
191                                GTK_WIDGET (label)->allocation.y,
192                                GTK_WIDGET (label)->allocation.width,
193                                GTK_WIDGET (label)->allocation.height);
194       
195       gtk_widget_queue_resize (GTK_WIDGET (label));
196     }
197 }
198
199 void
200 gtk_label_set_justify (GtkLabel        *label,
201                        GtkJustification jtype)
202 {
203   g_return_if_fail (label != NULL);
204   g_return_if_fail (GTK_IS_LABEL (label));
205   
206   if ((GtkJustification) label->jtype != jtype)
207     {
208       label->jtype = jtype;
209       
210       if (GTK_WIDGET_VISIBLE (label))
211         {
212           if (GTK_WIDGET_MAPPED (label))
213             gdk_window_clear_area (GTK_WIDGET (label)->window,
214                                    GTK_WIDGET (label)->allocation.x,
215                                    GTK_WIDGET (label)->allocation.y,
216                                    GTK_WIDGET (label)->allocation.width,
217                                    GTK_WIDGET (label)->allocation.height);
218           
219           gtk_widget_queue_resize (GTK_WIDGET (label));
220         }
221     }
222 }
223
224 void
225 gtk_label_get (GtkLabel  *label,
226                gchar    **str)
227 {
228   g_return_if_fail (label != NULL);
229   g_return_if_fail (GTK_IS_LABEL (label));
230   g_return_if_fail (str != NULL);
231   
232   *str = label->label;
233 }
234
235
236 static void
237 gtk_label_finalize (GtkObject *object)
238 {
239   GtkLabel *label;
240   
241   g_return_if_fail (object != NULL);
242   g_return_if_fail (GTK_IS_LABEL (object));
243   
244   label = GTK_LABEL (object);
245   
246   g_free (label->label);
247   g_slist_free (label->row);
248   
249   (* GTK_OBJECT_CLASS (parent_class)->finalize) (object);
250 }
251
252 static void
253 gtk_label_size_request (GtkWidget      *widget,
254                         GtkRequisition *requisition)
255 {
256   GtkLabel *label;
257   GSList *row;
258   gint width;
259   
260   g_return_if_fail (widget != NULL);
261   g_return_if_fail (GTK_IS_LABEL (widget));
262   g_return_if_fail (requisition != NULL);
263   
264   label = GTK_LABEL (widget);
265   
266   row = label->row;
267   width = 0;
268   while (row)
269     {
270       if (row->next)
271         width = MAX (width,
272                      gdk_text_width (GTK_WIDGET (label)->style->font,
273                                      row->data,
274                                      (gchar*) row->next->data - (gchar*) row->data - 1));
275       else
276         width = MAX (width, gdk_string_width (GTK_WIDGET (label)->style->font, row->data));
277       row = row->next;
278     }
279
280   label->max_width = width;
281   requisition->width = width + label->misc.xpad * 2;
282   requisition->height = ((GTK_WIDGET (label)->style->font->ascent +
283                           GTK_WIDGET (label)->style->font->descent + 2) *
284                          g_slist_length(label->row) +
285                          label->misc.ypad * 2);
286 }
287
288 static gint
289 gtk_label_expose (GtkWidget      *widget,
290                   GdkEventExpose *event)
291 {
292   GtkLabel *label;
293   GtkMisc *misc;
294   GSList *row;
295   gint state;
296   gint offset;
297   gint len;
298   gint x, y;
299   
300   g_return_val_if_fail (widget != NULL, FALSE);
301   g_return_val_if_fail (GTK_IS_LABEL (widget), FALSE);
302   g_return_val_if_fail (event != NULL, FALSE);
303   
304   if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget))
305     {
306       label = GTK_LABEL (widget);
307       misc = GTK_MISC (widget);
308       
309       state = widget->state;
310       
311       /*
312        * GC Clipping
313        */
314       gdk_gc_set_clip_rectangle (widget->style->white_gc, &event->area);
315       gdk_gc_set_clip_rectangle (widget->style->fg_gc[state], &event->area);
316       
317       /* We clear the whole allocation here so that if a partial
318        * expose is triggered we don't just clear part and mess up
319        * when the queued redraw comes along. (There will always
320        * be a complete queued redraw when the needs_clear flag
321        * is set.)
322        */
323       if (label->needs_clear)
324         {
325           gdk_window_clear_area (widget->window,
326                                  widget->allocation.x,
327                                  widget->allocation.y,
328                                  widget->allocation.width,
329                                  widget->allocation.height);
330           
331           label->needs_clear = FALSE;
332         }
333       
334       x = widget->allocation.x + misc->xpad +
335         (widget->allocation.width - (label->max_width + label->misc.xpad * 2))
336         * misc->xalign + 0.5;
337       
338       y = (widget->allocation.y * (1.0 - misc->yalign) +
339            (widget->allocation.y + widget->allocation.height -
340             (widget->requisition.height - misc->ypad * 2)) *
341            misc->yalign + widget->style->font->ascent) + 1.5;
342       
343       row = label->row;
344       while (row && row->next)
345         {
346           len = (gchar*) row->next->data - (gchar*) row->data - 1;
347           offset = 0;
348           
349           if (label->jtype == GTK_JUSTIFY_CENTER)
350             offset = (label->max_width - gdk_text_width (widget->style->font, row->data, len)) / 2;
351           
352           else if (label->jtype == GTK_JUSTIFY_RIGHT)
353             offset = (label->max_width - gdk_text_width (widget->style->font, row->data, len));
354           
355           if (state == GTK_STATE_INSENSITIVE)
356             gdk_draw_text (widget->window, widget->style->font,
357                            widget->style->white_gc,
358                            offset + x + 1, y + 1, row->data, len);
359           
360           gdk_draw_text (widget->window, widget->style->font,
361                          widget->style->fg_gc[state],
362                          offset + x, y, row->data, len);
363           row = row->next;
364           y += widget->style->font->ascent + widget->style->font->descent + 2;
365         }
366       
367       /* 
368        * COMMENT: we can avoid gdk_text_width() calls here storing in label->row
369        * the widths of the rows calculated in gtk_label_set.
370        * Once we have a wrapping interface we can support GTK_JUSTIFY_FILL.
371        */
372       offset = 0;
373       
374       if (label->jtype == GTK_JUSTIFY_CENTER)
375         offset = (label->max_width - gdk_string_width (widget->style->font, row->data)) / 2;
376       
377       else if (label->jtype == GTK_JUSTIFY_RIGHT)
378         offset = (label->max_width - gdk_string_width (widget->style->font, row->data));
379       
380       if (state == GTK_STATE_INSENSITIVE)
381         gdk_draw_string (widget->window, widget->style->font,
382                          widget->style->white_gc,
383                          offset + x + 1, y + 1, row->data);
384       
385       gdk_draw_string (widget->window, widget->style->font,
386                        widget->style->fg_gc[state],
387                        offset + x, y, row->data);
388       
389       gdk_gc_set_clip_mask (widget->style->white_gc, NULL);
390       gdk_gc_set_clip_mask (widget->style->fg_gc[state], NULL);
391       
392     }
393   return TRUE;
394 }
395
396 static void 
397 gtk_label_state_changed (GtkWidget      *widget,
398                          guint           previous_state)
399 {
400   if (GTK_WIDGET_DRAWABLE (widget))
401     GTK_LABEL (widget)->needs_clear = TRUE;
402 }
403
404 static void 
405 gtk_label_style_set (GtkWidget  *widget,
406                      GtkStyle   *previous_style)
407 {
408   if (GTK_WIDGET_DRAWABLE (widget))
409     GTK_LABEL (widget)->needs_clear = TRUE;
410 }
411