1 /*
2  *
3  *		dpaned.c
4  *
5  *      Copyright 2010 Alexander Petukhov <devel(at)apetukhov.ru>
6  *
7  *      This program is free software; you can redistribute it and/or modify
8  *      it under the terms of the GNU General Public License as published by
9  *      the Free Software Foundation; either version 2 of the License, or
10  *      (at your option) any later version.
11  *
12  *      This program is distributed in the hope that it will be useful,
13  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *      GNU General Public License for more details.
16  *
17  *      You should have received a copy of the GNU General Public License
18  *      along with this program; if not, write to the Free Software
19  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20  *      MA 02110-1301, USA.
21  */
22 
23 /*
24  *		Working with debug paned.
25  */
26 
27 #include <sys/stat.h>
28 
29 #include <memory.h>
30 #include <string.h>
31 
32 #include <geanyplugin.h>
33 extern GeanyData		*geany_data;
34 
35 #include "dpaned.h"
36 #include "tabs.h"
37 #include "breakpoints.h"
38 #include "debug.h"
39 #include "btnpanel.h"
40 #include "stree.h"
41 #include "dconfig.h"
42 
43 #define NOTEBOOK_GROUP "notebook-438948394"
44 #define HPANED_BORDER_WIDTH 4
45 
46 #define CONNECT_PAGE_SIGNALS(X) \
47 	switch_left_handler_id = g_signal_connect(G_OBJECT(debug_notebook_left), "switch-page", G_CALLBACK(on_change_current_page), NULL); \
48 	switch_right_handler_id = g_signal_connect(G_OBJECT(debug_notebook_right), "switch-page", G_CALLBACK(on_change_current_page), NULL); \
49 	reorder_left_handler_id = g_signal_connect(G_OBJECT(debug_notebook_left), "page-reordered", G_CALLBACK(on_page_reordered), NULL); \
50 	reorder_right_handler_id = g_signal_connect(G_OBJECT(debug_notebook_right), "page-reordered", G_CALLBACK(on_page_reordered), NULL); \
51 	add_left_handler_id = g_signal_connect(G_OBJECT(debug_notebook_left), "page-added", G_CALLBACK(on_page_added), NULL); \
52 	add_right_handler_id = g_signal_connect(G_OBJECT(debug_notebook_right), "page-added", G_CALLBACK(on_page_added), NULL); \
53 	remove_left_handler_id = g_signal_connect(G_OBJECT(debug_notebook_left), "page-removed", G_CALLBACK(on_page_removed), NULL); \
54 	remove_right_handler_id = g_signal_connect(G_OBJECT(debug_notebook_right), "page-removed", G_CALLBACK(on_page_removed), NULL);
55 
56 #define DISCONNECT_PAGE_SIGNALS(X) \
57 	g_signal_handler_disconnect(G_OBJECT(debug_notebook_left), switch_left_handler_id); \
58 	g_signal_handler_disconnect(G_OBJECT(debug_notebook_right), switch_right_handler_id); \
59 	g_signal_handler_disconnect(G_OBJECT(debug_notebook_left), reorder_left_handler_id); \
60 	g_signal_handler_disconnect(G_OBJECT(debug_notebook_right), reorder_right_handler_id); \
61 	g_signal_handler_disconnect(G_OBJECT(debug_notebook_left), add_left_handler_id); \
62 	g_signal_handler_disconnect(G_OBJECT(debug_notebook_right), add_right_handler_id); \
63 	g_signal_handler_disconnect(G_OBJECT(debug_notebook_left), remove_left_handler_id); \
64 	g_signal_handler_disconnect(G_OBJECT(debug_notebook_right), remove_right_handler_id);
65 
66 #define CONNECT_ALLOCATED_PAGE_SIGNALS(X) \
67 	allocate_handler_id = g_signal_connect(G_OBJECT(hpaned), "size-allocate", G_CALLBACK(on_size_allocate), NULL);
68 
69 #define DISCONNECT_ALLOCATED_PAGE_SIGNALS(X) \
70 	g_signal_handler_disconnect(G_OBJECT(hpaned), allocate_handler_id); \
71 
72 /* pane */
73 static GtkWidget *hpaned = NULL;
74 
75 /* left and right notebooks */
76 static GtkWidget *debug_notebook_left = NULL;
77 static GtkWidget *debug_notebook_right = NULL;
78 
79 /* notebook signal handler ids */
80 static gulong allocate_handler_id;
81 static gulong switch_left_handler_id;
82 static gulong switch_right_handler_id;
83 static gulong reorder_left_handler_id;
84 static gulong reorder_right_handler_id;
85 static gulong add_left_handler_id;
86 static gulong add_right_handler_id;
87 static gulong remove_left_handler_id;
88 static gulong remove_right_handler_id;
89 
90 /*
91  *	first allocation handler to properly set paned position
92  */
on_size_allocate(GtkWidget * widget,GdkRectangle * allocation,gpointer user_data)93 static void on_size_allocate(GtkWidget *widget,GdkRectangle *allocation, gpointer   user_data)
94 {
95 	int position;
96 
97 	DISCONNECT_ALLOCATED_PAGE_SIGNALS();
98 
99 	position = (allocation->width - 2 * HPANED_BORDER_WIDTH) * 0.5;
100 	gtk_paned_set_position(GTK_PANED(hpaned), position);
101 }
102 
103 /*
104  *	page added event handler
105  */
on_page_added(GtkNotebook * notebook,GtkWidget * child,guint page_num,gpointer user_data)106 static void on_page_added(GtkNotebook *notebook, GtkWidget *child, guint page_num, gpointer user_data)
107 {
108 	gboolean is_left = (GTK_NOTEBOOK(debug_notebook_left) == notebook);
109 	gboolean is_tabbed = config_get_tabbed();
110 	int *tabs = NULL, *array, *new_tabs;
111 	gsize length;
112 	GtkWidget *page;
113 	tab_id id;
114 	int config_part;
115 
116 	if (!is_tabbed)
117 		tabs = config_get_tabs(&length);
118 	else if (is_left)
119 		tabs = config_get_left_tabs(&length);
120 	else
121 		tabs = config_get_right_tabs(&length);
122 
123 	array = g_malloc((length + 2) * sizeof(int));
124 	new_tabs = array + 1;
125 	memcpy(new_tabs, tabs, length * sizeof(int));
126 	memmove(new_tabs + page_num + 1, new_tabs + page_num, (length - page_num) * sizeof(int));
127 
128 	page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(is_left ? debug_notebook_left : debug_notebook_right), page_num);
129 	id = tabs_get_tab_id(page);
130 	new_tabs[page_num] = id;
131 
132 	if (!is_tabbed)
133 		config_part = CP_OT_TABS;
134 	else if (is_left)
135 		config_part = CP_TT_LTABS;
136 	else
137 		config_part = CP_TT_RTABS;
138 
139 	array[0] = length + 1;
140 	memcpy(array + 1, new_tabs, length + 1);
141 	config_set_panel(config_part, array, 0);
142 
143 	g_free(tabs);
144 	g_free(array);
145 }
146 
147 /*
148  *	page reordered event handler
149  */
on_page_reordered(GtkNotebook * notebook,GtkWidget * child,guint page_num,gpointer user_data)150 static void on_page_reordered(GtkNotebook *notebook, GtkWidget *child, guint page_num, gpointer user_data)
151 {
152 	gboolean is_left = (GTK_NOTEBOOK(debug_notebook_left) == notebook);
153 	gboolean is_tabbed = config_get_tabbed();
154 	int *tabs = NULL;
155 	gsize length;
156 	gsize prev_index, min, max;
157 	GtkWidget *page;
158 	tab_id id;
159 	int i;
160 	int config_part_tabs;
161 	int config_part_selected_index;
162 	int *array;
163 
164 	if (!is_tabbed)
165 		tabs = config_get_tabs(&length);
166 	else if (is_left)
167 		tabs = config_get_left_tabs(&length);
168 	else
169 		tabs = config_get_right_tabs(&length);
170 
171 	page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(is_left ? debug_notebook_left : debug_notebook_right), page_num);
172 	id = tabs_get_tab_id(page);
173 	for (prev_index = 0; prev_index < length; prev_index++)
174 	{
175 		if (id == tabs[prev_index])
176 		{
177 			break;
178 		}
179 	}
180 
181 	min = MIN(prev_index, page_num);
182 	max = MAX(prev_index, page_num);
183 	for (i = min; i < max; i++)
184 	{
185 		int tmp = tabs[i];
186 		tabs[i] = tabs[i + 1];
187 		tabs[i + 1] = tmp;
188 	}
189 
190 	if (!is_tabbed)
191 	{
192 		config_part_tabs = CP_OT_TABS;
193 		config_part_selected_index = CP_OT_SELECTED;
194 	}
195 	else if (is_left)
196 	{
197 		config_part_tabs = CP_TT_LTABS;
198 		config_part_selected_index = CP_TT_LSELECTED;
199 	}
200 	else
201 	{
202 		config_part_tabs = CP_TT_RTABS;
203 		config_part_selected_index = CP_TT_RSELECTED;
204 	}
205 
206 	array = g_malloc((length + 1) * sizeof(int));
207 	array[0] = length;
208 	memcpy(array + 1, tabs, length * sizeof(int));
209 
210 	config_set_panel(
211 		config_part_tabs, array,
212 		config_part_selected_index, &page_num,
213 		0
214 	);
215 
216 	g_free(tabs);
217 	g_free(array);
218 }
219 
220 /*
221  *	page removed event handler
222  */
on_page_removed(GtkNotebook * notebook,GtkWidget * child,guint page_num,gpointer user_data)223 static void on_page_removed(GtkNotebook *notebook, GtkWidget *child, guint page_num, gpointer user_data)
224 {
225 	gboolean is_left = (GTK_NOTEBOOK(debug_notebook_left) == notebook);
226 	gboolean is_tabbed = config_get_tabbed();
227 	int *tabs = NULL;
228 	gsize length;
229 	int config_part;
230 
231 	if (!is_tabbed)
232 		tabs = config_get_tabs(&length);
233 	else if (is_left)
234 		tabs = config_get_left_tabs(&length);
235 	else
236 		tabs = config_get_right_tabs(&length);
237 
238 	memmove(tabs + page_num, tabs + page_num + 1, (length - page_num - 1) * sizeof(int));
239 	memmove(tabs + 1, tabs, (length - 1) * sizeof(int));
240 	tabs[0] = length - 1;
241 
242 	if (!is_tabbed)
243 		config_part = CP_OT_TABS;
244 	else if (is_left)
245 		config_part = CP_TT_LTABS;
246 	else
247 		config_part = CP_TT_RTABS;
248 
249 	config_set_panel(config_part, tabs, 0);
250 
251 	g_free(tabs);
252 }
253 
254 /*
255  *	active page changed event handler
256  */
on_change_current_page(GtkNotebook * notebook,gpointer arg1,guint arg2,gpointer user_data)257 static gboolean on_change_current_page(GtkNotebook *notebook, gpointer arg1, guint arg2, gpointer user_data)
258 {
259 	gboolean is_left = (GTK_NOTEBOOK(debug_notebook_left) == notebook);
260 	gboolean is_tabbed = config_get_tabbed();
261 
262 	int config_part;
263 	if (!is_tabbed)
264 		config_part = CP_OT_SELECTED;
265 	else if (is_left)
266 		config_part = CP_TT_LSELECTED;
267 	else
268 		config_part = CP_TT_RSELECTED;
269 
270 	config_set_panel(config_part, (gpointer)&arg2, 0);
271 
272 	return TRUE;
273 }
274 
275 /*
276  *	create GUI and check config
277  */
dpaned_init(void)278 void dpaned_init(void)
279 {
280 	/* create paned */
281 	hpaned = gtk_hpaned_new();
282 
283 	/* create notebooks */
284 	debug_notebook_left = gtk_notebook_new();
285 	debug_notebook_right = gtk_notebook_new();
286 
287 	/* setup notebooks */
288 	gtk_notebook_set_scrollable(GTK_NOTEBOOK(debug_notebook_left), TRUE);
289 	gtk_notebook_set_scrollable(GTK_NOTEBOOK(debug_notebook_right), TRUE);
290 	gtk_notebook_set_group_name(GTK_NOTEBOOK(debug_notebook_left), NOTEBOOK_GROUP);
291 	gtk_notebook_set_group_name(GTK_NOTEBOOK(debug_notebook_right), NOTEBOOK_GROUP);
292 	gtk_notebook_set_tab_pos(GTK_NOTEBOOK(debug_notebook_left), GTK_POS_TOP);
293 	gtk_notebook_set_tab_pos(GTK_NOTEBOOK(debug_notebook_right), GTK_POS_TOP);
294 
295 	/* add notebooks */
296 	gtk_paned_add1(GTK_PANED(hpaned), debug_notebook_left);
297 	gtk_paned_add2(GTK_PANED(hpaned), debug_notebook_right);
298 
299 	if (config_get_tabbed())
300 	{
301 		gsize length;
302 		int *tab_ids, i;
303 
304 		/* left */
305 		tab_ids = config_get_left_tabs(&length);
306 		for (i = 0; i < length; i++)
307 		{
308 			GtkWidget *tab = tabs_get_tab((tab_id)tab_ids[i]);
309 			gtk_notebook_append_page(GTK_NOTEBOOK(debug_notebook_left),
310 				tab,
311 				gtk_label_new(tabs_get_label(tab_ids[i])));
312 			gtk_notebook_set_tab_detachable(GTK_NOTEBOOK(debug_notebook_left), tab, TRUE);
313 			gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(debug_notebook_left), tab, TRUE);
314 		}
315 		g_free(tab_ids);
316 
317 		/* right */
318 		tab_ids = config_get_right_tabs(&length);
319 		for (i = 0; i < length; i++)
320 		{
321 			GtkWidget *tab = tabs_get_tab((tab_id)tab_ids[i]);
322 			gtk_notebook_append_page(GTK_NOTEBOOK(debug_notebook_right),
323 				tab,
324 				gtk_label_new(tabs_get_label(tab_ids[i])));
325 			gtk_notebook_set_tab_detachable(GTK_NOTEBOOK(debug_notebook_right), tab, TRUE);
326 			gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(debug_notebook_right), tab, TRUE);
327 		}
328 		g_free(tab_ids);
329 
330 		gtk_widget_show_all(hpaned);
331 
332 		gtk_notebook_set_current_page(GTK_NOTEBOOK(debug_notebook_left), config_get_left_selected_tab_index());
333 		gtk_notebook_set_current_page(GTK_NOTEBOOK(debug_notebook_right),config_get_right_selected_tab_index());
334 	}
335 	else
336 	{
337 		gsize i, length;
338 		int *tab_ids;
339 
340 		g_object_ref(debug_notebook_right);
341 		gtk_container_remove(GTK_CONTAINER(hpaned), debug_notebook_right);
342 
343 		tab_ids = config_get_tabs(&length);
344 		for (i = 0; i < length; i++)
345 		{
346 			GtkWidget *tab = tabs_get_tab((tab_id)tab_ids[i]);
347 			gtk_notebook_append_page(GTK_NOTEBOOK(debug_notebook_left),
348 				tab,
349 				gtk_label_new(tabs_get_label(tab_ids[i])));
350 			gtk_notebook_set_tab_detachable(GTK_NOTEBOOK(debug_notebook_left), tab, TRUE);
351 			gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(debug_notebook_left), tab, TRUE);
352 		}
353 		g_free(tab_ids);
354 
355 		gtk_widget_show_all(hpaned);
356 		gtk_notebook_set_current_page(GTK_NOTEBOOK(debug_notebook_left), config_get_selected_tab_index());
357 	}
358 
359 	CONNECT_PAGE_SIGNALS();
360 	CONNECT_ALLOCATED_PAGE_SIGNALS();
361 }
362 
363 /*
364  *	free local data, disconnect signals
365  */
dpaned_destroy(void)366 void dpaned_destroy(void)
367 {
368 	DISCONNECT_PAGE_SIGNALS();
369 }
370 
371 /*
372  *	gets widget
373  */
dpaned_get_paned(void)374 GtkWidget* dpaned_get_paned(void)
375 {
376 	return hpaned;
377 }
378 
379 /*
380  *	sets tabs mode
381  */
dpaned_set_tabbed(gboolean tabbed)382 void dpaned_set_tabbed(gboolean tabbed)
383 {
384 	DISCONNECT_PAGE_SIGNALS();
385 
386 	if (!tabbed)
387 	{
388 		gsize length;
389 		int *tab_ids;
390 		int i;
391 
392 		g_object_ref(debug_notebook_right);
393 		gtk_container_remove(GTK_CONTAINER(hpaned), debug_notebook_right);
394 
395 		tab_ids = config_get_tabs(&length);
396 		for (i = 0; i < length; i++)
397 		{
398 			GtkWidget *tab = tabs_get_tab((tab_id)tab_ids[i]);
399 			if (-1 == gtk_notebook_page_num(GTK_NOTEBOOK(debug_notebook_left), tab))
400 			{
401 				g_object_ref(tab);
402 				gtk_container_remove(GTK_CONTAINER(debug_notebook_right), tab);
403 				gtk_notebook_insert_page(GTK_NOTEBOOK(debug_notebook_left), tab, gtk_label_new(tabs_get_label(tab_ids[i])), i);
404 				g_object_unref(tab);
405 				gtk_notebook_set_tab_detachable(GTK_NOTEBOOK(debug_notebook_left), tab, TRUE);
406 				gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(debug_notebook_left), tab, TRUE);
407 			}
408 		}
409 		g_free(tab_ids);
410 
411 		gtk_notebook_set_current_page(GTK_NOTEBOOK(debug_notebook_left), config_get_selected_tab_index());
412 
413 		gtk_widget_show_all(hpaned);
414 	}
415 	else
416 	{
417 		gsize length;
418 		int i;
419 		int *tab_ids;
420 
421 		gtk_paned_add2(GTK_PANED(hpaned), debug_notebook_right);
422 		g_object_unref(debug_notebook_right);
423 
424 		tab_ids = config_get_right_tabs(&length);
425 		for (i = 0; i < length; i++)
426 		{
427 			GtkWidget *tab = tabs_get_tab((tab_id)tab_ids[i]);
428 			g_object_ref(tab);
429 			gtk_container_remove(GTK_CONTAINER(debug_notebook_left), tab);
430 			gtk_notebook_insert_page(GTK_NOTEBOOK(debug_notebook_right), tab, gtk_label_new(tabs_get_label(tab_ids[i])), i);
431 			g_object_unref(tab);
432 			gtk_notebook_set_tab_detachable(GTK_NOTEBOOK(debug_notebook_right), tab, TRUE);
433 				gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(debug_notebook_right), tab, TRUE);
434 		}
435 		g_free(tab_ids);
436 
437 		gtk_notebook_set_current_page(GTK_NOTEBOOK(debug_notebook_left), config_get_left_selected_tab_index());
438 		gtk_notebook_set_current_page(GTK_NOTEBOOK(debug_notebook_right), config_get_right_selected_tab_index());
439 
440 		gtk_widget_show_all(hpaned);
441 	}
442 
443 	CONNECT_PAGE_SIGNALS();
444 
445 	config_set_panel(CP_TABBED_MODE, (gpointer)&tabbed, 0);
446 }
447