~noskcaj/ubuntu/trusty/gnome-documents/3.10.2

« back to all changes in this revision

Viewing changes to src/lib/gd-notification.c

  • Committer: Package Import Robot
  • Author(s): Robert Ancell
  • Date: 2012-08-22 10:01:18 UTC
  • mfrom: (1.1.8)
  • Revision ID: package-import@ubuntu.com-20120822100118-3837rqfy72e1op72
Tags: 3.5.90-0ubuntu1
New upstream release

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
 
/*
3
 
 * gd-notification
4
 
 * Based on gtk-notification from gnome-contacts:
5
 
 * http://git.gnome.org/browse/gnome-contacts/tree/src/gtk-notification.c?id=3.3.91
6
 
 *
7
 
 * Copyright (C) Erick Pérez Castellanos 2011 <erick.red@gmail.com>
8
 
 * Copyright (C) 2012 Red Hat, Inc.
9
 
 *
10
 
 * This program is free software: you can redistribute it and/or modify it
11
 
 * under the terms of the GNU Lesser General Public License as published
12
 
 * by the Free Software Foundation, either version 3 of the License, or
13
 
 * (at your option) any later version.
14
 
 *
15
 
 * This program is distributed in the hope that it will be useful, but
16
 
 * WITHOUT ANY WARRANTY; without even the implied warranty of
17
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18
 
 * See the GNU Lesser General Public License for more details.
19
 
 *
20
 
 * You should have received a copy of the GNU Lesser General Public License
21
 
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.";
22
 
 */
23
 
 
24
 
#include "gd-notification.h"
25
 
 
26
 
/**
27
 
 * SECTION:gdnotification
28
 
 * @short_description: Report notification messages to the user
29
 
 * @include: gtk/gtk.h
30
 
 * @see_also: #GtkStatusbar, #GtkMessageDialog, #GtkInfoBar
31
 
 *
32
 
 * #GdNotification is a widget made for showing notifications to
33
 
 * the user, allowing them to close the notification or wait for it
34
 
 * to time out.
35
 
 *
36
 
 * #GdNotification provides one signal (#GdNotification::dismissed), for when the notification
37
 
 * times out or is closed.
38
 
 *
39
 
 */
40
 
 
41
 
#define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
42
 
#define SHADOW_OFFSET_X 2
43
 
#define SHADOW_OFFSET_Y 3
44
 
#define ANIMATION_TIME 200 /* msec */
45
 
#define ANIMATION_STEP 40 /* msec */
46
 
 
47
 
enum {
48
 
  PROP_0,
49
 
  PROP_TIMEOUT,
50
 
  PROP_SHOW_CLOSE_BUTTON
51
 
};
52
 
 
53
 
struct _GdNotificationPrivate {
54
 
  GtkWidget *close_button;
55
 
  gboolean show_close_button;
56
 
 
57
 
  GdkWindow *bin_window;
58
 
 
59
 
  int animate_y; /* from 0 to allocation.height */
60
 
  gboolean waiting_for_viewable;
61
 
  gboolean revealed;
62
 
  gboolean dismissed;
63
 
  gboolean sent_dismissed;
64
 
  guint animate_timeout;
65
 
 
66
 
  gint timeout;
67
 
  guint timeout_source_id;
68
 
};
69
 
 
70
 
enum {
71
 
  DISMISSED,
72
 
  LAST_SIGNAL
73
 
};
74
 
 
75
 
static guint notification_signals[LAST_SIGNAL] = { 0 };
76
 
 
77
 
static gboolean gd_notification_draw                           (GtkWidget       *widget,
78
 
                                                                 cairo_t         *cr);
79
 
static void     gd_notification_get_preferred_width            (GtkWidget       *widget,
80
 
                                                                 gint            *minimum_size,
81
 
                                                                 gint            *natural_size);
82
 
static void     gd_notification_get_preferred_height_for_width (GtkWidget       *widget,
83
 
                                                                 gint             width,
84
 
                                                                 gint            *minimum_height,
85
 
                                                                 gint            *natural_height);
86
 
static void     gd_notification_get_preferred_height           (GtkWidget       *widget,
87
 
                                                                 gint            *minimum_size,
88
 
                                                                 gint            *natural_size);
89
 
static void     gd_notification_get_preferred_width_for_height (GtkWidget       *widget,
90
 
                                                                 gint             height,
91
 
                                                                 gint            *minimum_width,
92
 
                                                                 gint            *natural_width);
93
 
static void     gd_notification_size_allocate                  (GtkWidget       *widget,
94
 
                                                                 GtkAllocation   *allocation);
95
 
static gboolean gd_notification_timeout_cb                     (gpointer         user_data);
96
 
static void     gd_notification_show                           (GtkWidget       *widget);
97
 
static void     gd_notification_add                            (GtkContainer    *container,
98
 
                                                                 GtkWidget       *child);
99
 
 
100
 
/* signals handlers */
101
 
static void     gd_notification_close_button_clicked_cb        (GtkWidget       *widget,
102
 
                                                                 gpointer         user_data);
103
 
 
104
 
G_DEFINE_TYPE(GdNotification, gd_notification, GTK_TYPE_BIN);
105
 
 
106
 
static void
107
 
gd_notification_init (GdNotification *notification)
108
 
{
109
 
  GtkWidget *close_button_image;
110
 
  GtkStyleContext *context;
111
 
  GdNotificationPrivate *priv;
112
 
 
113
 
  context = gtk_widget_get_style_context (GTK_WIDGET (notification));
114
 
  gtk_style_context_add_class (context, "documents-notification");
115
 
 
116
 
  gtk_widget_set_halign (GTK_WIDGET (notification), GTK_ALIGN_CENTER);
117
 
  gtk_widget_set_valign (GTK_WIDGET (notification), GTK_ALIGN_START);
118
 
 
119
 
  gtk_widget_set_has_window (GTK_WIDGET (notification), TRUE);
120
 
 
121
 
  gtk_widget_push_composite_child ();
122
 
 
123
 
  priv = notification->priv =
124
 
    G_TYPE_INSTANCE_GET_PRIVATE (notification,
125
 
                                 GD_TYPE_NOTIFICATION,
126
 
                                 GdNotificationPrivate);
127
 
 
128
 
  priv->animate_y = 0;
129
 
  priv->close_button = gtk_button_new ();
130
 
  gtk_widget_set_parent (priv->close_button, GTK_WIDGET (notification));
131
 
  gtk_widget_show (priv->close_button);
132
 
  g_object_set (priv->close_button,
133
 
                "relief", GTK_RELIEF_NONE,
134
 
                "focus-on-click", FALSE,
135
 
                NULL);
136
 
  g_signal_connect (priv->close_button,
137
 
                    "clicked",
138
 
                    G_CALLBACK (gd_notification_close_button_clicked_cb),
139
 
                    notification);
140
 
  close_button_image = gtk_image_new_from_icon_name ("window-close-symbolic", GTK_ICON_SIZE_BUTTON);
141
 
  gtk_button_set_image (GTK_BUTTON (notification->priv->close_button), close_button_image);
142
 
 
143
 
  gtk_widget_pop_composite_child ();
144
 
 
145
 
  priv->timeout_source_id = 0;
146
 
}
147
 
 
148
 
static void
149
 
gd_notification_finalize (GObject *object)
150
 
{
151
 
  GdNotification *notification;
152
 
  GdNotificationPrivate *priv;
153
 
 
154
 
  g_return_if_fail (GTK_IS_NOTIFICATION (object));
155
 
 
156
 
  notification = GD_NOTIFICATION (object);
157
 
  priv = notification->priv;
158
 
 
159
 
  if (priv->animate_timeout != 0)
160
 
    g_source_remove (priv->animate_timeout);
161
 
 
162
 
  if (priv->timeout_source_id != 0)
163
 
    g_source_remove (priv->timeout_source_id);
164
 
 
165
 
  G_OBJECT_CLASS (gd_notification_parent_class)->finalize (object);
166
 
}
167
 
 
168
 
static void
169
 
gd_notification_destroy (GtkWidget *widget)
170
 
{
171
 
  GdNotification *notification = GD_NOTIFICATION (widget);
172
 
  GdNotificationPrivate *priv = notification->priv;
173
 
 
174
 
  if (!priv->sent_dismissed)
175
 
    {
176
 
      g_signal_emit (notification, notification_signals[DISMISSED], 0);
177
 
      priv->sent_dismissed = TRUE;
178
 
    }
179
 
 
180
 
  if (priv->close_button)
181
 
    {
182
 
      gtk_widget_unparent (priv->close_button);
183
 
      priv->close_button = NULL;
184
 
    }
185
 
 
186
 
  GTK_WIDGET_CLASS (gd_notification_parent_class)->destroy (widget);
187
 
}
188
 
 
189
 
static void
190
 
gd_notification_realize (GtkWidget *widget)
191
 
{
192
 
  GdNotification *notification = GD_NOTIFICATION (widget);
193
 
  GdNotificationPrivate *priv = notification->priv;
194
 
  GtkBin *bin = GTK_BIN (widget);
195
 
  GtkAllocation allocation;
196
 
  GtkWidget *child;
197
 
  GdkWindow *window;
198
 
  GdkWindowAttr attributes;
199
 
  gint attributes_mask;
200
 
 
201
 
  gtk_widget_set_realized (widget, TRUE);
202
 
 
203
 
  gtk_widget_get_allocation (widget, &allocation);
204
 
 
205
 
  attributes.x = allocation.x;
206
 
  attributes.y = allocation.y;
207
 
  attributes.width = allocation.width;
208
 
  attributes.height = allocation.height;
209
 
  attributes.window_type = GDK_WINDOW_CHILD;
210
 
  attributes.wclass = GDK_INPUT_OUTPUT;
211
 
  attributes.visual = gtk_widget_get_visual (widget);
212
 
 
213
 
  attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK;
214
 
 
215
 
  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
216
 
 
217
 
  window = gdk_window_new (gtk_widget_get_parent_window (widget),
218
 
                           &attributes, attributes_mask);
219
 
  gtk_widget_set_window (widget, window);
220
 
  gdk_window_set_user_data (window, notification);
221
 
 
222
 
  attributes.x = 0;
223
 
  attributes.y = attributes.height + priv->animate_y;
224
 
  attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK | GDK_VISIBILITY_NOTIFY_MASK;
225
 
 
226
 
  priv->bin_window = gdk_window_new (window, &attributes, attributes_mask);
227
 
  gdk_window_set_user_data (priv->bin_window, notification);
228
 
 
229
 
  child = gtk_bin_get_child (bin);
230
 
  if (child)
231
 
    gtk_widget_set_parent_window (child, priv->bin_window);
232
 
  gtk_widget_set_parent_window (priv->close_button, priv->bin_window);
233
 
 
234
 
  gdk_window_show (priv->bin_window);
235
 
}
236
 
 
237
 
static void
238
 
gd_notification_unrealize (GtkWidget *widget)
239
 
{
240
 
  GdNotification *notification = GD_NOTIFICATION (widget);
241
 
  GdNotificationPrivate *priv = notification->priv;
242
 
 
243
 
  gdk_window_set_user_data (priv->bin_window, NULL);
244
 
  gdk_window_destroy (priv->bin_window);
245
 
  priv->bin_window = NULL;
246
 
 
247
 
  GTK_WIDGET_CLASS (gd_notification_parent_class)->unrealize (widget);
248
 
}
249
 
 
250
 
static int
251
 
animation_target (GdNotification *notification)
252
 
{
253
 
  GdNotificationPrivate *priv = notification->priv;
254
 
  GtkAllocation allocation;
255
 
 
256
 
  if (priv->revealed) {
257
 
    gtk_widget_get_allocation (GTK_WIDGET (notification), &allocation);
258
 
    return allocation.height;
259
 
  } else {
260
 
    return 0;
261
 
  }
262
 
}
263
 
 
264
 
static gboolean
265
 
animation_timeout_cb (gpointer user_data)
266
 
{
267
 
  GdNotification *notification = GD_NOTIFICATION (user_data);
268
 
  GdNotificationPrivate *priv = notification->priv;
269
 
  GtkAllocation allocation;
270
 
  int target, delta;
271
 
 
272
 
  target = animation_target (notification);
273
 
 
274
 
  if (priv->animate_y != target) {
275
 
    gtk_widget_get_allocation (GTK_WIDGET (notification), &allocation);
276
 
 
277
 
    delta = allocation.height * ANIMATION_STEP / ANIMATION_TIME;
278
 
 
279
 
    if (priv->revealed)
280
 
      priv->animate_y += delta;
281
 
    else
282
 
      priv->animate_y -= delta;
283
 
 
284
 
    priv->animate_y = CLAMP (priv->animate_y, 0, allocation.height);
285
 
 
286
 
    if (priv->bin_window != NULL)
287
 
      gdk_window_move (priv->bin_window,
288
 
                       0,
289
 
                       -allocation.height + priv->animate_y);
290
 
    return TRUE;
291
 
  }
292
 
 
293
 
  if (priv->dismissed && priv->animate_y == 0)
294
 
    gtk_widget_destroy (GTK_WIDGET (notification));
295
 
 
296
 
  priv->animate_timeout = 0;
297
 
  return FALSE;
298
 
}
299
 
 
300
 
static void
301
 
start_animation (GdNotification *notification)
302
 
{
303
 
  GdNotificationPrivate *priv = notification->priv;
304
 
  int target;
305
 
 
306
 
  if (priv->animate_timeout != 0)
307
 
    return; /* Already running */
308
 
 
309
 
  target = animation_target (notification);
310
 
  if (priv->animate_y != target)
311
 
    notification->priv->animate_timeout =
312
 
      gdk_threads_add_timeout (ANIMATION_STEP,
313
 
                               animation_timeout_cb,
314
 
                               notification);
315
 
}
316
 
 
317
 
static void
318
 
gd_notification_show (GtkWidget *widget)
319
 
{
320
 
  GdNotification *notification = GD_NOTIFICATION (widget);
321
 
  GdNotificationPrivate *priv = notification->priv;
322
 
 
323
 
  GTK_WIDGET_CLASS (gd_notification_parent_class)->show (widget);
324
 
  priv->revealed = TRUE;
325
 
  priv->waiting_for_viewable = TRUE;
326
 
}
327
 
 
328
 
static void
329
 
gd_notification_hide (GtkWidget *widget)
330
 
{
331
 
  GdNotification *notification = GD_NOTIFICATION (widget);
332
 
  GdNotificationPrivate *priv = notification->priv;
333
 
 
334
 
  GTK_WIDGET_CLASS (gd_notification_parent_class)->hide (widget);
335
 
  priv->revealed = FALSE;
336
 
  priv->waiting_for_viewable = FALSE;
337
 
}
338
 
 
339
 
static void
340
 
gd_notification_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
341
 
{
342
 
  GdNotification *notification = GD_NOTIFICATION (object);
343
 
 
344
 
  g_return_if_fail (GTK_IS_NOTIFICATION (object));
345
 
 
346
 
  switch (prop_id) {
347
 
  case PROP_TIMEOUT:
348
 
    gd_notification_set_timeout (notification,
349
 
                                 g_value_get_int (value));
350
 
    break;
351
 
  case PROP_SHOW_CLOSE_BUTTON:
352
 
    gd_notification_set_show_close_button (notification,
353
 
                                           g_value_get_boolean (value));
354
 
    break;
355
 
  default:
356
 
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
357
 
    break;
358
 
  }
359
 
}
360
 
 
361
 
static void
362
 
gd_notification_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
363
 
{
364
 
  g_return_if_fail (GTK_IS_NOTIFICATION (object));
365
 
  GdNotification *notification = GD_NOTIFICATION (object);
366
 
 
367
 
  switch (prop_id) {
368
 
  case PROP_TIMEOUT:
369
 
    g_value_set_int (value, notification->priv->timeout);
370
 
    break;
371
 
  case PROP_SHOW_CLOSE_BUTTON:
372
 
    g_value_set_boolean (value,
373
 
                         notification->priv->show_close_button);
374
 
    break;
375
 
  default:
376
 
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
377
 
    break;
378
 
  }
379
 
}
380
 
 
381
 
static void
382
 
gd_notification_forall (GtkContainer *container,
383
 
                         gboolean      include_internals,
384
 
                         GtkCallback   callback,
385
 
                         gpointer      callback_data)
386
 
{
387
 
  GtkBin *bin = GTK_BIN (container);
388
 
  GdNotification *notification = GD_NOTIFICATION (container);
389
 
  GdNotificationPrivate *priv = notification->priv;
390
 
  GtkWidget *child;
391
 
 
392
 
  child = gtk_bin_get_child (bin);
393
 
  if (child)
394
 
    (* callback) (child, callback_data);
395
 
 
396
 
  if (include_internals)
397
 
    (* callback) (priv->close_button, callback_data);
398
 
}
399
 
 
400
 
static gboolean
401
 
gd_notification_visibility_notify_event (GtkWidget          *widget,
402
 
                                          GdkEventVisibility  *event)
403
 
{
404
 
  GdNotification *notification = GD_NOTIFICATION (widget);
405
 
  GdNotificationPrivate *priv = notification->priv;
406
 
 
407
 
  if (!gtk_widget_get_visible (widget))
408
 
    return FALSE;
409
 
 
410
 
  if (priv->waiting_for_viewable)
411
 
    {
412
 
      start_animation (notification);
413
 
      priv->waiting_for_viewable = FALSE;
414
 
    }
415
 
 
416
 
  if (notification->priv->timeout_source_id == 0 &&
417
 
      notification->priv->timeout != -1)
418
 
    notification->priv->timeout_source_id =
419
 
      gdk_threads_add_timeout (notification->priv->timeout * 1000,
420
 
                               gd_notification_timeout_cb,
421
 
                               widget);
422
 
 
423
 
  return FALSE;
424
 
}
425
 
 
426
 
static void
427
 
gd_notification_class_init (GdNotificationClass *klass)
428
 
{
429
 
  GObjectClass* object_class = G_OBJECT_CLASS (klass);
430
 
  GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
431
 
  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
432
 
 
433
 
  object_class->finalize = gd_notification_finalize;
434
 
  object_class->set_property = gd_notification_set_property;
435
 
  object_class->get_property = gd_notification_get_property;
436
 
 
437
 
  widget_class->show = gd_notification_show;
438
 
  widget_class->hide = gd_notification_hide;
439
 
  widget_class->destroy = gd_notification_destroy;
440
 
  widget_class->get_preferred_width = gd_notification_get_preferred_width;
441
 
  widget_class->get_preferred_height_for_width = gd_notification_get_preferred_height_for_width;
442
 
  widget_class->get_preferred_height = gd_notification_get_preferred_height;
443
 
  widget_class->get_preferred_width_for_height = gd_notification_get_preferred_width_for_height;
444
 
  widget_class->size_allocate = gd_notification_size_allocate;
445
 
  widget_class->draw = gd_notification_draw;
446
 
  widget_class->realize = gd_notification_realize;
447
 
  widget_class->unrealize = gd_notification_unrealize;
448
 
  widget_class->visibility_notify_event = gd_notification_visibility_notify_event;
449
 
 
450
 
  container_class->add = gd_notification_add;
451
 
  container_class->forall = gd_notification_forall;
452
 
  gtk_container_class_handle_border_width (container_class);
453
 
 
454
 
 
455
 
  /**
456
 
   * GdNotification:timeout:
457
 
   *
458
 
   * The time it takes to hide the widget, in seconds.
459
 
   *
460
 
   * Since: 0.1
461
 
   */
462
 
  g_object_class_install_property (object_class,
463
 
                                   PROP_TIMEOUT,
464
 
                                   g_param_spec_int("timeout", "timeout",
465
 
                                                    "The time it takes to hide the widget, in seconds",
466
 
                                                    -1, G_MAXINT, -1,
467
 
                                                    GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
468
 
  g_object_class_install_property (object_class,
469
 
                                   PROP_SHOW_CLOSE_BUTTON,
470
 
                                   g_param_spec_boolean("show-close-button", "show-close-button",
471
 
                                                        "Whether to show a stock close button that dismisses the notification",
472
 
                                                        TRUE,
473
 
                                                        GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
474
 
 
475
 
  notification_signals[DISMISSED] = g_signal_new ("dismissed",
476
 
                                                  G_OBJECT_CLASS_TYPE (klass),
477
 
                                                  G_SIGNAL_RUN_LAST,
478
 
                                                  G_STRUCT_OFFSET (GdNotificationClass, dismissed),
479
 
                                                  NULL,
480
 
                                                  NULL,
481
 
                                                  g_cclosure_marshal_VOID__VOID,
482
 
                                                  G_TYPE_NONE,
483
 
                                                  0);
484
 
 
485
 
  g_type_class_add_private (object_class, sizeof (GdNotificationPrivate));
486
 
}
487
 
 
488
 
static void
489
 
draw_shadow_box (cairo_t *cr, GdkRectangle rect, int left_border, int right_border,
490
 
                 int bottom_border, double inner_alpha)
491
 
{
492
 
  cairo_pattern_t *pattern;
493
 
  cairo_matrix_t matrix;
494
 
  double x0, x1, x2, x3;
495
 
  double y0, y2, y3;
496
 
 
497
 
  cairo_save (cr);
498
 
 
499
 
  x0 = rect.x;
500
 
  x1 = rect.x + left_border;
501
 
  x2 = rect.x + rect.width - right_border;
502
 
  x3 = rect.x + rect.width;
503
 
 
504
 
  y0 = rect.y;
505
 
  y2 = rect.y + rect.height - bottom_border;
506
 
  y3 = rect.y + rect.height;
507
 
 
508
 
  /* Bottom border */
509
 
 
510
 
  pattern = cairo_pattern_create_linear(0, y2, 0, y3);
511
 
 
512
 
  cairo_pattern_add_color_stop_rgba(pattern, 0.0, 0, 0, 0, inner_alpha);
513
 
  cairo_pattern_add_color_stop_rgba(pattern, 1.0, 0, 0, 0, 0.0);
514
 
 
515
 
  cairo_set_source(cr, pattern);
516
 
  cairo_pattern_destroy(pattern);
517
 
 
518
 
  cairo_rectangle(cr, x1, y2, x2 - x1, y3 - y2);
519
 
  cairo_fill(cr);
520
 
 
521
 
  /* Left border */
522
 
 
523
 
  pattern = cairo_pattern_create_linear(x0, 0, x1, 0);
524
 
 
525
 
  cairo_pattern_add_color_stop_rgba(pattern, 0.0, 0, 0, 0, 0.0);
526
 
  cairo_pattern_add_color_stop_rgba(pattern, 1.0, 0, 0, 0, inner_alpha);
527
 
 
528
 
  cairo_set_source(cr, pattern);
529
 
  cairo_pattern_destroy(pattern);
530
 
 
531
 
  cairo_rectangle(cr, x0, y0, x1 - x0, y2 - y0);
532
 
  cairo_fill(cr);
533
 
 
534
 
  /* Right border */
535
 
 
536
 
  pattern = cairo_pattern_create_linear(x2, 0, x3, 0);
537
 
 
538
 
  cairo_pattern_add_color_stop_rgba(pattern, 0.0, 0, 0, 0, inner_alpha);
539
 
  cairo_pattern_add_color_stop_rgba(pattern, 1.0, 0, 0, 0, 0.0);
540
 
 
541
 
  cairo_set_source(cr, pattern);
542
 
  cairo_pattern_destroy(pattern);
543
 
 
544
 
  cairo_rectangle(cr, x2, y0, x3 - x2, y2 - y0);
545
 
  cairo_fill(cr);
546
 
 
547
 
  /* SW corner */
548
 
 
549
 
  pattern = cairo_pattern_create_radial(0, 0, 0, 0.0, 0, 1.0);
550
 
  cairo_pattern_add_color_stop_rgba(pattern, 0.0, 0, 0, 0, inner_alpha);
551
 
  cairo_pattern_add_color_stop_rgba(pattern, 1.0, 0, 0, 0, 0.0);
552
 
 
553
 
  cairo_matrix_init_scale (&matrix, 1.0 / left_border, 1.0 / bottom_border);
554
 
  cairo_matrix_translate (&matrix, - x1, -y2);
555
 
  cairo_pattern_set_matrix (pattern, &matrix);
556
 
 
557
 
  cairo_set_source(cr, pattern);
558
 
  cairo_pattern_destroy(pattern);
559
 
 
560
 
  cairo_rectangle(cr, x0, y2, x1 - x0, y3 - y2);
561
 
  cairo_fill(cr);
562
 
 
563
 
  /* SE corner */
564
 
 
565
 
  pattern = cairo_pattern_create_radial(0, 0, 0, 0, 0, 1.0);
566
 
  cairo_pattern_add_color_stop_rgba(pattern, 0.0, 0.0, 0, 0, inner_alpha);
567
 
  cairo_pattern_add_color_stop_rgba(pattern, 1.0, 0.0, 0, 0, 0.0);
568
 
 
569
 
  cairo_matrix_init_scale (&matrix, 1.0 / left_border, 1.0 / bottom_border);
570
 
  cairo_matrix_translate (&matrix, - x2, -y2);
571
 
  cairo_pattern_set_matrix (pattern, &matrix);
572
 
 
573
 
  cairo_set_source(cr, pattern);
574
 
  cairo_pattern_destroy(pattern);
575
 
 
576
 
  cairo_rectangle(cr, x2, y2, x3 - x2, y3 - y2);
577
 
  cairo_fill(cr);
578
 
 
579
 
  cairo_restore (cr);
580
 
}
581
 
 
582
 
static void
583
 
get_padding_and_border (GdNotification *notification,
584
 
                        GtkBorder *border)
585
 
{
586
 
  GtkStyleContext *context;
587
 
  GtkStateFlags state;
588
 
  GtkBorder tmp;
589
 
 
590
 
  context = gtk_widget_get_style_context (GTK_WIDGET (notification));
591
 
  state = gtk_widget_get_state_flags (GTK_WIDGET (notification));
592
 
 
593
 
  gtk_style_context_get_padding (context, state, border);
594
 
 
595
 
  gtk_style_context_get_border (context, state, &tmp);
596
 
  border->top += tmp.top;
597
 
  border->right += tmp.right;
598
 
  border->bottom += tmp.bottom;
599
 
  border->left += tmp.left;
600
 
}
601
 
 
602
 
static gboolean
603
 
gd_notification_draw (GtkWidget *widget, cairo_t *cr)
604
 
{
605
 
  GdNotification *notification = GD_NOTIFICATION (widget);
606
 
  GdNotificationPrivate *priv = notification->priv;
607
 
  GtkStyleContext *context;
608
 
  GdkRectangle rect;
609
 
  int inner_radius;
610
 
 
611
 
  if (gtk_cairo_should_draw_window (cr, priv->bin_window))
612
 
    {
613
 
      gtk_widget_get_allocation (widget, &rect);
614
 
 
615
 
      context = gtk_widget_get_style_context(widget);
616
 
 
617
 
      inner_radius = 5;
618
 
      draw_shadow_box (cr, rect, SHADOW_OFFSET_X + inner_radius, SHADOW_OFFSET_X + inner_radius,
619
 
                       SHADOW_OFFSET_Y + inner_radius, 0.8);
620
 
 
621
 
      gtk_style_context_save (context);
622
 
      gtk_render_background (context,  cr,
623
 
                             SHADOW_OFFSET_X, 0,
624
 
                             gtk_widget_get_allocated_width (widget) - 2 *SHADOW_OFFSET_X,
625
 
                             gtk_widget_get_allocated_height (widget) - SHADOW_OFFSET_Y);
626
 
      gtk_render_frame (context,cr,
627
 
                        SHADOW_OFFSET_X, 0,
628
 
                        gtk_widget_get_allocated_width (widget) - 2 *SHADOW_OFFSET_X,
629
 
                        gtk_widget_get_allocated_height (widget) - SHADOW_OFFSET_Y);
630
 
 
631
 
      gtk_style_context_restore (context);
632
 
 
633
 
      if (GTK_WIDGET_CLASS (gd_notification_parent_class)->draw)
634
 
        GTK_WIDGET_CLASS (gd_notification_parent_class)->draw(widget, cr);
635
 
    }
636
 
 
637
 
  return FALSE;
638
 
}
639
 
 
640
 
static void
641
 
gd_notification_add (GtkContainer *container,
642
 
                      GtkWidget    *child)
643
 
{
644
 
  GtkBin *bin = GTK_BIN (container);
645
 
  GdNotification *notification = GD_NOTIFICATION (bin);
646
 
  GdNotificationPrivate *priv = notification->priv;
647
 
 
648
 
  g_return_if_fail (gtk_bin_get_child (bin) == NULL);
649
 
 
650
 
  gtk_widget_set_parent_window (child, priv->bin_window);
651
 
 
652
 
  GTK_CONTAINER_CLASS (gd_notification_parent_class)->add (container, child);
653
 
}
654
 
 
655
 
 
656
 
static void
657
 
gd_notification_get_preferred_width (GtkWidget *widget, gint *minimum_size, gint *natural_size)
658
 
{
659
 
  GdNotification *notification = GD_NOTIFICATION (widget);
660
 
  GdNotificationPrivate *priv = notification->priv;
661
 
  GtkBin *bin = GTK_BIN (widget);
662
 
  gint child_min, child_nat;
663
 
  GtkWidget *child;
664
 
  GtkBorder padding;
665
 
  gint minimum, natural;
666
 
 
667
 
  get_padding_and_border (notification, &padding);
668
 
 
669
 
  minimum = 0;
670
 
  natural = 0;
671
 
 
672
 
  child = gtk_bin_get_child (bin);
673
 
  if (child && gtk_widget_get_visible (child))
674
 
    {
675
 
      gtk_widget_get_preferred_width (child,
676
 
                                      &child_min, &child_nat);
677
 
      minimum += child_min;
678
 
      natural += child_nat;
679
 
    }
680
 
 
681
 
  if (priv->show_close_button)
682
 
    {
683
 
      gtk_widget_get_preferred_width (priv->close_button,
684
 
                                      &child_min, &child_nat);
685
 
      minimum += child_min;
686
 
      natural += child_nat;
687
 
    }
688
 
 
689
 
  minimum += padding.left + padding.right + 2 * SHADOW_OFFSET_X;
690
 
  natural += padding.left + padding.right + 2 * SHADOW_OFFSET_X;
691
 
 
692
 
 if (minimum_size)
693
 
    *minimum_size = minimum;
694
 
 
695
 
  if (natural_size)
696
 
    *natural_size = natural;
697
 
}
698
 
 
699
 
static void
700
 
gd_notification_get_preferred_width_for_height (GtkWidget *widget,
701
 
                                                 gint height,
702
 
                                                 gint *minimum_width,
703
 
                                                 gint *natural_width)
704
 
{
705
 
  GdNotification *notification = GD_NOTIFICATION (widget);
706
 
  GdNotificationPrivate *priv = notification->priv;
707
 
  GtkBin *bin = GTK_BIN (widget);
708
 
  gint child_min, child_nat, child_height;
709
 
  GtkWidget *child;
710
 
  GtkBorder padding;
711
 
  gint minimum, natural;
712
 
 
713
 
  get_padding_and_border (notification, &padding);
714
 
 
715
 
  minimum = 0;
716
 
  natural = 0;
717
 
 
718
 
  child_height = height - SHADOW_OFFSET_Y - padding.top - padding.bottom;
719
 
 
720
 
  child = gtk_bin_get_child (bin);
721
 
  if (child && gtk_widget_get_visible (child))
722
 
    {
723
 
      gtk_widget_get_preferred_width_for_height (child, child_height,
724
 
                                                 &child_min, &child_nat);
725
 
      minimum += child_min;
726
 
      natural += child_nat;
727
 
    }
728
 
 
729
 
  if (priv->show_close_button)
730
 
    {
731
 
      gtk_widget_get_preferred_width_for_height (priv->close_button, child_height,
732
 
                                                 &child_min, &child_nat);
733
 
      minimum += child_min;
734
 
      natural += child_nat;
735
 
    }
736
 
 
737
 
  minimum += padding.left + padding.right + 2 * SHADOW_OFFSET_X;
738
 
  natural += padding.left + padding.right + 2 * SHADOW_OFFSET_X;
739
 
 
740
 
 if (minimum_width)
741
 
    *minimum_width = minimum;
742
 
 
743
 
  if (natural_width)
744
 
    *natural_width = natural;
745
 
}
746
 
 
747
 
static void
748
 
gd_notification_get_preferred_height_for_width (GtkWidget *widget,
749
 
                                                 gint width,
750
 
                                                 gint *minimum_height,
751
 
                                                 gint *natural_height)
752
 
{
753
 
  GdNotification *notification = GD_NOTIFICATION (widget);
754
 
  GdNotificationPrivate *priv = notification->priv;
755
 
  GtkBin *bin = GTK_BIN (widget);
756
 
  gint child_min, child_nat, child_width, button_width = 0;
757
 
  GtkWidget *child;
758
 
  GtkBorder padding;
759
 
  gint minimum = 0, natural = 0;
760
 
 
761
 
  get_padding_and_border (notification, &padding);
762
 
 
763
 
  if (priv->show_close_button)
764
 
    {
765
 
      gtk_widget_get_preferred_height (priv->close_button,
766
 
                                       &minimum, &natural);
767
 
      gtk_widget_get_preferred_width (priv->close_button,
768
 
                                      NULL, &button_width);
769
 
    }
770
 
 
771
 
  child = gtk_bin_get_child (bin);
772
 
  if (child && gtk_widget_get_visible (child))
773
 
    {
774
 
      child_width = width - button_width -
775
 
        2 * SHADOW_OFFSET_X - padding.left - padding.right;
776
 
 
777
 
      gtk_widget_get_preferred_height_for_width (child, child_width,
778
 
                                                 &child_min, &child_nat);
779
 
      minimum = MAX (minimum, child_min);
780
 
      natural = MAX (natural, child_nat);
781
 
    }
782
 
 
783
 
  minimum += padding.top + padding.bottom + SHADOW_OFFSET_Y;
784
 
  natural += padding.top + padding.bottom + SHADOW_OFFSET_Y;
785
 
 
786
 
 if (minimum_height)
787
 
    *minimum_height = minimum;
788
 
 
789
 
  if (natural_height)
790
 
    *natural_height = natural;
791
 
}
792
 
 
793
 
static void
794
 
gd_notification_get_preferred_height (GtkWidget *widget, 
795
 
                                      gint *minimum_height, 
796
 
                                      gint *natural_height)
797
 
{
798
 
  gint width;
799
 
 
800
 
  gd_notification_get_preferred_width (widget, &width, NULL);
801
 
  gd_notification_get_preferred_height_for_width (widget, width,
802
 
                                                  minimum_height, natural_height);
803
 
}
804
 
 
805
 
static void
806
 
gd_notification_size_allocate (GtkWidget *widget,
807
 
                                GtkAllocation *allocation)
808
 
{
809
 
  GdNotification *notification = GD_NOTIFICATION (widget);
810
 
  GdNotificationPrivate *priv = notification->priv;
811
 
  GtkBin *bin = GTK_BIN (widget);
812
 
  GtkAllocation child_allocation;
813
 
  GtkBorder padding;
814
 
  GtkRequisition button_req;
815
 
  GtkWidget *child;
816
 
 
817
 
  gtk_widget_set_allocation (widget, allocation);
818
 
 
819
 
  /* If somehow the notification changes while not hidden
820
 
     and we're not animating, immediately follow the resize */
821
 
  if (priv->animate_y > 0 &&
822
 
      !priv->animate_timeout)
823
 
    priv->animate_y = allocation->height;
824
 
 
825
 
  get_padding_and_border (notification, &padding);
826
 
 
827
 
  if (gtk_widget_get_realized (widget))
828
 
    {
829
 
      gdk_window_move_resize (gtk_widget_get_window (widget),
830
 
                              allocation->x,
831
 
                              allocation->y,
832
 
                              allocation->width,
833
 
                              allocation->height);
834
 
      gdk_window_move_resize (priv->bin_window,
835
 
                              0,
836
 
                              -allocation->height + priv->animate_y,
837
 
                              allocation->width,
838
 
                              allocation->height);
839
 
    }
840
 
 
841
 
  child_allocation.x = SHADOW_OFFSET_X + padding.left;
842
 
  child_allocation.y = padding.top;
843
 
 
844
 
  if (priv->show_close_button)
845
 
    gtk_widget_get_preferred_size (priv->close_button, &button_req, NULL);
846
 
  else
847
 
    button_req.width = button_req.height = 0;
848
 
 
849
 
  child_allocation.height = MAX (1, allocation->height - SHADOW_OFFSET_Y - padding.top - padding.bottom);
850
 
  child_allocation.width = MAX (1, (allocation->width - button_req.width -
851
 
                                    2 * SHADOW_OFFSET_X - padding.left - padding.right));
852
 
 
853
 
  child = gtk_bin_get_child (bin);
854
 
  if (child && gtk_widget_get_visible (child))
855
 
    gtk_widget_size_allocate (child, &child_allocation);
856
 
 
857
 
  if (priv->show_close_button)
858
 
    {
859
 
      child_allocation.x += child_allocation.width;
860
 
      child_allocation.width = button_req.width;
861
 
      child_allocation.y += (child_allocation.height - button_req.height) / 2;
862
 
      child_allocation.height = button_req.height;
863
 
 
864
 
      gtk_widget_size_allocate (priv->close_button, &child_allocation);
865
 
    }
866
 
}
867
 
 
868
 
static gboolean
869
 
gd_notification_timeout_cb (gpointer user_data)
870
 
{
871
 
  GdNotification *notification = GD_NOTIFICATION (user_data);
872
 
 
873
 
  gd_notification_dismiss (notification);
874
 
 
875
 
  return FALSE;
876
 
}
877
 
 
878
 
void
879
 
gd_notification_set_timeout (GdNotification *notification,
880
 
                             gint            timeout_sec)
881
 
{
882
 
  GdNotificationPrivate *priv = notification->priv;
883
 
 
884
 
  priv->timeout = timeout_sec;
885
 
  g_object_notify (G_OBJECT (notification), "timeout");
886
 
}
887
 
 
888
 
void
889
 
gd_notification_set_show_close_button (GdNotification *notification,
890
 
                                       gboolean show_close_button)
891
 
{
892
 
  GdNotificationPrivate *priv = notification->priv;
893
 
 
894
 
  priv->show_close_button = show_close_button;
895
 
 
896
 
  gtk_widget_set_visible (priv->close_button, show_close_button);
897
 
  gtk_widget_queue_resize (GTK_WIDGET (notification));
898
 
}
899
 
 
900
 
void
901
 
gd_notification_dismiss (GdNotification *notification)
902
 
{
903
 
  GdNotificationPrivate *priv = notification->priv;
904
 
 
905
 
  if (notification->priv->timeout_source_id)
906
 
    {
907
 
      g_source_remove (notification->priv->timeout_source_id);
908
 
      notification->priv->timeout_source_id = 0;
909
 
    }
910
 
 
911
 
  priv->dismissed = TRUE;
912
 
  priv->revealed = FALSE;
913
 
  start_animation (notification);
914
 
}
915
 
 
916
 
static void
917
 
gd_notification_close_button_clicked_cb (GtkWidget *widget, gpointer user_data)
918
 
{
919
 
  GdNotification *notification = GD_NOTIFICATION(user_data);
920
 
 
921
 
  gd_notification_dismiss (notification);
922
 
}
923
 
 
924
 
GtkWidget *
925
 
gd_notification_new (void)
926
 
{
927
 
  return g_object_new (GD_TYPE_NOTIFICATION, NULL);
928
 
}