~canonical-dx-team/ido/trunk

35 by Cody Russell
IdoMessageDialog - a morphing message dialog.
1
/*
2
 * Copyright (C) 2010 Canonical, Ltd.
3
 *
41 by Cody Russell
Fix license
4
 * This program is free software: you can redistribute it and/or modify it
5
 * under the terms of either or both of the following licenses:
6
 *
7
 * 1) the GNU Lesser General Public License version 3, as published by the
8
 * Free Software Foundation; and/or
9
 * 2) the GNU Lesser General Public License version 2.1, as published by
10
 * the Free Software Foundation.
11
 *
12
 * This program is distributed in the hope that it will be useful, but
13
 * WITHOUT ANY WARRANTY; without even the implied warranties of
14
 * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
15
 * PURPOSE.  See the applicable version of the GNU Lesser General Public
16
 * License for more details.
17
 *
18
 * You should have received a copy of both the GNU Lesser General Public
19
 * License version 3 and version 2.1 along with this program.  If not, see
20
 * <http://www.gnu.org/licenses/>
35 by Cody Russell
IdoMessageDialog - a morphing message dialog.
21
 *
22
 * Authors:
23
 *    Cody Russell <crussell@canonical.com>
24
 *
25
 * Design and specification:
26
 *    Matthew Paul Thomas <mpt@canonical.com>
27
 */
28
29
#include <string.h>
30
31
#include <gtk/gtk.h>
32
33
#include "idomessagedialog.h"
34
#include "idotimeline.h"
97.1.1 by Ken VanDine
removed deprecations from gtk3 and fixed sizing issues with idemessagedialog (LP: #888392)
35
#include "config.h"
35 by Cody Russell
IdoMessageDialog - a morphing message dialog.
36
37
#define IDO_MESSAGE_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), IDO_TYPE_MESSAGE_DIALOG, IdoMessageDialogPrivate))
38
39
static GtkWidget *ido_message_dialog_get_secondary_label (IdoMessageDialog *dialog);
40
static GtkWidget *ido_message_dialog_get_primary_label   (IdoMessageDialog *dialog);
41
42
typedef struct _IdoMessageDialogPrivate      IdoMessageDialogPrivate;
43
typedef struct _IdoMessageDialogMorphContext IdoMessageDialogMorphContext;
44
45
struct _IdoMessageDialogPrivate
46
{
47
  GtkWidget   *action_area;
48
  GtkWidget   *primary_label;
49
  GtkWidget   *secondary_label;
50
51
  gboolean     expanded;
52
};
53
54
struct _IdoMessageDialogMorphContext
55
{
56
  GtkWidget   *widget;
57
  IdoTimeline *timeline;
58
59
  GtkRequisition start;
60
  GtkRequisition end;
61
};
62
63
G_DEFINE_TYPE (IdoMessageDialog, ido_message_dialog, GTK_TYPE_MESSAGE_DIALOG)
64
65
static void
66
ido_message_dialog_map (GtkWidget *widget)
67
{
68
  IdoMessageDialog *dialog = IDO_MESSAGE_DIALOG (widget);
69
  IdoMessageDialogPrivate *priv = IDO_MESSAGE_DIALOG_GET_PRIVATE (dialog);
70
71
  GTK_WIDGET_CLASS (ido_message_dialog_parent_class)->map (widget);
72
73
  priv->primary_label = ido_message_dialog_get_primary_label (dialog);
74
  priv->secondary_label = ido_message_dialog_get_secondary_label (dialog);
75
76
  gtk_widget_hide (priv->secondary_label);
77
78
  gtk_label_set_selectable (GTK_LABEL (priv->primary_label), FALSE);
79
  gtk_label_set_selectable (GTK_LABEL (priv->secondary_label), FALSE);
80
81
  /* XXX: We really want to use gtk_window_set_deletable (GTK_WINDOW (widget), FALSE)
82
   *      here, but due to a bug in compiz this is more compatible.
83
   *
84
   * See: https://bugs.launchpad.net/ubuntu/+source/compiz/+bug/240794
85
   */
77.1.1 by Ken VanDine
porting to gtk3, WIP
86
  gdk_window_set_functions (gtk_widget_get_window (widget),
35 by Cody Russell
IdoMessageDialog - a morphing message dialog.
87
                            GDK_FUNC_RESIZE | GDK_FUNC_MOVE);
88
89
  ido_message_dialog_get_secondary_label (IDO_MESSAGE_DIALOG (widget));
90
}
91
92
static IdoMessageDialogMorphContext *
93
ido_message_dialog_morph_context_new (GtkWidget      *widget,
94
                                      IdoTimeline    *timeline,
95
                                      gpointer        identifier,
96
                                      GtkRequisition *start,
97
                                      GtkRequisition *end)
98
{
99
  IdoMessageDialogMorphContext *context;
100
101
  context = g_slice_new (IdoMessageDialogMorphContext);
102
103
  context->widget   = widget;
104
  context->timeline = timeline;
105
  context->start    = *start;
106
  context->end      = *end;
107
108
  return context;
109
}
110
111
static void
112
ido_message_dialog_morph_context_free (IdoMessageDialogMorphContext *context)
113
{
114
  g_object_unref (context->timeline);
115
116
  g_slice_free (IdoMessageDialogMorphContext, context);
117
}
118
119
static void
120
timeline_frame_cb (IdoTimeline *timeline,
121
                   gdouble      progress,
122
                   gpointer     user_data)
123
{
124
  IdoMessageDialogMorphContext *context = user_data;
125
  GtkRequisition start = context->start;
126
  GtkRequisition end = context->end;
127
  gint width_diff;
128
  gint height_diff;
129
  gint width, height;
130
131
  width_diff  = (MAX(start.width,  end.width)  - MIN(start.width,  end.width)) * progress;
132
  height_diff = (MAX(start.height, end.height) - MIN(start.height, end.height)) * progress;
133
134
  gtk_window_get_size (GTK_WINDOW (context->widget),
135
                       &width,
136
                       &height);
137
138
  gtk_widget_set_size_request (context->widget,
139
                               width_diff ? start.width + width_diff : -1,
140
                               height_diff ? start.height + height_diff : -1);
141
}
142
143
static void
144
timeline_finished_cb (IdoTimeline *timeline,
145
                      gpointer     user_data)
146
{
147
  IdoMessageDialogMorphContext *context = user_data;
148
  IdoMessageDialogPrivate *priv = IDO_MESSAGE_DIALOG_GET_PRIVATE (context->widget);
149
150
  gtk_widget_show (priv->action_area);
151
  gtk_widget_show (priv->secondary_label);
152
153
  ido_message_dialog_morph_context_free (context);
154
}
155
156
static gboolean
37 by Cody Russell
Start the morph on focus-in-event rather than button-press-event.
157
ido_message_dialog_focus_in_event (GtkWidget     *widget,
158
                                   GdkEventFocus *event)
35 by Cody Russell
IdoMessageDialog - a morphing message dialog.
159
{
37 by Cody Russell
Start the morph on focus-in-event rather than button-press-event.
160
  IdoMessageDialog *dialog = IDO_MESSAGE_DIALOG (widget);
35 by Cody Russell
IdoMessageDialog - a morphing message dialog.
161
  IdoMessageDialogPrivate *priv = IDO_MESSAGE_DIALOG_GET_PRIVATE (dialog);
162
163
  if (!priv->expanded)
164
    {
165
      GtkRequisition start;
166
      GtkRequisition end;
167
      IdoTimeline *timeline;
168
      IdoMessageDialogMorphContext *context;
169
97.1.1 by Ken VanDine
removed deprecations from gtk3 and fixed sizing issues with idemessagedialog (LP: #888392)
170
#ifdef USE_GTK3
171
      gtk_widget_get_preferred_size (GTK_WIDGET (dialog), NULL, &start);
172
#else
77.1.1 by Ken VanDine
porting to gtk3, WIP
173
      gtk_widget_get_requisition (GTK_WIDGET (dialog), &start);
97.1.1 by Ken VanDine
removed deprecations from gtk3 and fixed sizing issues with idemessagedialog (LP: #888392)
174
#endif
35 by Cody Russell
IdoMessageDialog - a morphing message dialog.
175
176
      priv->expanded = TRUE;
177
178
      gtk_widget_show (priv->action_area);
179
      gtk_widget_show (priv->secondary_label);
180
97.1.1 by Ken VanDine
removed deprecations from gtk3 and fixed sizing issues with idemessagedialog (LP: #888392)
181
#ifdef USE_GTK3
182
      gtk_widget_get_preferred_size (GTK_WIDGET (dialog), NULL, &end);
183
#else
77.1.1 by Ken VanDine
porting to gtk3, WIP
184
      gtk_widget_get_requisition (GTK_WIDGET (dialog), &end);
97.1.1 by Ken VanDine
removed deprecations from gtk3 and fixed sizing issues with idemessagedialog (LP: #888392)
185
#endif
35 by Cody Russell
IdoMessageDialog - a morphing message dialog.
186
187
      gtk_widget_hide (priv->action_area);
188
      gtk_widget_hide (priv->secondary_label);
189
190
      timeline = ido_timeline_new (500);
191
      context = ido_message_dialog_morph_context_new (GTK_WIDGET (dialog),
192
                                                      timeline,
193
                                                      "foo",
194
                                                      &start,
195
                                                      &end);
196
      g_signal_connect (timeline,
197
                        "frame",
198
                        G_CALLBACK (timeline_frame_cb),
199
                        context);
200
      g_signal_connect (timeline,
201
                        "finished",
202
                        G_CALLBACK (timeline_finished_cb),
203
                        context);
204
205
      ido_timeline_start (timeline);
206
    }
207
208
  return FALSE;
209
}
210
211
static void
212
ido_message_dialog_constructed (GObject *object)
213
{
214
  IdoMessageDialogPrivate *priv = IDO_MESSAGE_DIALOG_GET_PRIVATE (object);
215
  GtkWidget *vbox;
216
  GtkWidget *event_box;
217
218
  event_box = gtk_event_box_new ();
219
  gtk_widget_show (event_box);
220
77.1.1 by Ken VanDine
porting to gtk3, WIP
221
  vbox = gtk_dialog_get_content_area (GTK_DIALOG (object));
35 by Cody Russell
IdoMessageDialog - a morphing message dialog.
222
  priv->action_area = gtk_dialog_get_action_area (GTK_DIALOG (object));
223
224
  g_object_ref (G_OBJECT (vbox));
225
  gtk_container_remove (GTK_CONTAINER (object), vbox);
226
  gtk_container_add (GTK_CONTAINER (event_box), vbox);
227
  gtk_container_add (GTK_CONTAINER (object), event_box);
228
229
  gtk_widget_hide (priv->action_area);
230
}
231
232
static void
233
ido_message_dialog_class_init (IdoMessageDialogClass *class)
234
{
235
  GObjectClass *object_class = G_OBJECT_CLASS (class);
236
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
237
37 by Cody Russell
Start the morph on focus-in-event rather than button-press-event.
238
  object_class->constructed    = ido_message_dialog_constructed;
35 by Cody Russell
IdoMessageDialog - a morphing message dialog.
239
37 by Cody Russell
Start the morph on focus-in-event rather than button-press-event.
240
  widget_class->map            = ido_message_dialog_map;
241
  widget_class->focus_in_event = ido_message_dialog_focus_in_event;
35 by Cody Russell
IdoMessageDialog - a morphing message dialog.
242
243
  g_type_class_add_private (object_class, sizeof (IdoMessageDialogPrivate));
244
}
245
246
static void
247
ido_message_dialog_init (IdoMessageDialog *dialog)
248
{
36 by Cody Russell
Set focus-on-map to FALSE.
249
  gtk_window_set_focus_on_map (GTK_WINDOW (dialog), FALSE);
35 by Cody Russell
IdoMessageDialog - a morphing message dialog.
250
}
251
252
/**
253
 * ido_message_dialog_new:
254
 * @parent: transient parent, or %NULL for none
255
 * @flags: flags
256
 * @type: type of message
257
 * @buttons: a set of buttons to use
258
 * @message_format: printf()-style format string, or %NULL
259
 * @Varargs: arguments for @message_format
260
 *
261
 * Creates a new message dialog, which is based upon
262
 * GtkMessageDialog so it shares API and functionality
263
 * with it.  IdoMessageDialog differs in that it has two
264
 * states.  The initial state hides the action buttons
265
 * and the secondary message.  When a user clicks on the
266
 * dialog it will expand to provide the secondary message
267
 * and the action buttons.
268
 *
269
 * Return value: a new #IdoMessageDialog
270
 **/
271
GtkWidget*
272
ido_message_dialog_new (GtkWindow      *parent,
273
                        GtkDialogFlags  flags,
274
                        GtkMessageType  type,
275
                        GtkButtonsType  buttons,
276
                        const gchar    *message_format,
277
                        ...)
278
{
279
  GtkWidget *widget;
280
  GtkDialog *dialog;
281
  gchar* msg = NULL;
282
  va_list args;
283
284
  g_return_val_if_fail (parent == NULL || GTK_IS_WINDOW (parent), NULL);
285
286
  widget = g_object_new (IDO_TYPE_MESSAGE_DIALOG,
287
                         "message-type", type,
288
                         "buttons", buttons,
289
                         NULL);
290
  dialog = GTK_DIALOG (widget);
291
77.1.2 by Michael Terry
allow building either gtk2 or gtk3 versions; some deprecation cleanups
292
#if ! GTK_CHECK_VERSION(3, 0, 0)
35 by Cody Russell
IdoMessageDialog - a morphing message dialog.
293
  if (flags & GTK_DIALOG_NO_SEPARATOR)
294
    {
295
      g_warning ("The GTK_DIALOG_NO_SEPARATOR flag cannot be used for IdoMessageDialog");
296
      flags &= ~GTK_DIALOG_NO_SEPARATOR;
297
    }
77.1.2 by Michael Terry
allow building either gtk2 or gtk3 versions; some deprecation cleanups
298
#endif
35 by Cody Russell
IdoMessageDialog - a morphing message dialog.
299
300
  if (message_format)
301
    {
302
      va_start (args, message_format);
303
      msg = g_strdup_vprintf (message_format, args);
304
      va_end (args);
305
77.1.2 by Michael Terry
allow building either gtk2 or gtk3 versions; some deprecation cleanups
306
      g_object_set (G_OBJECT (widget), "text", msg, NULL);
35 by Cody Russell
IdoMessageDialog - a morphing message dialog.
307
308
      g_free (msg);
309
    }
310
311
  if (parent != NULL)
312
    gtk_window_set_transient_for (GTK_WINDOW (widget),
313
                                  GTK_WINDOW (parent));
314
315
  if (flags & GTK_DIALOG_MODAL)
316
    gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
317
318
  if (flags & GTK_DIALOG_DESTROY_WITH_PARENT)
319
    gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
320
321
  return widget;
322
}
323
324
GtkWidget*
325
ido_message_dialog_new_with_markup (GtkWindow      *parent,
326
                                    GtkDialogFlags  flags,
327
                                    GtkMessageType  type,
328
                                    GtkButtonsType  buttons,
329
                                    const gchar    *message_format,
330
                                    ...)
331
{
332
  GtkWidget *widget;
333
  va_list args;
334
  gchar *msg = NULL;
335
336
  g_return_val_if_fail (parent == NULL || GTK_IS_WINDOW (parent), NULL);
337
338
  widget = ido_message_dialog_new (parent, flags, type, buttons, NULL);
339
340
  if (message_format)
341
    {
342
      va_start (args, message_format);
343
      msg = g_markup_vprintf_escaped (message_format, args);
344
      va_end (args);
345
346
      gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (widget), msg);
347
348
      g_free (msg);
349
    }
350
351
  return widget;
352
}
353
354
 /*
355
  * This is almost humorously stupid.  We jump through some hoops and kill
356
  * a few kittens here because we want to preserve API compatibility with
357
  * GtkMessageDialog and extend it instead of duplicating its functionality.
358
  * If only GtkMessageDialog were easier to extend then maybe all those
359
  * kittens wouldn't have had to die...
360
  */
361
static GtkWidget *
362
ido_message_dialog_get_label (IdoMessageDialog *dialog, gboolean primary)
363
{
364
  GList *list;
365
  gchar *text;
366
  gchar *secondary_text;
42 by Cody Russell
Fix IdoMessageDialog to compile with deprecated gtk+ for Meerkat.
367
  GtkWidget *content;
368
  GList *children;
35 by Cody Russell
IdoMessageDialog - a morphing message dialog.
369
370
  g_object_get (G_OBJECT (dialog),
371
                "text", &text,
372
                "secondary-text", &secondary_text,
373
                NULL);
374
375
  g_return_val_if_fail (IDO_IS_MESSAGE_DIALOG (dialog), NULL);
376
42 by Cody Russell
Fix IdoMessageDialog to compile with deprecated gtk+ for Meerkat.
377
  content = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
378
  children = gtk_container_get_children (GTK_CONTAINER (content));
379
380
  for (list = children; list != NULL; list = list->next)
35 by Cody Russell
IdoMessageDialog - a morphing message dialog.
381
    {
97.1.1 by Ken VanDine
removed deprecations from gtk3 and fixed sizing issues with idemessagedialog (LP: #888392)
382
#ifdef USE_GTK3
383
      if (G_TYPE_FROM_INSTANCE (list->data) == GTK_TYPE_BOX && gtk_orientable_get_orientation (list->data) == GTK_ORIENTATION_HORIZONTAL)
384
#else
42 by Cody Russell
Fix IdoMessageDialog to compile with deprecated gtk+ for Meerkat.
385
      if (G_TYPE_FROM_INSTANCE (list->data) == GTK_TYPE_HBOX)
97.1.1 by Ken VanDine
removed deprecations from gtk3 and fixed sizing issues with idemessagedialog (LP: #888392)
386
#endif
35 by Cody Russell
IdoMessageDialog - a morphing message dialog.
387
        {
42 by Cody Russell
Fix IdoMessageDialog to compile with deprecated gtk+ for Meerkat.
388
	  GList *hchildren;
35 by Cody Russell
IdoMessageDialog - a morphing message dialog.
389
          GList *hlist;
42 by Cody Russell
Fix IdoMessageDialog to compile with deprecated gtk+ for Meerkat.
390
          GtkWidget *hbox = GTK_WIDGET (list->data);
391
392
	  hchildren = gtk_container_get_children (GTK_CONTAINER (hbox));
393
394
          for (hlist = hchildren; hlist != NULL; hlist = hlist->next)
35 by Cody Russell
IdoMessageDialog - a morphing message dialog.
395
            {
97.1.1 by Ken VanDine
removed deprecations from gtk3 and fixed sizing issues with idemessagedialog (LP: #888392)
396
#ifdef USE_GTK3
397
              if (G_TYPE_FROM_INSTANCE (hlist->data) == GTK_TYPE_BOX && gtk_orientable_get_orientation (hlist->data) == GTK_ORIENTATION_VERTICAL)
398
#else
42 by Cody Russell
Fix IdoMessageDialog to compile with deprecated gtk+ for Meerkat.
399
              if (G_TYPE_FROM_INSTANCE (hlist->data) == GTK_TYPE_VBOX)
97.1.1 by Ken VanDine
removed deprecations from gtk3 and fixed sizing issues with idemessagedialog (LP: #888392)
400
#endif
35 by Cody Russell
IdoMessageDialog - a morphing message dialog.
401
                {
402
                  GList *vlist;
42 by Cody Russell
Fix IdoMessageDialog to compile with deprecated gtk+ for Meerkat.
403
                  GtkWidget *vbox = GTK_WIDGET (hlist->data);
404
		  GList *vchildren;
405
406
		  vchildren = gtk_container_get_children (GTK_CONTAINER (vbox));
407
408
                  for (vlist = vchildren; vlist != NULL; vlist = vlist->next)
35 by Cody Russell
IdoMessageDialog - a morphing message dialog.
409
                    {
410
                      GtkLabel *label;
411
42 by Cody Russell
Fix IdoMessageDialog to compile with deprecated gtk+ for Meerkat.
412
                      label = GTK_LABEL (vlist->data);
35 by Cody Russell
IdoMessageDialog - a morphing message dialog.
413
77.1.2 by Michael Terry
allow building either gtk2 or gtk3 versions; some deprecation cleanups
414
                      if (strcmp ((primary ? text : secondary_text),
415
                                  gtk_label_get_label (label)) == 0)
35 by Cody Russell
IdoMessageDialog - a morphing message dialog.
416
                        {
42 by Cody Russell
Fix IdoMessageDialog to compile with deprecated gtk+ for Meerkat.
417
                          return GTK_WIDGET (label);
35 by Cody Russell
IdoMessageDialog - a morphing message dialog.
418
                        }
419
                    }
420
                }
421
            }
422
        }
423
    }
424
425
  return NULL;
426
}
427
428
static GtkWidget *
429
ido_message_dialog_get_secondary_label (IdoMessageDialog *dialog)
430
{
431
  return ido_message_dialog_get_label (dialog, FALSE);
432
}
433
434
static GtkWidget *
435
ido_message_dialog_get_primary_label (IdoMessageDialog *dialog)
436
{
437
  return ido_message_dialog_get_label (dialog, TRUE);
438
}