1 /*
2  *  views.c
3  *
4  *  Copyright 2012 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23 
24 #include <ctype.h>
25 #include <string.h>
26 #include <gdk/gdkkeysyms.h>
27 
28 #include "common.h"
29 
30 typedef enum _ViewContext
31 {
32 	VC_NONE,
33 	VC_DATA,
34 	VC_FRAME
35 } ViewContext;
36 
37 typedef struct _ViewInfo
38 {
39 	gboolean dirty;
40 	ViewContext context;
41 	void (*clear)(void);
42 	gboolean (*update)(void);
43 	gboolean flush;
44 	DebugState state;
45 } ViewInfo;
46 
47 static ViewInfo views[VIEW_COUNT] =
48 {
49 	{ FALSE, VC_NONE,  NULL,            NULL,             FALSE, 0 },
50 	{ FALSE, VC_NONE,  threads_clear,   threads_update,   FALSE, DS_SENDABLE },
51 	{ FALSE, VC_NONE,  breaks_clear,    breaks_update,    FALSE, DS_SENDABLE },
52 	{ FALSE, VC_DATA,  stack_clear,     stack_update,     TRUE,  DS_DEBUG },
53 	{ FALSE, VC_FRAME, locals_clear,    locals_update,    TRUE,  DS_DEBUG },
54 	{ FALSE, VC_FRAME, watches_clear,   watches_update,   FALSE, DS_VARIABLE },
55 	{ FALSE, VC_DATA,  memory_clear,    memory_update,    FALSE, DS_VARIABLE },
56 	{ FALSE, VC_NONE,  NULL,            dc_update,        FALSE, DS_DEBUG },
57 	{ FALSE, VC_FRAME, inspects_clear,  inspects_update,  FALSE, DS_VARIABLE },
58 	{ FALSE, VC_FRAME, registers_clear, registers_update, TRUE,  DS_DEBUG },
59 	{ FALSE, VC_DATA,  tooltip_clear,   tooltip_update,   FALSE, DS_SENDABLE },
60 	{ FALSE, VC_NONE,  menu_clear,      NULL,             FALSE, 0 }
61 };
62 
view_update_dirty(ViewIndex index,DebugState state)63 static void view_update_dirty(ViewIndex index, DebugState state)
64 {
65 	ViewInfo *view = views + index;
66 
67 	if (view->state & state)
68 	{
69 		if (view->update())
70 			view->dirty = FALSE;
71 	}
72 	else if (view->flush)
73 	{
74 		view->clear();
75 		view->dirty = FALSE;
76 	}
77 }
78 
view_update(ViewIndex index,DebugState state)79 static void view_update(ViewIndex index, DebugState state)
80 {
81 	if (views[index].dirty)
82 		view_update_dirty(index, state);
83 }
84 
85 static GtkNotebook *geany_sidebar;
86 static GtkWidget *inspect_page;
87 static GtkWidget *register_page;
88 
views_sidebar_update(gint page_num,DebugState state)89 static void views_sidebar_update(gint page_num, DebugState state)
90 {
91 	GtkWidget *page = gtk_notebook_get_nth_page(geany_sidebar, page_num);
92 
93 	if (page == inspect_page)
94 		view_update(VIEW_INSPECT, state);
95 	else if (page == register_page)
96 		view_update(VIEW_REGISTERS, state);
97 }
98 
view_dirty(ViewIndex index)99 void view_dirty(ViewIndex index)
100 {
101 	views[index].dirty = TRUE;
102 }
103 
views_context_dirty(DebugState state,gboolean frame_only)104 void views_context_dirty(DebugState state, gboolean frame_only)
105 {
106 	ViewIndex i;
107 
108 	for (i = 0; i < VIEW_COUNT; i++)
109 		if (views[i].context >= (frame_only ? VC_FRAME : VC_DATA))
110 			view_dirty(i);
111 
112 	if (state != DS_BUSY)
113 	{
114 		if (option_update_all_views)
115 			views_update(state);
116 		else
117 			views_sidebar_update(gtk_notebook_get_current_page(geany_sidebar), state);
118 	}
119 }
120 
121 #ifdef G_OS_UNIX
122 static ViewIndex view_current = VIEW_TERMINAL;
123 #else
124 static ViewIndex view_current = VIEW_THREADS;
125 #endif
126 
views_clear(void)127 void views_clear(void)
128 {
129 	ViewIndex i;
130 	ViewInfo *view = views;
131 
132 	for (i = 0; i < VIEW_COUNT; i++, view++)
133 	{
134 		view->dirty = FALSE;
135 
136 		if (view->clear)
137 			view->clear();
138 	}
139 }
140 
views_update(DebugState state)141 void views_update(DebugState state)
142 {
143 	if (option_update_all_views)
144 	{
145 		ViewIndex i;
146 		gboolean skip_frame = FALSE;
147 
148 		if (thread_state == THREAD_QUERY_FRAME)
149 		{
150 			if (!views[VIEW_THREADS].dirty)
151 				thread_query_frame('4');
152 
153 			thread_state = THREAD_STOPPED;
154 		}
155 
156 		for (i = 0; i < VIEW_COUNT; i++)
157 		{
158 			if (views[i].dirty && (!skip_frame || views[i].context != VC_FRAME))
159 			{
160 				view_update_dirty(i, state);
161 
162 				if (i == VIEW_STACK && thread_state >= THREAD_STOPPED)
163 					skip_frame = TRUE;
164 			}
165 		}
166 	}
167 	else
168 	{
169 		if (thread_state == THREAD_QUERY_FRAME)
170 		{
171 			if (view_current != VIEW_THREADS || !views[VIEW_THREADS].dirty)
172 				thread_query_frame('4');
173 
174 			thread_state = THREAD_STOPPED;
175 		}
176 
177 		view_update(view_current, state);
178 		view_update(VIEW_TOOLTIP, state);
179 		views_sidebar_update(gtk_notebook_get_current_page(geany_sidebar), state);
180 	}
181 }
182 
view_stack_update(void)183 gboolean view_stack_update(void)
184 {
185 	if (views[VIEW_STACK].dirty)
186 	{
187 		DebugState state = thread_state >= THREAD_STOPPED ? DS_DEBUG : DS_READY;
188 		view_update_dirty(VIEW_STACK, state);
189 		return state == DS_DEBUG;
190 	}
191 
192 	return FALSE;
193 }
194 
on_view_changed(G_GNUC_UNUSED GtkNotebook * notebook,G_GNUC_UNUSED gpointer page,gint page_num,G_GNUC_UNUSED gpointer gdata)195 void on_view_changed(G_GNUC_UNUSED GtkNotebook *notebook, G_GNUC_UNUSED gpointer page,
196 	gint page_num, G_GNUC_UNUSED gpointer gdata)
197 {
198 	view_current = page_num;
199 	view_update(view_current, debug_state());
200 }
201 
on_view_key_press(G_GNUC_UNUSED GtkWidget * widget,GdkEventKey * event,ViewSeeker seeker)202 gboolean on_view_key_press(G_GNUC_UNUSED GtkWidget *widget, GdkEventKey *event,
203 	ViewSeeker seeker)
204 {
205 	/* from msgwindow.c */
206 	gboolean enter_or_return = ui_is_keyval_enter_or_return(event->keyval);
207 
208 	if (enter_or_return || event->keyval == GDK_space || event->keyval == GDK_KP_Space)
209 		seeker(enter_or_return);
210 
211 	return FALSE;
212 }
213 
on_view_button_1_press(GtkWidget * widget,GdkEventButton * event,ViewSeeker seeker)214 gboolean on_view_button_1_press(GtkWidget *widget, GdkEventButton *event, ViewSeeker seeker)
215 {
216 	if (event->button == 1 && (pref_auto_view_source || event->type == GDK_2BUTTON_PRESS))
217 	{
218 		utils_handle_button_press(widget, event);
219 		seeker(event->type == GDK_2BUTTON_PRESS);
220 		return TRUE;
221 	}
222 
223 	return FALSE;
224 }
225 
on_view_query_base_tooltip(GtkWidget * widget,gint x,gint y,gboolean keyboard_tip,GtkTooltip * tooltip,GtkTreeViewColumn * base_name_column)226 gboolean on_view_query_base_tooltip(GtkWidget *widget, gint x, gint y, gboolean keyboard_tip,
227 	GtkTooltip *tooltip, GtkTreeViewColumn *base_name_column)
228 {
229 	GtkTreeView *tree = GTK_TREE_VIEW(widget);
230 	GtkTreeIter iter;
231 
232 	if (gtk_tree_view_get_tooltip_context(tree, &x, &y, keyboard_tip, NULL, NULL, &iter))
233 	{
234 		const char *file;
235 
236 		gtk_tree_view_set_tooltip_cell(tree, tooltip, NULL, base_name_column, NULL);
237 		scp_tree_store_get((ScpTreeStore *) gtk_tree_view_get_model(tree), &iter,
238 			COLUMN_FILE, &file, -1);
239 
240 		if (file)
241 		{
242 			gchar *utf8 = utils_get_utf8_from_locale(file);
243 
244 			gtk_tooltip_set_text(tooltip, utf8);
245 			g_free(utf8);
246 			return TRUE;
247 		}
248 	}
249 
250 	return FALSE;
251 }
252 
on_view_editable_map(GtkWidget * widget,gchar * replace)253 gboolean on_view_editable_map(GtkWidget *widget, gchar *replace)
254 {
255 	iff (GTK_IS_EDITABLE(widget), "cell editable: not an editable")
256 	{
257 		gint position = 0;
258 		GtkEditable *editable = GTK_EDITABLE(widget);
259 
260 		gtk_editable_delete_text(editable, 0, -1);
261 		gtk_editable_insert_text(editable, replace ? replace : "", -1, &position);
262 		gtk_editable_select_region(editable, -1, 0);
263 		g_free(replace);
264 	}
265 
266 	return FALSE;
267 }
268 
view_create(const char * name,ScpTreeStore ** store,GtkTreeSelection ** selection)269 GtkTreeView *view_create(const char *name, ScpTreeStore **store, GtkTreeSelection **selection)
270 {
271 	GtkTreeView *tree = GTK_TREE_VIEW(get_widget(name));
272 
273 	*store = SCP_TREE_STORE(gtk_tree_view_get_model(tree));
274 	*selection = gtk_tree_view_get_selection(tree);
275 	return tree;
276 }
277 
on_editing_started(G_GNUC_UNUSED GtkCellRenderer * cell,GtkCellEditable * editable,G_GNUC_UNUSED const gchar * path,GtkAdjustment * hadjustment)278 static void on_editing_started(G_GNUC_UNUSED GtkCellRenderer *cell, GtkCellEditable *editable,
279 	G_GNUC_UNUSED const gchar *path, GtkAdjustment *hadjustment)
280 {
281 	if (GTK_IS_ENTRY(editable))
282 		gtk_entry_set_cursor_hadjustment(GTK_ENTRY(editable), hadjustment);
283 }
284 
on_display_editing_started(G_GNUC_UNUSED GtkCellRenderer * cell,GtkCellEditable * editable,const gchar * path_str,ScpTreeStore * store)285 static void on_display_editing_started(G_GNUC_UNUSED GtkCellRenderer *cell,
286 	GtkCellEditable *editable, const gchar *path_str, ScpTreeStore *store)
287 {
288 	GtkTreeIter iter;
289 	const char *value;
290 	gint hb_mode;
291 
292 	scp_tree_store_get_iter_from_string(store, &iter, path_str);
293 	scp_tree_store_get(store, &iter, COLUMN_VALUE, &value, COLUMN_HB_MODE, &hb_mode, -1);
294 	/* scrolling editable to the proper position is left as an exercise for the reader */
295 	g_signal_connect(editable, "map", G_CALLBACK(on_view_editable_map),
296 		parse_get_display_from_7bit(value, hb_mode, MR_EDITVC));
297 }
298 
view_connect(const char * name,ScpTreeStore ** store,GtkTreeSelection ** selection,const TreeCell * cell_info,const char * window,GObject ** display_cell)299 GtkTreeView *view_connect(const char *name, ScpTreeStore **store, GtkTreeSelection **selection,
300 	const TreeCell *cell_info, const char *window, GObject **display_cell)
301 {
302 	guint i;
303 	GtkScrolledWindow *scrolled = GTK_SCROLLED_WINDOW(get_widget(window));
304 	GtkAdjustment *hadjustment = gtk_scrolled_window_get_hadjustment(scrolled);
305 	GtkTreeView *tree = view_create(name, store, selection);
306 
307 	for (i = 0; cell_info->name; cell_info++, i++)
308 	{
309 		GtkCellRenderer *cell = GTK_CELL_RENDERER(get_object(cell_info->name));
310 		const char *signame;
311 		const char *property;
312 
313 		if (GTK_IS_CELL_RENDERER_TEXT(cell))
314 		{
315 			signame = "edited";
316 			property = "editable";
317 
318 			g_signal_connect(cell, "editing-started", G_CALLBACK(on_editing_started),
319 				hadjustment);
320 
321 			if (display_cell && i == 0)
322 			{
323 				g_signal_connect(cell, "editing-started",
324 					G_CALLBACK(on_display_editing_started), *store);
325 				*display_cell = G_OBJECT(cell);
326 			}
327 		}
328 		else
329 		{
330 			g_assert(GTK_IS_CELL_RENDERER_TOGGLE(cell));
331 			signame = "toggled";
332 			property = "activatable";
333 		}
334 
335 		g_signal_connect(cell, signame, cell_info->callback, GINT_TO_POINTER(i));
336 		g_object_set(cell, property, TRUE, NULL);
337 	}
338 
339 	return tree;
340 }
341 
view_line_cell_data_func(G_GNUC_UNUSED GtkTreeViewColumn * column,GtkCellRenderer * cell,GtkTreeModel * model,GtkTreeIter * iter,gpointer gdata)342 static void view_line_cell_data_func(G_GNUC_UNUSED GtkTreeViewColumn *column,
343 	GtkCellRenderer *cell, GtkTreeModel *model, GtkTreeIter *iter, gpointer gdata)
344 {
345 	gint line;
346 	gchar *s;
347 
348 	gtk_tree_model_get(model, iter, GPOINTER_TO_INT(gdata), &line, -1);
349 	s = line ? g_strdup_printf("%d", line) : NULL;
350 	g_object_set(cell, "text", s, NULL);
351 	g_free(s);
352 }
353 
view_set_line_data_func(const char * column,const char * cell,gint column_id)354 void view_set_line_data_func(const char *column, const char *cell, gint column_id)
355 {
356 	gtk_tree_view_column_set_cell_data_func(get_column(column),
357 		GTK_CELL_RENDERER(get_object(cell)), view_line_cell_data_func,
358 		GINT_TO_POINTER(column_id), NULL);
359 }
360 
view_display_edited(ScpTreeStore * store,gboolean condition,const gchar * path_str,const char * format,gchar * new_text)361 void view_display_edited(ScpTreeStore *store, gboolean condition, const gchar *path_str,
362 	const char *format, gchar *new_text)
363 {
364 	if (validate_column(new_text, TRUE))
365 	{
366 		if (condition)
367 		{
368 			GtkTreeIter iter;
369 			const char *name;
370 			gint hb_mode;
371 			char *locale;
372 
373 			scp_tree_store_get_iter_from_string(store, &iter, path_str);
374 			scp_tree_store_get(store, &iter, COLUMN_NAME, &name, COLUMN_HB_MODE,
375 				&hb_mode, -1);
376 			locale = utils_get_locale_from_display(new_text, hb_mode);
377 			utils_strchrepl(locale, '\n', ' ');
378 			debug_send_format(F, format, name, locale);
379 			g_free(locale);
380 		}
381 		else
382 			plugin_blink();
383 	}
384 }
385 
view_column_set_visible(const char * name,gboolean visible)386 void view_column_set_visible(const char *name, gboolean visible)
387 {
388 	gtk_tree_view_column_set_visible(get_column(name), visible);
389 }
390 
view_seek_selected(GtkTreeSelection * selection,gboolean focus,SeekerType seeker)391 void view_seek_selected(GtkTreeSelection *selection, gboolean focus, SeekerType seeker)
392 {
393 	ScpTreeStore *store;
394 	GtkTreeIter iter;
395 
396 	if (scp_tree_selection_get_selected(selection, &store, &iter))
397 	{
398 		const char *file;
399 		gint line;
400 
401 		scp_tree_store_get(store, &iter, COLUMN_FILE, &file, COLUMN_LINE, &line, -1);
402 
403 		if (file)
404 			utils_seek(file, line, focus, seeker);
405 	}
406 }
407 
408 enum
409 {
410 	COMMAND_DISPLAY,
411 	COMMAND_TEXT,
412 	COMMAND_LOCALE
413 };
414 
415 static GtkWidget *command_dialog;
416 static GtkWidget *command_view;
417 static GObject *command_cell;
418 static GtkTextBuffer *command_text;
419 static GtkComboBox *command_history;
420 static ScpTreeStore *command_store;
421 static GtkToggleButton *command_locale;
422 static GtkWidget *command_send;
423 
on_command_text_changed(GtkTextBuffer * command_text,G_GNUC_UNUSED gpointer gdata)424 static void on_command_text_changed(GtkTextBuffer *command_text, G_GNUC_UNUSED gpointer gdata)
425 {
426 	gchar *text = utils_text_buffer_get_text(command_text, -1);
427 	const gchar *start = utils_skip_spaces(text);
428 
429 	gtk_widget_set_sensitive(command_send, *start != '0' || !isdigit(start[1]));
430 	g_free(text);
431 }
432 
on_command_dialog_configure(G_GNUC_UNUSED GtkWidget * widget,G_GNUC_UNUSED GdkEventButton * event,G_GNUC_UNUSED gpointer gdata)433 gboolean on_command_dialog_configure(G_GNUC_UNUSED GtkWidget *widget,
434 	G_GNUC_UNUSED GdkEventButton *event, G_GNUC_UNUSED gpointer gdata)
435 {
436 	gint width;
437 
438 #if GTK_CHECK_VERSION(2, 24, 0)
439 	width = gdk_window_get_width(gtk_widget_get_window(command_view));
440 #else
441 	gint height;
442 	gdk_drawable_get_size(GDK_DRAWABLE(command_view->window), &width, &height);
443 #endif
444 	g_object_set(command_cell, "wrap-width", width, NULL);
445 	return FALSE;
446 }
447 
on_command_history_size_request(G_GNUC_UNUSED GtkWidget * widget,GtkRequisition * requisition,G_GNUC_UNUSED gpointer gdata)448 static void on_command_history_size_request(G_GNUC_UNUSED GtkWidget *widget,
449 	GtkRequisition *requisition, G_GNUC_UNUSED gpointer gdata)
450 {
451 	static gint empty_height;
452 	GtkTreeIter iter;
453 
454 	if (scp_tree_store_get_iter_first(command_store, &iter))
455 		requisition->height = empty_height;
456 	else
457 		empty_height = requisition->height;
458 }
459 
on_command_history_changed(GtkComboBox * command_history,G_GNUC_UNUSED gpointer gdata)460 static void on_command_history_changed(GtkComboBox *command_history,
461 	G_GNUC_UNUSED gpointer gdata)
462 {
463 	GtkTreeIter iter;
464 
465 	if (gtk_combo_box_get_active_iter(command_history, &iter))
466 	{
467 		const gchar *text;
468 		gboolean locale;
469 
470 		scp_tree_store_get(command_store, &iter, COMMAND_TEXT, &text, COMMAND_LOCALE,
471 			&locale, -1);
472 		gtk_text_buffer_set_text(command_text, text, -1);
473 		gtk_toggle_button_set_active(command_locale, locale);
474 		gtk_widget_grab_focus(command_view);
475 		gtk_combo_box_set_active_iter(command_history, NULL);
476 	}
477 }
478 
on_command_insert_button_clicked(G_GNUC_UNUSED GtkButton * button,gpointer gdata)479 static void on_command_insert_button_clicked(G_GNUC_UNUSED GtkButton *button, gpointer gdata)
480 {
481 	const char *prefix;
482 	const char *id;
483 	GString *text = g_string_new("--");
484 
485 	switch (GPOINTER_TO_INT(gdata))
486 	{
487 		case 't' : prefix = "thread"; id = thread_id; break;
488 		case 'g' : prefix = "group"; id = thread_group_id(); break;
489 		default : prefix = "frame"; id = frame_id;
490 	}
491 
492 	g_string_append_printf(text, "%s ", prefix);
493 	if (id)
494 		g_string_append_printf(text, "%s ", id);
495 
496 	gtk_text_buffer_delete_selection(command_text, FALSE, TRUE);
497 	gtk_text_buffer_insert_at_cursor(command_text, text->str, -1);
498 	g_string_free(text, TRUE);
499 	gtk_widget_grab_focus(command_view);
500 }
501 
on_command_send_button_clicked(G_GNUC_UNUSED GtkButton * button,G_GNUC_UNUSED gpointer gdata)502 static void on_command_send_button_clicked(G_GNUC_UNUSED GtkButton *button,
503 	G_GNUC_UNUSED gpointer gdata)
504 {
505 	gchar *text = utils_text_buffer_get_text(command_text, -1);
506 	const gchar *start;
507 	char *locale;
508 
509 	thread_synchronize();
510 	utils_strchrepl(text, '\n', ' ');
511 	start = utils_skip_spaces(text);
512 	locale = gtk_toggle_button_get_active(command_locale) ?
513 		utils_get_locale_from_utf8(start) : g_strdup(start);
514 	debug_send_command(N, locale);
515 	g_free(locale);
516 	gtk_text_buffer_set_text(command_text, "", -1);
517 	gtk_widget_hide(command_dialog);
518 
519 	if (*start)
520 	{
521 		GtkTreePath *path;
522 		GtkTreeIter iter;
523 		gchar *display = g_strdup(start);
524 
525 		/* from ui_combo_box_add_to_history() */
526 		if (store_find(command_store, &iter, COMMAND_TEXT, start))
527 			scp_tree_store_remove(command_store, &iter);
528 
529 		if (strlen(display) >= 273)
530 			strcpy(display + 270, _("\342\200\246"));  /* For translators: ellipsis */
531 
532 		scp_tree_store_prepend(command_store, &iter, NULL);
533 		scp_tree_store_set(command_store, &iter, COMMAND_DISPLAY, display, COMMAND_TEXT,
534 			start, COMMAND_LOCALE, gtk_toggle_button_get_active(command_locale), -1);
535 		g_free(display);
536 
537 		path = gtk_tree_path_new_from_indices(15, -1);
538 		if (scp_tree_store_get_iter(command_store, &iter, path))
539 			scp_tree_store_remove(command_store, &iter);
540 		gtk_tree_path_free(path);
541 	}
542 
543 	g_free(text);
544 }
545 
command_line_update_state(DebugState state)546 static void command_line_update_state(DebugState state)
547 {
548 	if (state == DS_INACTIVE)
549 		gtk_widget_hide(command_dialog);
550 	else
551 	{
552 		gtk_button_set_label(GTK_BUTTON(command_send),
553 			state & DS_SENDABLE ? _("_Send") : _("_Busy"));
554 	}
555 }
556 
view_command_line(const gchar * text,const gchar * title,const gchar * seek,gboolean seek_after)557 void view_command_line(const gchar *text, const gchar *title, const gchar *seek,
558 	gboolean seek_after)
559 {
560 	GtkTextIter start, end;
561 
562 	gtk_window_set_title(GTK_WINDOW(command_dialog), title ? title : _("GDB Command"));
563 	gtk_widget_grab_focus(command_view);
564 
565 	if (text)
566 	{
567 		const gchar *pos = seek ? strstr(text, seek) : NULL;
568 		GtkTextIter iter;
569 
570 		gtk_text_buffer_set_text(command_text, text, -1);
571 		gtk_text_buffer_get_iter_at_offset(command_text, &iter,
572 			g_utf8_strlen(text, pos ? pos + strlen(seek) * seek_after - text : -1));
573 		gtk_text_buffer_place_cursor(command_text, &iter);
574 	}
575 	else
576 	{
577 		gtk_text_buffer_get_start_iter(command_text, &start);
578 		gtk_text_buffer_get_end_iter(command_text, &end);
579 		gtk_text_buffer_select_range(command_text, &start, &end);
580 	}
581 
582 	on_command_text_changed(command_text, NULL);
583 	command_line_update_state(debug_state());
584 	gtk_combo_box_set_active_iter(command_history, NULL);
585 	gtk_dialog_run(GTK_DIALOG(command_dialog));
586 }
587 
view_command_active(void)588 gboolean view_command_active(void)
589 {
590 	return gtk_widget_get_visible(command_dialog);
591 }
592 
views_update_state(DebugState state)593 void views_update_state(DebugState state)
594 {
595 	static DebugState last_state = 0;
596 
597 	if (state != last_state)
598 	{
599 		if (gtk_widget_get_visible(command_dialog))
600 			command_line_update_state(state);
601 		locals_update_state(state);
602 		watches_update_state(state);
603 		inspects_update_state(state);
604 		last_state = state;
605 	}
606 }
607 
on_geany_sidebar_switch_page(G_GNUC_UNUSED GtkNotebook * notebook,G_GNUC_UNUSED gpointer page,gint page_num,G_GNUC_UNUSED gpointer gdata)608 static void on_geany_sidebar_switch_page(G_GNUC_UNUSED GtkNotebook *notebook,
609 	G_GNUC_UNUSED gpointer page, gint page_num, G_GNUC_UNUSED gpointer gdata)
610 {
611 	views_sidebar_update(page_num, debug_state());
612 }
613 
614 static gulong switch_sidebar_page_id;
615 
views_init(void)616 void views_init(void)
617 {
618 	if (pref_var_update_bug)
619 		views[VIEW_INSPECT].state = DS_DEBUG;
620 
621 	command_dialog = dialog_connect("command_dialog");
622 	command_view = get_widget("command_view");
623 	command_text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(command_view));
624 	g_signal_connect(command_text, "changed", G_CALLBACK(on_command_text_changed), NULL);
625 	command_history = GTK_COMBO_BOX(get_widget("command_history"));
626 	command_store = SCP_TREE_STORE(gtk_combo_box_get_model(command_history));
627 	command_cell = get_object("command_cell");
628 	g_signal_connect(command_dialog, "configure-event",
629 		G_CALLBACK(on_command_dialog_configure), NULL);
630 	g_signal_connect(command_history, "size-request",
631 		G_CALLBACK(on_command_history_size_request), NULL);
632 	g_signal_connect(command_history, "changed", G_CALLBACK(on_command_history_changed),
633 		NULL);
634 	command_locale = GTK_TOGGLE_BUTTON(get_widget("command_locale"));
635 
636 	g_signal_connect(get_widget("command_thread"), "clicked",
637 		G_CALLBACK(on_command_insert_button_clicked), GINT_TO_POINTER('t'));
638 	g_signal_connect(get_widget("command_group"), "clicked",
639 		G_CALLBACK(on_command_insert_button_clicked), GINT_TO_POINTER('g'));
640 	g_signal_connect(get_widget("command_frame"), "clicked",
641 		G_CALLBACK(on_command_insert_button_clicked), GINT_TO_POINTER('f'));
642 	command_send = get_widget("command_send");
643 	gtk_widget_grab_default(command_send);
644 	g_signal_connect(command_send, "clicked", G_CALLBACK(on_command_send_button_clicked),
645 		NULL);
646 	utils_enter_to_clicked(command_view, command_send);
647 
648 	geany_sidebar = GTK_NOTEBOOK(geany->main_widgets->sidebar_notebook);
649 	switch_sidebar_page_id = g_signal_connect(geany_sidebar, "switch-page",
650 		G_CALLBACK(on_geany_sidebar_switch_page), NULL);
651 	inspect_page = get_widget("inspect_page");
652 	gtk_notebook_append_page(geany_sidebar, inspect_page, get_widget("inspect_label"));
653 	register_page = get_widget("register_page");
654 	gtk_notebook_append_page(geany_sidebar, register_page, get_widget("register_label"));
655 }
656 
views_finalize(void)657 void views_finalize(void)
658 {
659 	g_signal_handler_disconnect(geany_sidebar, switch_sidebar_page_id);
660 	gtk_widget_destroy(GTK_WIDGET(command_dialog));
661 	gtk_widget_destroy(inspect_page);
662 	gtk_widget_destroy(register_page);
663 }
664