]> git.0d.be Git - empathy.git/blob - src/empathy-sidebar.c
Updated Polish translation
[empathy.git] / src / empathy-sidebar.c
1 /*
2  * Copyright (C) 2004 Red Hat, Inc.
3  * Copyright (C) 2007 The Free Software Foundation
4  * Copyright (C) 2008 Marco Barisione <marco@barisione.org>
5  *
6  * Based on evince code (shell/ev-sidebar.c) by:
7  *      - Jonathan Blandford <jrb@alum.mit.edu>
8  *
9  * Base on eog code (src/eog-sidebar.c) by:
10  *      - Lucas Rocha <lucasr@gnome.org>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include <string.h>
32 #include <gtk/gtk.h>
33 #include <gdk/gdkkeysyms.h>
34
35 #include "empathy-sidebar.h"
36
37 enum
38 {
39     PROP_0,
40     PROP_CURRENT_PAGE
41 };
42
43 enum
44 {
45     PAGE_COLUMN_TITLE,
46     PAGE_COLUMN_MENU_ITEM,
47     PAGE_COLUMN_MAIN_WIDGET,
48     PAGE_COLUMN_NOTEBOOK_INDEX,
49     PAGE_COLUMN_NUM_COLS
50 };
51
52 enum
53 {
54     SIGNAL_PAGE_ADDED,
55     SIGNAL_PAGE_REMOVED,
56     SIGNAL_LAST
57 };
58
59 static gint signals[SIGNAL_LAST];
60
61 struct _EmpathySidebarPrivate
62 {
63     GtkWidget *notebook;
64     GtkWidget *select_button;
65     GtkWidget *menu;
66     GtkWidget *hbox;
67     GtkWidget *label;
68
69     GtkTreeModel *page_model;
70 };
71
72 G_DEFINE_TYPE (EmpathySidebar, empathy_sidebar, GTK_TYPE_VBOX)
73
74 #define EMPATHY_SIDEBAR_GET_PRIVATE(object) \
75     (G_TYPE_INSTANCE_GET_PRIVATE ((object), EMPATHY_TYPE_SIDEBAR, EmpathySidebarPrivate))
76
77 static void
78 empathy_sidebar_destroy (GtkObject *object)
79 {
80   EmpathySidebar *sidebar = EMPATHY_SIDEBAR (object);
81
82   if (sidebar->priv->menu)
83     {
84       gtk_menu_detach (GTK_MENU (sidebar->priv->menu));
85       sidebar->priv->menu = NULL;
86     }
87
88   if (sidebar->priv->page_model)
89     {
90       g_object_unref (sidebar->priv->page_model);
91       sidebar->priv->page_model = NULL;
92     }
93
94   (* GTK_OBJECT_CLASS (empathy_sidebar_parent_class)->destroy) (object);
95 }
96
97 static void
98 empathy_sidebar_select_page (EmpathySidebar *sidebar,
99                                 GtkTreeIter *iter)
100 {
101   gchar *title;
102   gint index_;
103
104   gtk_tree_model_get (sidebar->priv->page_model, iter,
105       PAGE_COLUMN_TITLE, &title,
106       PAGE_COLUMN_NOTEBOOK_INDEX, &index_,
107       -1);
108
109   gtk_notebook_set_current_page (GTK_NOTEBOOK (sidebar->priv->notebook),
110       index_);
111   gtk_label_set_text (GTK_LABEL (sidebar->priv->label), title);
112
113   g_free (title);
114 }
115
116 void
117 empathy_sidebar_set_page (EmpathySidebar *sidebar,
118                              GtkWidget *main_widget)
119 {
120   GtkTreeIter iter;
121   gboolean valid;
122
123   valid = gtk_tree_model_get_iter_first (sidebar->priv->page_model, &iter);
124
125   while (valid)
126     {
127       GtkWidget *widget;
128
129       gtk_tree_model_get (sidebar->priv->page_model, &iter,
130           PAGE_COLUMN_MAIN_WIDGET, &widget,
131           -1);
132
133       if (widget == main_widget)
134         {
135           empathy_sidebar_select_page (sidebar, &iter);
136           valid = FALSE;
137         }
138       else
139         {
140           valid = gtk_tree_model_iter_next (sidebar->priv->page_model, &iter);
141         }
142
143       g_object_unref (widget);
144   }
145
146   g_object_notify (G_OBJECT (sidebar), "current-page");
147 }
148
149 static GtkWidget *
150 empathy_sidebar_get_current_page (EmpathySidebar *sidebar)
151 {
152   GtkNotebook *notebook = GTK_NOTEBOOK (sidebar->priv->notebook);
153
154   return gtk_notebook_get_nth_page (
155     notebook, gtk_notebook_get_current_page (notebook));
156 }
157
158 static void
159 empathy_sidebar_set_property (GObject *object,
160                                  guint prop_id,
161                                  const GValue *value,
162                                  GParamSpec *pspec)
163 {
164   EmpathySidebar *sidebar = EMPATHY_SIDEBAR (object);
165
166   switch (prop_id)
167     {
168       case PROP_CURRENT_PAGE:
169         empathy_sidebar_set_page (sidebar, g_value_get_object (value));
170         break;
171       default:
172         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
173     }
174 }
175
176 static void
177 empathy_sidebar_get_property (GObject *object,
178                                  guint prop_id,
179                                  GValue *value,
180                                  GParamSpec *pspec)
181 {
182   EmpathySidebar *sidebar = EMPATHY_SIDEBAR (object);
183
184   switch (prop_id)
185     {
186       case PROP_CURRENT_PAGE:
187         g_value_set_object (value,
188             empathy_sidebar_get_current_page (sidebar));
189         break;
190       default:
191         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
192     }
193 }
194
195 static void
196 empathy_sidebar_class_init (EmpathySidebarClass *empathy_sidebar_class)
197 {
198   GObjectClass *g_object_class;
199   GtkWidgetClass *widget_class;
200   GtkObjectClass *gtk_object_klass;
201
202   g_object_class = G_OBJECT_CLASS (empathy_sidebar_class);
203   widget_class = GTK_WIDGET_CLASS (empathy_sidebar_class);
204   gtk_object_klass = GTK_OBJECT_CLASS (empathy_sidebar_class);
205
206   g_type_class_add_private (g_object_class, sizeof (EmpathySidebarPrivate));
207
208   gtk_object_klass->destroy = empathy_sidebar_destroy;
209   g_object_class->get_property = empathy_sidebar_get_property;
210   g_object_class->set_property = empathy_sidebar_set_property;
211
212   g_object_class_install_property (g_object_class,
213       PROP_CURRENT_PAGE,
214       g_param_spec_object ("current-page",
215           "Current page",
216           "The currently visible page",
217           GTK_TYPE_WIDGET,
218           G_PARAM_READWRITE));
219
220   signals[SIGNAL_PAGE_ADDED] = g_signal_new ("page-added",
221       EMPATHY_TYPE_SIDEBAR, G_SIGNAL_RUN_FIRST,
222       G_STRUCT_OFFSET (EmpathySidebarClass, page_added),
223       NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
224       G_TYPE_NONE, 1, GTK_TYPE_WIDGET);
225
226   signals[SIGNAL_PAGE_REMOVED] = g_signal_new ("page-removed",
227       EMPATHY_TYPE_SIDEBAR, G_SIGNAL_RUN_FIRST,
228       G_STRUCT_OFFSET (EmpathySidebarClass, page_removed),
229       NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
230       G_TYPE_NONE, 1, GTK_TYPE_WIDGET);
231 }
232
233 static void
234 empathy_sidebar_menu_position_under (GtkMenu *menu,
235                                          gint *x,
236                                          gint *y,
237                                          gboolean *push_in,
238                                          gpointer user_data)
239 {
240   GtkWidget *widget;
241   GtkAllocation allocation;
242
243   g_return_if_fail (GTK_IS_BUTTON (user_data));
244   g_return_if_fail (gtk_widget_get_has_window (user_data));
245
246   widget = GTK_WIDGET (user_data);
247
248   gdk_window_get_origin (gtk_widget_get_window (widget), x, y);
249
250   gtk_widget_get_allocation (widget, &allocation);
251   *x += allocation.x;
252   *y += allocation.y + allocation.height;
253
254   *push_in = FALSE;
255 }
256
257 static gboolean
258 empathy_sidebar_select_button_press_cb (GtkWidget *widget,
259                                            GdkEventButton *event,
260                                            gpointer user_data)
261 {
262   EmpathySidebar *sidebar = EMPATHY_SIDEBAR (user_data);
263
264   if (event->button == 1)
265     {
266       GtkRequisition requisition;
267       gint width;
268       GtkAllocation allocation;
269
270       gtk_widget_get_allocation (widget, &allocation);
271       width = allocation.width;
272
273       gtk_widget_set_size_request (sidebar->priv->menu, -1, -1);
274       gtk_widget_size_request (sidebar->priv->menu, &requisition);
275       gtk_widget_set_size_request (sidebar->priv->menu,
276           MAX (width, requisition.width), -1);
277
278       gtk_widget_grab_focus (widget);
279
280       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
281
282       gtk_menu_popup (GTK_MENU (sidebar->priv->menu),
283           NULL, NULL, empathy_sidebar_menu_position_under, widget,
284           event->button, event->time);
285
286       return TRUE;
287     }
288
289   return FALSE;
290 }
291
292 static gboolean
293 empathy_sidebar_select_button_key_press_cb (GtkWidget *widget,
294                                                GdkEventKey *event,
295                                                gpointer user_data)
296 {
297   EmpathySidebar *sidebar = EMPATHY_SIDEBAR (user_data);
298
299   if (event->keyval == GDK_space ||
300       event->keyval == GDK_KP_Space ||
301       event->keyval == GDK_Return ||
302       event->keyval == GDK_KP_Enter)
303     {
304       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
305
306       gtk_menu_popup (GTK_MENU (sidebar->priv->menu),
307           NULL, NULL, empathy_sidebar_menu_position_under, widget,
308           1, event->time);
309
310       return TRUE;
311     }
312
313   return FALSE;
314 }
315
316 static void
317 empathy_sidebar_close_clicked_cb (GtkWidget *widget,
318                                      gpointer user_data)
319 {
320   EmpathySidebar *sidebar = EMPATHY_SIDEBAR (user_data);
321
322   gtk_widget_hide (GTK_WIDGET (sidebar));
323 }
324
325 static void
326 empathy_sidebar_menu_deactivate_cb (GtkWidget *widget,
327                                        gpointer user_data)
328 {
329   GtkWidget *menu_button;
330
331   menu_button = GTK_WIDGET (user_data);
332
333   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (menu_button), FALSE);
334 }
335
336 static void
337 empathy_sidebar_menu_detach_cb (GtkWidget *widget,
338                                    GtkMenu *menu)
339 {
340   EmpathySidebar *sidebar = EMPATHY_SIDEBAR (widget);
341
342   sidebar->priv->menu = NULL;
343 }
344
345 static void
346 empathy_sidebar_menu_item_activate_cb (GtkWidget *widget,
347                                           gpointer user_data)
348 {
349   EmpathySidebar *sidebar = EMPATHY_SIDEBAR (user_data);
350   GtkTreeIter iter;
351   GtkWidget *menu_item, *item;
352   gboolean valid;
353
354   menu_item = gtk_menu_get_active (GTK_MENU (sidebar->priv->menu));
355   valid = gtk_tree_model_get_iter_first (sidebar->priv->page_model, &iter);
356
357   while (valid)
358     {
359       gtk_tree_model_get (sidebar->priv->page_model, &iter,
360           PAGE_COLUMN_MENU_ITEM, &item,
361           -1);
362
363       if (item == menu_item)
364         {
365           empathy_sidebar_select_page (sidebar, &iter);
366           valid = FALSE;
367         }
368       else
369         {
370           valid = gtk_tree_model_iter_next (sidebar->priv->page_model, &iter);
371         }
372
373       g_object_unref (item);
374     }
375
376   g_object_notify (G_OBJECT (sidebar), "current-page");
377 }
378
379 static void
380 empathy_sidebar_init (EmpathySidebar *sidebar)
381 {
382   GtkWidget *hbox;
383   GtkWidget *close_button;
384   GtkWidget *select_hbox;
385   GtkWidget *arrow;
386   GtkWidget *image;
387
388   sidebar->priv = EMPATHY_SIDEBAR_GET_PRIVATE (sidebar);
389
390   /* data model */
391   sidebar->priv->page_model = (GtkTreeModel *) gtk_list_store_new (
392       PAGE_COLUMN_NUM_COLS,
393       G_TYPE_STRING,
394       GTK_TYPE_WIDGET,
395       GTK_TYPE_WIDGET,
396       G_TYPE_INT);
397
398   /* top option menu */
399   hbox = gtk_hbox_new (FALSE, 0);
400   sidebar->priv->hbox = hbox;
401   gtk_box_pack_start (GTK_BOX (sidebar), hbox, FALSE, FALSE, 0);
402   gtk_widget_show (hbox);
403
404   sidebar->priv->select_button = gtk_toggle_button_new ();
405   gtk_button_set_relief (GTK_BUTTON (sidebar->priv->select_button),
406       GTK_RELIEF_NONE);
407
408   g_signal_connect (sidebar->priv->select_button, "button_press_event",
409       G_CALLBACK (empathy_sidebar_select_button_press_cb),
410       sidebar);
411
412   g_signal_connect (sidebar->priv->select_button, "key_press_event",
413       G_CALLBACK (empathy_sidebar_select_button_key_press_cb),
414       sidebar);
415
416   select_hbox = gtk_hbox_new (FALSE, 0);
417
418   sidebar->priv->label = gtk_label_new ("");
419
420   gtk_box_pack_start (GTK_BOX (select_hbox),
421       sidebar->priv->label,
422       FALSE, FALSE, 0);
423
424   gtk_widget_show (sidebar->priv->label);
425
426   arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
427   gtk_box_pack_end (GTK_BOX (select_hbox), arrow, FALSE, FALSE, 0);
428   gtk_widget_show (arrow);
429
430   gtk_container_add (GTK_CONTAINER (sidebar->priv->select_button), select_hbox);
431   gtk_widget_show (select_hbox);
432
433   gtk_box_pack_start (GTK_BOX (hbox), sidebar->priv->select_button, TRUE, TRUE, 0);
434   gtk_widget_show (sidebar->priv->select_button);
435
436   close_button = gtk_button_new ();
437
438   gtk_button_set_relief (GTK_BUTTON (close_button), GTK_RELIEF_NONE);
439
440   g_signal_connect (close_button, "clicked",
441       G_CALLBACK (empathy_sidebar_close_clicked_cb),
442       sidebar);
443
444   image = gtk_image_new_from_stock (GTK_STOCK_CLOSE,
445       GTK_ICON_SIZE_MENU);
446   gtk_container_add (GTK_CONTAINER (close_button), image);
447   gtk_widget_show (image);
448
449   gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);
450   gtk_widget_show (close_button);
451
452   sidebar->priv->menu = gtk_menu_new ();
453
454   g_signal_connect (sidebar->priv->menu, "deactivate",
455       G_CALLBACK (empathy_sidebar_menu_deactivate_cb),
456       sidebar->priv->select_button);
457
458   gtk_menu_attach_to_widget (GTK_MENU (sidebar->priv->menu),
459       GTK_WIDGET (sidebar),
460       empathy_sidebar_menu_detach_cb);
461
462   gtk_widget_show (sidebar->priv->menu);
463
464   sidebar->priv->notebook = gtk_notebook_new ();
465
466   gtk_notebook_set_show_border (GTK_NOTEBOOK (sidebar->priv->notebook), FALSE);
467   gtk_notebook_set_show_tabs (GTK_NOTEBOOK (sidebar->priv->notebook), FALSE);
468
469   gtk_box_pack_start (GTK_BOX (sidebar), sidebar->priv->notebook,
470       TRUE, TRUE, 0);
471
472   gtk_widget_show (sidebar->priv->notebook);
473 }
474
475 GtkWidget *
476 empathy_sidebar_new (void)
477 {
478   GtkWidget *sidebar;
479
480   sidebar = g_object_new (EMPATHY_TYPE_SIDEBAR, NULL);
481
482   return sidebar;
483 }
484
485 void
486 empathy_sidebar_add_page (EmpathySidebar *sidebar,
487                              const gchar *title,
488                              GtkWidget *main_widget)
489 {
490   GtkTreeIter iter;
491   GtkWidget *menu_item;
492   gchar *label_title;
493   gint index_;
494
495   g_return_if_fail (EMPATHY_IS_SIDEBAR (sidebar));
496   g_return_if_fail (GTK_IS_WIDGET (main_widget));
497
498   index_ = gtk_notebook_append_page (GTK_NOTEBOOK (sidebar->priv->notebook),
499       main_widget, NULL);
500
501   menu_item = gtk_image_menu_item_new_with_label (title);
502
503   g_signal_connect (menu_item, "activate",
504       G_CALLBACK (empathy_sidebar_menu_item_activate_cb),
505       sidebar);
506
507   gtk_widget_show (menu_item);
508
509   gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->priv->menu),
510       menu_item);
511
512   /* Insert and move to end */
513   gtk_list_store_insert_with_values (GTK_LIST_STORE (sidebar->priv->page_model),
514       &iter, 0,
515       PAGE_COLUMN_TITLE, title,
516       PAGE_COLUMN_MENU_ITEM, menu_item,
517       PAGE_COLUMN_MAIN_WIDGET, main_widget,
518       PAGE_COLUMN_NOTEBOOK_INDEX, index_,
519       -1);
520
521   gtk_list_store_move_before (GTK_LIST_STORE(sidebar->priv->page_model),
522       &iter,
523       NULL);
524
525   /* Set the first item added as active */
526   gtk_tree_model_get_iter_first (sidebar->priv->page_model, &iter);
527   gtk_tree_model_get (sidebar->priv->page_model,
528       &iter,
529       PAGE_COLUMN_TITLE, &label_title,
530       PAGE_COLUMN_NOTEBOOK_INDEX, &index_,
531       -1);
532
533   gtk_menu_set_active (GTK_MENU (sidebar->priv->menu), index_);
534
535   gtk_label_set_text (GTK_LABEL (sidebar->priv->label), label_title);
536
537   gtk_notebook_set_current_page (GTK_NOTEBOOK (sidebar->priv->notebook),
538       index_);
539
540   g_free (label_title);
541
542   g_signal_emit (G_OBJECT (sidebar), signals[SIGNAL_PAGE_ADDED],
543       0, main_widget);
544 }
545
546 void
547 empathy_sidebar_remove_page (EmpathySidebar *sidebar,
548                                 GtkWidget *main_widget)
549 {
550   GtkTreeIter iter;
551   GtkWidget *widget, *menu_item;
552   gboolean valid;
553   gint index_;
554
555   g_return_if_fail (EMPATHY_IS_SIDEBAR (sidebar));
556   g_return_if_fail (GTK_IS_WIDGET (main_widget));
557
558   valid = gtk_tree_model_get_iter_first (sidebar->priv->page_model, &iter);
559
560   while (valid)
561     {
562       gtk_tree_model_get (sidebar->priv->page_model, &iter,
563           PAGE_COLUMN_NOTEBOOK_INDEX, &index_,
564           PAGE_COLUMN_MENU_ITEM, &menu_item,
565           PAGE_COLUMN_MAIN_WIDGET, &widget,
566           -1);
567
568       if (widget == main_widget)
569           break;
570       else
571           valid = gtk_tree_model_iter_next (sidebar->priv->page_model, &iter);
572
573       g_object_unref (menu_item);
574       g_object_unref (widget);
575     }
576
577   if (valid)
578     {
579       gtk_notebook_remove_page (GTK_NOTEBOOK (sidebar->priv->notebook),
580           index_);
581
582       gtk_container_remove (GTK_CONTAINER (sidebar->priv->menu), menu_item);
583
584       gtk_list_store_remove (GTK_LIST_STORE (sidebar->priv->page_model),
585           &iter);
586
587       g_signal_emit (G_OBJECT (sidebar),
588           signals[SIGNAL_PAGE_REMOVED], 0, main_widget);
589     }
590 }
591
592 gint
593 empathy_sidebar_get_n_pages (EmpathySidebar *sidebar)
594 {
595   g_return_val_if_fail (EMPATHY_IS_SIDEBAR (sidebar), TRUE);
596
597   return gtk_tree_model_iter_n_children (
598       GTK_TREE_MODEL (sidebar->priv->page_model), NULL);
599 }
600
601 gboolean
602 empathy_sidebar_is_empty (EmpathySidebar *sidebar)
603 {
604   g_return_val_if_fail (EMPATHY_IS_SIDEBAR (sidebar), TRUE);
605
606   return gtk_tree_model_iter_n_children (
607       GTK_TREE_MODEL (sidebar->priv->page_model), NULL) == 0;
608 }