1 /*************************************************************************/ 2 /* Copyright (C) 2013 matias <mati86dl@gmail.com> */ 3 /* */ 4 /* This program is free software: you can redistribute it and/or modify */ 5 /* it under the terms of the GNU General Public License as published by */ 6 /* the Free Software Foundation, either version 3 of the License, or */ 7 /* (at your option) any later version. */ 8 /* */ 9 /* This program is distributed in the hope that it will be useful, */ 10 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ 11 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ 12 /* GNU General Public License for more details. */ 13 /* */ 14 /* You should have received a copy of the GNU General Public License */ 15 /* along with this program. If not, see <http://www.gnu.org/licenses/>. */ 16 /*************************************************************************/ 17 18 #include "pragha-sidebar.h" 19 20 struct _PraghaSidebar { 21 GtkBox __parent__; 22 23 GtkWidget *container; 24 GtkWidget *header; 25 GtkWidget *menu_button; 26 GtkWidget *close_button; 27 GtkWidget *title_box; 28 29 GtkMenu *popup_menu; 30 }; 31 32 G_DEFINE_TYPE(PraghaSidebar, pragha_sidebar, GTK_TYPE_BOX) 33 34 enum { 35 SIGNAL_CHILDREN_CHANGED, 36 LAST_SIGNAL 37 }; 38 static int signals[LAST_SIGNAL] = { 0 }; 39 40 /* 41 * Public Api. 42 */ 43 44 void 45 pragha_sidebar_attach_plugin (PraghaSidebar *sidebar, 46 GtkWidget *widget, 47 GtkWidget *title, 48 GtkMenu *popup_menu) 49 { 50 if (!widget || !title) 51 return; 52 53 gtk_notebook_insert_page (GTK_NOTEBOOK(sidebar->container), 54 widget, 55 NULL, 56 0); 57 58 gtk_container_add (GTK_CONTAINER(sidebar->title_box), title); 59 60 if (popup_menu) { 61 gtk_menu_attach_to_widget(GTK_MENU(popup_menu), title, NULL); 62 sidebar->popup_menu = popup_menu; 63 } 64 gtk_widget_show_all (title); 65 66 g_signal_emit (sidebar, signals[SIGNAL_CHILDREN_CHANGED], 0); 67 } 68 69 void 70 pragha_sidebar_remove_plugin (PraghaSidebar *sidebar, 71 GtkWidget *widget) 72 { 73 GList *list; 74 GtkWidget *children; 75 gint page; 76 77 page = gtk_notebook_page_num (GTK_NOTEBOOK(sidebar->container), widget); 78 79 if (page >= 0) { 80 gtk_notebook_remove_page (GTK_NOTEBOOK(sidebar->container), page); 81 gtk_menu_detach (sidebar->popup_menu); 82 83 list = gtk_container_get_children (GTK_CONTAINER(sidebar->title_box)); 84 if (list) { 85 children = list->data; 86 gtk_container_remove(GTK_CONTAINER(sidebar->title_box), children); 87 g_list_free(list); 88 } 89 } 90 91 g_signal_emit (sidebar, signals[SIGNAL_CHILDREN_CHANGED], 0); 92 } 93 94 gint 95 pragha_sidebar_get_n_panes (PraghaSidebar *sidebar) 96 { 97 return gtk_notebook_get_n_pages (GTK_NOTEBOOK(sidebar->container)); 98 } 99 100 void 101 pragha_sidebar_style_position (PraghaSidebar *sidebar, GtkPositionType position) 102 { 103 GtkWidget *parent; 104 parent = gtk_widget_get_parent (GTK_WIDGET(sidebar->close_button)); 105 gtk_box_reorder_child (GTK_BOX(parent), 106 GTK_WIDGET(sidebar->close_button), 107 (position == GTK_POS_RIGHT) ? 0 : 1); 108 } 109 110 /* 111 * Internal Calbacks. 112 */ 113 114 static void 115 pragha_sidebar_menu_position (GtkMenu *menu, 116 gint *x, 117 gint *y, 118 gboolean *push_in, 119 gpointer user_data) 120 { 121 GtkWidget *widget; 122 GtkAllocation allocation; 123 GtkRequisition requisition; 124 gint menu_xpos, menu_ypos; 125 126 widget = GTK_WIDGET (user_data); 127 128 gtk_widget_get_preferred_size (GTK_WIDGET(menu), &requisition, NULL); 129 130 gdk_window_get_origin (gtk_widget_get_window(widget), &menu_xpos, &menu_ypos); 131 132 gtk_widget_get_allocation(widget, &allocation); 133 134 menu_xpos += allocation.x; 135 menu_ypos += allocation.y; 136 137 if (menu_ypos > gdk_screen_get_height (gtk_widget_get_screen (widget)) / 2) 138 menu_ypos -= requisition.height; 139 else 140 menu_ypos += allocation.height; 141 142 *x = menu_xpos; 143 *y = menu_ypos - 5; 144 145 *push_in = TRUE; 146 } 147 148 static void 149 pragha_sidebar_close_button_cb (GtkWidget *widget, PraghaSidebar *sidebar) 150 { 151 gtk_widget_hide (GTK_WIDGET(sidebar)); 152 } 153 154 static gboolean 155 pragha_sidebar_right_click_cb(GtkWidget *widget, 156 GdkEventButton *event, 157 PraghaSidebar *sidebar) 158 { 159 gboolean ret = FALSE; 160 161 if(!sidebar->popup_menu) 162 return FALSE; 163 164 if(!gtk_widget_get_sensitive(gtk_notebook_get_nth_page (GTK_NOTEBOOK(sidebar->container), 0))) 165 return FALSE; 166 167 switch(event->button) { 168 case 3: 169 gtk_menu_popup(GTK_MENU(sidebar->popup_menu), 170 NULL, NULL, NULL, NULL, 171 event->button, event->time); 172 ret = TRUE; 173 break; 174 case 1: 175 if (widget == sidebar->menu_button) { 176 gtk_menu_popup(GTK_MENU(sidebar->popup_menu), 177 NULL, NULL, 178 (GtkMenuPositionFunc) pragha_sidebar_menu_position, 179 widget, 180 0, 181 gtk_get_current_event_time()); 182 ret = TRUE; 183 } 184 break; 185 default: 186 break; 187 } 188 189 return ret; 190 } 191 192 /** 193 * Construction: 194 **/ 195 196 void 197 pragha_sidebar_set_tiny_button (GtkWidget *button) 198 { 199 GtkCssProvider *provider; 200 provider = gtk_css_provider_new (); 201 gtk_css_provider_load_from_data (provider, 202 "#s-tiny-button {\n" 203 " -GtkButton-default-border : 0px;\n" 204 " -GtkButton-default-outside-border : 0px;\n" 205 " -GtkButton-inner-border: 0px;\n" 206 " -GtkWidget-focus-line-width: 0px;\n" 207 " -GtkWidget-focus-padding: 0px;\n" 208 " padding: 1px;}", 209 -1, NULL); 210 gtk_style_context_add_provider (gtk_widget_get_style_context (button), 211 GTK_STYLE_PROVIDER (provider), 212 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); 213 gtk_widget_set_name (button, "s-tiny-button"); 214 g_object_unref (provider); 215 } 216 217 GtkWidget * 218 praga_sidebar_menu_button_new (PraghaSidebar *sidebar) 219 { 220 GtkWidget *button, *hbox, *arrow; 221 222 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); 223 224 button = gtk_button_new(); 225 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); 226 227 #if GTK_CHECK_VERSION (3, 14, 0) 228 arrow = gtk_image_new_from_icon_name("pan-down-symbolic", GTK_ICON_SIZE_MENU); 229 #else 230 arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE); 231 #endif 232 233 gtk_box_pack_start (GTK_BOX(hbox), 234 sidebar->title_box, 235 TRUE, TRUE, 0); 236 gtk_box_pack_start (GTK_BOX(hbox), 237 arrow, 238 FALSE, FALSE, 0); 239 240 gtk_widget_set_valign (GTK_WIDGET(sidebar->title_box), GTK_ALIGN_CENTER); 241 242 gtk_container_add (GTK_CONTAINER(button), hbox); 243 244 g_signal_connect(G_OBJECT(button), 245 "button-press-event", 246 G_CALLBACK(pragha_sidebar_right_click_cb), 247 sidebar); 248 249 return button; 250 } 251 252 GtkWidget * 253 pragha_sidebar_close_button_new(PraghaSidebar *sidebar) 254 { 255 GtkWidget *button; 256 GIcon *icon = NULL; 257 258 const gchar *fallback_icons[] = { 259 "view-left-close", 260 "tab-close", 261 "window-close", 262 NULL, 263 }; 264 265 button = gtk_button_new (); 266 gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE); 267 gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE); 268 pragha_sidebar_set_tiny_button (button); 269 270 icon = g_themed_icon_new_from_names ((gchar **)fallback_icons, -1); 271 gtk_button_set_image (GTK_BUTTON (button), 272 gtk_image_new_from_gicon(icon, GTK_ICON_SIZE_MENU)); 273 g_object_unref (icon); 274 275 g_signal_connect(G_OBJECT (button), 276 "clicked", 277 G_CALLBACK(pragha_sidebar_close_button_cb), 278 sidebar); 279 280 return button; 281 } 282 283 GtkWidget * 284 pragha_sidebar_header_new(PraghaSidebar *sidebar) 285 { 286 GtkWidget *hbox; 287 288 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); 289 290 gtk_box_pack_start(GTK_BOX(hbox), 291 sidebar->menu_button, 292 TRUE, 293 TRUE, 294 0); 295 gtk_box_pack_start(GTK_BOX(hbox), 296 sidebar->close_button, 297 FALSE, 298 FALSE, 299 0); 300 return hbox; 301 } 302 303 GtkWidget * 304 pragha_sidebar_container_new(PraghaSidebar *sidebar) 305 { 306 GtkWidget *notebook; 307 308 notebook = gtk_notebook_new(); 309 310 gtk_notebook_set_show_tabs (GTK_NOTEBOOK(notebook), FALSE); 311 gtk_notebook_set_show_border (GTK_NOTEBOOK(notebook), FALSE); 312 gtk_notebook_popup_disable(GTK_NOTEBOOK(notebook)); 313 314 return notebook; 315 } 316 317 static void 318 pragha_sidebar_finalize (GObject *object) 319 { 320 //PraghaSidebar *sidebar = PRAGHA_SIDEBAR (object); 321 (*G_OBJECT_CLASS (pragha_sidebar_parent_class)->finalize) (object); 322 } 323 324 static void 325 pragha_sidebar_init (PraghaSidebar *sidebar) 326 { 327 gtk_orientable_set_orientation (GTK_ORIENTABLE (sidebar), 328 GTK_ORIENTATION_VERTICAL); 329 330 gtk_box_set_spacing (GTK_BOX(sidebar), 2); 331 332 sidebar->title_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); 333 sidebar->menu_button = praga_sidebar_menu_button_new (sidebar); 334 sidebar->close_button = pragha_sidebar_close_button_new (sidebar); 335 336 sidebar->header = pragha_sidebar_header_new (sidebar); 337 sidebar->container = pragha_sidebar_container_new (sidebar); 338 339 sidebar->popup_menu = NULL; 340 341 gtk_box_pack_start (GTK_BOX(sidebar), sidebar->header, 342 FALSE, FALSE, 0); 343 gtk_box_pack_start (GTK_BOX(sidebar), sidebar->container, 344 TRUE, TRUE, 0); 345 346 gtk_widget_show_all (GTK_WIDGET(sidebar->header)); 347 gtk_widget_show_all (GTK_WIDGET(sidebar->container)); 348 } 349 350 static void 351 pragha_sidebar_class_init (PraghaSidebarClass *klass) 352 { 353 GObjectClass *gobject_class; 354 355 gobject_class = G_OBJECT_CLASS (klass); 356 gobject_class->finalize = pragha_sidebar_finalize; 357 358 signals[SIGNAL_CHILDREN_CHANGED] = 359 g_signal_new ("children-changed", 360 G_TYPE_FROM_CLASS (gobject_class), 361 G_SIGNAL_RUN_LAST, 362 G_STRUCT_OFFSET (PraghaSidebarClass, children_changed), 363 NULL, NULL, 364 g_cclosure_marshal_VOID__VOID, 365 G_TYPE_NONE, 0); 366 } 367 368 PraghaSidebar * 369 pragha_sidebar_new (void) 370 { 371 return g_object_new (PRAGHA_TYPE_SIDEBAR, NULL); 372 } 373