~indicator-applet-developers/indicator-messages/trunk.13.10

« back to all changes in this revision

Viewing changes to src/ido-detail-label.c

  • Committer: Tarmac
  • Author(s): Lars Uebernickel, Ted Gould, sergio.schvezov at canonical, Sergio Schvezov, Ken VanDine, Renato Araujo Oliveira Filho, Ricardo Mendoza
  • Date: 2013-08-19 16:51:38 UTC
  • mfrom: (327.1.83 consolidate)
  • Revision ID: tarmac-20130819165138-rtvgstpdkoysfshf
Merge in phablet branch.

Approved by PS Jenkins bot, Pete Woods.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright 2012 Canonical Ltd.
3
 
 *
4
 
 * This program is free software: you can redistribute it and/or modify it
5
 
 * under the terms of the GNU General Public License version 3, as published
6
 
 * by the Free Software Foundation.
7
 
 *
8
 
 * This program is distributed in the hope that it will be useful, but
9
 
 * WITHOUT ANY WARRANTY; without even the implied warranties of
10
 
 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11
 
 * PURPOSE.  See the GNU General Public License for more details.
12
 
 *
13
 
 * You should have received a copy of the GNU General Public License along
14
 
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
15
 
 *
16
 
 * Authors:
17
 
 *     Lars Uebernickel <lars.uebernickel@canonical.com>
18
 
 */
19
 
 
20
 
#include "ido-detail-label.h"
21
 
 
22
 
#include <math.h>
23
 
 
24
 
G_DEFINE_TYPE (IdoDetailLabel, ido_detail_label, GTK_TYPE_WIDGET)
25
 
 
26
 
struct _IdoDetailLabelPrivate
27
 
{
28
 
  gchar *text;
29
 
  PangoLayout *layout;
30
 
  gboolean draw_lozenge;
31
 
};
32
 
 
33
 
enum
34
 
{
35
 
  PROP_0,
36
 
  PROP_TEXT,
37
 
  NUM_PROPERTIES
38
 
};
39
 
 
40
 
static GParamSpec *properties[NUM_PROPERTIES];
41
 
 
42
 
static void
43
 
ido_detail_label_get_property (GObject    *object,
44
 
                               guint       property_id,
45
 
                               GValue     *value,
46
 
                               GParamSpec *pspec)
47
 
{
48
 
  IdoDetailLabel *self = IDO_DETAIL_LABEL (object);
49
 
 
50
 
  switch (property_id)
51
 
    {
52
 
    case PROP_TEXT:
53
 
      g_value_set_string (value, self->priv->text);
54
 
      break;
55
 
 
56
 
    default:
57
 
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
58
 
    }
59
 
}
60
 
 
61
 
static void
62
 
ido_detail_label_set_property (GObject      *object,
63
 
                               guint         property_id,
64
 
                               const GValue *value,
65
 
                               GParamSpec   *pspec)
66
 
{
67
 
  IdoDetailLabel *self = IDO_DETAIL_LABEL (object);
68
 
 
69
 
  switch (property_id)
70
 
    {
71
 
    case PROP_TEXT:
72
 
      ido_detail_label_set_text (self, g_value_get_string (value));
73
 
      break;
74
 
 
75
 
    default:
76
 
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
77
 
    }
78
 
}
79
 
 
80
 
 
81
 
static void
82
 
ido_detail_label_finalize (GObject *object)
83
 
{
84
 
  IdoDetailLabelPrivate *priv = IDO_DETAIL_LABEL (object)->priv;
85
 
 
86
 
  g_free (priv->text);
87
 
 
88
 
  G_OBJECT_CLASS (ido_detail_label_parent_class)->finalize (object);
89
 
}
90
 
 
91
 
static void
92
 
ido_detail_label_dispose (GObject *object)
93
 
{
94
 
  IdoDetailLabelPrivate *priv = IDO_DETAIL_LABEL (object)->priv;
95
 
 
96
 
  g_clear_object (&priv->layout);
97
 
 
98
 
  G_OBJECT_CLASS (ido_detail_label_parent_class)->dispose (object);
99
 
}
100
 
 
101
 
static void
102
 
ido_detail_label_ensure_layout (IdoDetailLabel *label)
103
 
{
104
 
  IdoDetailLabelPrivate *priv = label->priv;
105
 
 
106
 
  if (priv->layout == NULL)
107
 
    {
108
 
      priv->layout = gtk_widget_create_pango_layout (GTK_WIDGET (label), priv->text);
109
 
      pango_layout_set_alignment (priv->layout, PANGO_ALIGN_CENTER);
110
 
      pango_layout_set_ellipsize (priv->layout, PANGO_ELLIPSIZE_END);
111
 
      pango_layout_set_height (priv->layout, -1);
112
 
 
113
 
      // TODO update layout on "style-updated" and "direction-changed"
114
 
    }
115
 
}
116
 
 
117
 
static void
118
 
cairo_lozenge (cairo_t *cr,
119
 
               double   x,
120
 
               double   y,
121
 
               double   w,
122
 
               double   h,
123
 
               double   radius)
124
 
{
125
 
  double x1 = x + w - radius;
126
 
  double x2 = x + radius;
127
 
  double y1 = y + radius;
128
 
  double y2 = y + h - radius;
129
 
 
130
 
  cairo_move_to (cr, x + radius, y);
131
 
  cairo_arc (cr, x1, y1, radius, G_PI * 1.5, G_PI * 2);
132
 
  cairo_arc (cr, x1, y2, radius, 0,          G_PI * 0.5);
133
 
  cairo_arc (cr, x2, y2, radius, G_PI * 0.5, G_PI);
134
 
  cairo_arc (cr, x2, y1, radius, G_PI,       G_PI * 1.5);
135
 
}
136
 
 
137
 
static PangoFontMetrics *
138
 
gtk_widget_get_font_metrics (GtkWidget    *widget,
139
 
                             PangoContext *context)
140
 
{
141
 
  PangoFontDescription *font;
142
 
  PangoFontMetrics *metrics;
143
 
 
144
 
  gtk_style_context_get (gtk_widget_get_style_context (widget),
145
 
                         gtk_widget_get_state_flags (widget), 
146
 
                         "font", &font, NULL);
147
 
 
148
 
  metrics = pango_context_get_metrics (context,
149
 
                                       font,
150
 
                                       pango_context_get_language (context));
151
 
 
152
 
  pango_font_description_free (font);
153
 
  return metrics;
154
 
}
155
 
 
156
 
static gint
157
 
ido_detail_label_get_minimum_text_width (IdoDetailLabel *label)
158
 
{
159
 
  IdoDetailLabelPrivate *priv = label->priv;
160
 
  PangoContext *context;
161
 
  PangoFontMetrics *metrics;
162
 
  gint char_width;
163
 
  gint w;
164
 
 
165
 
  context = pango_layout_get_context (priv->layout);
166
 
  metrics = gtk_widget_get_font_metrics (GTK_WIDGET (label), context);
167
 
  char_width = pango_font_metrics_get_approximate_digit_width (metrics);
168
 
 
169
 
  w = 2 * char_width / PANGO_SCALE;
170
 
  pango_font_metrics_unref (metrics);
171
 
  return w;
172
 
}
173
 
 
174
 
static gboolean
175
 
ido_detail_label_draw (GtkWidget *widget,
176
 
                       cairo_t   *cr)
177
 
{
178
 
  IdoDetailLabel *label = IDO_DETAIL_LABEL (widget);
179
 
  IdoDetailLabelPrivate *priv = IDO_DETAIL_LABEL (widget)->priv;
180
 
  PangoRectangle extents;
181
 
  GtkAllocation allocation;
182
 
  double x, w, h, radius;
183
 
  GdkRGBA color;
184
 
 
185
 
  if (!priv->text || !*priv->text)
186
 
    return TRUE;
187
 
 
188
 
  gtk_widget_get_allocation (widget, &allocation);
189
 
 
190
 
  ido_detail_label_ensure_layout (IDO_DETAIL_LABEL (widget));
191
 
 
192
 
  pango_layout_get_extents (priv->layout, NULL, &extents);
193
 
  pango_extents_to_pixels (&extents, NULL);
194
 
 
195
 
  h = MIN (allocation.height, extents.height);
196
 
  radius = floor (h / 2.0);
197
 
  w = MAX (ido_detail_label_get_minimum_text_width (label), extents.width) + 2.0 * radius;
198
 
  x = allocation.width - w;
199
 
 
200
 
  pango_layout_set_width (priv->layout, (allocation.width - 2 * radius) * PANGO_SCALE);
201
 
  pango_layout_get_extents (priv->layout, NULL, &extents);
202
 
  pango_extents_to_pixels (&extents, NULL);
203
 
 
204
 
  gtk_style_context_get_color (gtk_widget_get_style_context (widget),
205
 
                               gtk_widget_get_state_flags (widget),
206
 
                               &color);
207
 
  gdk_cairo_set_source_rgba (cr, &color);
208
 
 
209
 
  cairo_set_line_width (cr, 1.0);
210
 
  cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
211
 
 
212
 
  if (priv->draw_lozenge)
213
 
    cairo_lozenge (cr, x, 0.0, w, h, radius);
214
 
 
215
 
  cairo_move_to (cr, x + radius, (allocation.height - extents.height) / 2.0);
216
 
  pango_cairo_layout_path (cr, priv->layout);
217
 
  cairo_fill (cr);
218
 
 
219
 
  return TRUE;
220
 
}
221
 
 
222
 
static void
223
 
ido_detail_label_get_preferred_width (GtkWidget *widget,
224
 
                                      gint      *minimum,
225
 
                                      gint      *natural)
226
 
{
227
 
  IdoDetailLabelPrivate *priv = IDO_DETAIL_LABEL (widget)->priv;
228
 
  PangoRectangle extents;
229
 
  double radius;
230
 
 
231
 
  ido_detail_label_ensure_layout (IDO_DETAIL_LABEL (widget));
232
 
 
233
 
  pango_layout_get_extents (priv->layout, NULL, &extents);
234
 
  pango_extents_to_pixels (&extents, NULL);
235
 
 
236
 
  radius = floor (extents.height / 2.0);
237
 
 
238
 
  *minimum = ido_detail_label_get_minimum_text_width (IDO_DETAIL_LABEL (widget)) + 2.0 * radius;
239
 
  *natural = MAX (*minimum, extents.width + 2.0 * radius);
240
 
}
241
 
 
242
 
static void
243
 
ido_detail_label_get_preferred_height (GtkWidget *widget,
244
 
                                       gint      *minimum,
245
 
                                       gint      *natural)
246
 
{
247
 
  IdoDetailLabelPrivate *priv = IDO_DETAIL_LABEL (widget)->priv;
248
 
  PangoContext *context;
249
 
  PangoFontMetrics *metrics;
250
 
  PangoRectangle extents;
251
 
 
252
 
  ido_detail_label_ensure_layout (IDO_DETAIL_LABEL (widget));
253
 
 
254
 
  pango_layout_get_extents (priv->layout, NULL, &extents);
255
 
  pango_extents_to_pixels (&extents, NULL);
256
 
  context = pango_layout_get_context (priv->layout);
257
 
  metrics = gtk_widget_get_font_metrics (widget, context);
258
 
 
259
 
  *minimum = *natural = (pango_font_metrics_get_ascent (metrics) +
260
 
                         pango_font_metrics_get_descent (metrics)) / PANGO_SCALE;
261
 
 
262
 
  pango_font_metrics_unref (metrics);
263
 
}
264
 
 
265
 
static void
266
 
ido_detail_label_class_init (IdoDetailLabelClass *klass)
267
 
{
268
 
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
269
 
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
270
 
 
271
 
  object_class->get_property = ido_detail_label_get_property;
272
 
  object_class->set_property = ido_detail_label_set_property;
273
 
  object_class->finalize = ido_detail_label_finalize;
274
 
  object_class->dispose = ido_detail_label_dispose;
275
 
 
276
 
  widget_class->draw = ido_detail_label_draw;
277
 
  widget_class->get_preferred_width = ido_detail_label_get_preferred_width;
278
 
  widget_class->get_preferred_height = ido_detail_label_get_preferred_height;
279
 
 
280
 
  g_type_class_add_private (klass, sizeof (IdoDetailLabelPrivate));
281
 
 
282
 
  properties[PROP_TEXT] = g_param_spec_string ("text",
283
 
                                               "Text",
284
 
                                               "The text of the label",
285
 
                                               NULL,
286
 
                                               G_PARAM_READWRITE |
287
 
                                               G_PARAM_STATIC_STRINGS);
288
 
 
289
 
  g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
290
 
}
291
 
 
292
 
static void
293
 
ido_detail_label_init (IdoDetailLabel *self)
294
 
{
295
 
  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
296
 
                                            IDO_TYPE_DETAIL_LABEL,
297
 
                                            IdoDetailLabelPrivate);
298
 
 
299
 
  gtk_widget_set_has_window (GTK_WIDGET (self), FALSE);
300
 
}
301
 
 
302
 
GtkWidget *
303
 
ido_detail_label_new (const gchar *label)
304
 
{
305
 
  return g_object_new (IDO_TYPE_DETAIL_LABEL,
306
 
                       "text", label,
307
 
                       NULL);
308
 
}
309
 
 
310
 
const gchar *
311
 
ido_detail_label_get_text (IdoDetailLabel *label)
312
 
{
313
 
  g_return_val_if_fail (IDO_IS_DETAIL_LABEL (label), NULL);
314
 
  return label->priv->text;
315
 
}
316
 
 
317
 
/* collapse_whitespace:
318
 
 * @str: the source string
319
 
 *
320
 
 * Collapses all occurences of consecutive whitespace charactes in @str
321
 
 * into a single space.
322
 
 *
323
 
 * Returns: (transfer full): a newly-allocated string
324
 
 */
325
 
static gchar *
326
 
collapse_whitespace (const gchar *str)
327
 
{
328
 
  GString *result;
329
 
  gboolean in_space = FALSE;
330
 
 
331
 
  if (str == NULL)
332
 
    return NULL;
333
 
 
334
 
  result = g_string_new ("");
335
 
 
336
 
  while (*str)
337
 
    {
338
 
      gunichar c = g_utf8_get_char_validated (str, -1);
339
 
 
340
 
      if (c < 0)
341
 
        break;
342
 
 
343
 
      if (!g_unichar_isspace (c))
344
 
        {
345
 
          g_string_append_unichar (result, c);
346
 
          in_space = FALSE;
347
 
        }
348
 
      else if (!in_space)
349
 
        {
350
 
          g_string_append_c (result, ' ');
351
 
          in_space = TRUE;
352
 
        }
353
 
 
354
 
      str = g_utf8_next_char (str);
355
 
    }
356
 
 
357
 
  return g_string_free (result, FALSE);
358
 
}
359
 
 
360
 
static void
361
 
ido_detail_label_set_text_impl (IdoDetailLabel *label,
362
 
                                const gchar    *text,
363
 
                                gboolean        draw_lozenge)
364
 
{
365
 
  IdoDetailLabelPrivate * priv = label->priv;
366
 
 
367
 
  g_clear_object (&priv->layout);
368
 
  g_free (priv->text);
369
 
 
370
 
  priv->text = g_strdup (text);
371
 
  priv->draw_lozenge = draw_lozenge;
372
 
 
373
 
  g_object_notify_by_pspec (G_OBJECT (label), properties[PROP_TEXT]);
374
 
  gtk_widget_queue_resize (GTK_WIDGET (label));
375
 
}
376
 
 
377
 
void
378
 
ido_detail_label_set_text (IdoDetailLabel *label,
379
 
                           const gchar    *text)
380
 
{
381
 
  gchar *str;
382
 
 
383
 
  g_return_if_fail (IDO_IS_DETAIL_LABEL (label));
384
 
 
385
 
  str = collapse_whitespace (text);
386
 
  ido_detail_label_set_text_impl (label, str, FALSE);
387
 
  g_free (str);
388
 
}
389
 
 
390
 
void
391
 
ido_detail_label_set_count (IdoDetailLabel *label,
392
 
                            gint            count)
393
 
{
394
 
  gchar *text;
395
 
 
396
 
  g_return_if_fail (IDO_IS_DETAIL_LABEL (label));
397
 
 
398
 
  text = g_strdup_printf ("%d", count);
399
 
  ido_detail_label_set_text_impl (label, text, TRUE);
400
 
  g_free (text);
401
 
}