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