~ubuntu-branches/ubuntu/trusty/gnome-contacts/trusty

« back to all changes in this revision

Viewing changes to src/gtk-notification.c

  • Committer: Package Import Robot
  • Author(s): Michael Biebl, Jeremy Bicha, Michael Biebl
  • Date: 2013-09-19 18:23:06 UTC
  • mfrom: (1.3.10) (0.3.4 experimental)
  • mto: This revision was merged to the branch mainline in revision 40.
  • Revision ID: package-import@ubuntu.com-20130919182306-rcatwotzg94pr884
Tags: 3.8.3-1
[ Jeremy Bicha ]
* debian/control.in:
  - Drop alternate build-depends on valac-0.18 since it's no longer
    in Debian

[ Michael Biebl ]
* New upstream release.
* Loosen Build-Depends on libgnome-desktop-3-dev, we do not strictly require
  version (>= 3.6.0) which is not yet available in unstable.
* Bump Standards-Version to 3.9.4. No further changes.
* Upload to unstable.

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