Set up current working directory on File->Open
[gscope.git] / sourceview.c
blob46a09ef468e5a47929fe82c229a81f9a6a9dfef9
1 /*
2 * (c) 2010 Cyrill Gorcunov, gorcunov@gmail.com
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 2 of the License, or (at
7 * your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
18 * source view handling
21 #include <unistd.h>
22 #include <stdio.h>
23 #include <stddef.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <limits.h>
29 #include <sys/param.h>
30 #include <sys/types.h>
31 #include <unistd.h>
33 #include <gtk/gtk.h>
34 #include <gdk/gdk.h>
35 #include <gdk/gdkkeysyms.h>
36 #include <gio/gio.h>
38 #include "sourceview.h"
39 #include "tags.h"
41 #include "util.h"
42 #include "list.h"
44 #include "queries.h"
45 #include "view.h"
47 enum {
48 COL_NAME,
49 COL_LINE,
50 COL_TYPE,
51 COL_INDEX,
52 NUM_COLS
55 void source_view_scroll_to_line(struct source_view *sv, unsigned long line)
57 if (!line)
58 line = 1;
61 * for a caller @line is starting from 1 but in turn
62 * internally they are counted from 0 in widget
64 gtk_text_buffer_get_iter_at_line(sv->text_buffer, &sv->text_iter_start, line - 1);
65 gtk_text_buffer_get_iter_at_line(sv->text_buffer, &sv->text_iter_end, line);
67 gtk_text_buffer_select_range(sv->text_buffer, &sv->text_iter_start, &sv->text_iter_end);
69 while(gtk_events_pending())
70 gtk_main_iteration();
72 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(sv->text_widget), &sv->text_iter_start, 0.25, FALSE, 0, 0);
75 static int source_view_current_line(struct source_view *sv)
77 gtk_text_buffer_get_selection_bounds(sv->text_buffer,
78 &sv->text_iter_start, &sv->text_iter_end);
79 return gtk_text_iter_get_line(&sv->text_iter_start);
82 static struct tag *tag_from_row(GtkTreeView *view, GtkTreePath *path, struct list_head *tags)
84 GtkTreeModel *model;
85 GtkTreeIter iter;
86 struct tag *t = NULL;
88 model = gtk_tree_view_get_model(view);
89 if (gtk_tree_model_get_iter(model, &iter, path)) {
90 unsigned long key;
91 gtk_tree_model_get(model, &iter, COL_INDEX, &key, -1);
92 t = tag_key_lookup(tags, key);
93 /* FIXME: free or unreference */
96 return t;
99 static gboolean on_tagsTree_button_press_event(GtkWidget *widget,
100 GdkEventButton *event, gpointer data)
102 struct source_view *sv = (struct source_view *)data;
103 GtkTreeViewColumn *focus_column = NULL;
104 GtkTreePath *path = NULL;
106 if (event->button != 1 || event->type != GDK_2BUTTON_PRESS)
107 return FALSE;
109 gtk_tree_view_get_cursor(GTK_TREE_VIEW(widget), &path, &focus_column);
110 if (path) {
111 struct list_head *list = sv->etags;
112 struct tag *t = tag_from_row(GTK_TREE_VIEW(widget), path, list);
113 if (t)
114 source_view_scroll_to_line(sv, t->line);
115 gtk_tree_path_free(path);
118 return FALSE;
121 static void source_view_delete_hook(GtkWidget *child, void *private)
123 struct source_view *sv = private;
124 if (!sv)
125 return;
127 gtk_list_store_clear(sv->etags_store);
129 gtk_widget_destroy(sv->text_widget);
130 gtk_widget_destroy(sv->etags_tree_widget);
131 gtk_widget_destroy(sv->me);
133 g_free(sv->text_contents);
134 tags_free(sv->etags);
135 free(sv->file_path);
136 free(sv);
139 static void __cb_popup_cscope_query(struct source_view *sv, int type, int nocase)
141 gchar *str;
142 if (gtk_text_buffer_get_selection_bounds(sv->text_buffer,
143 &sv->text_iter_start, &sv->text_iter_end)) {
144 str = gtk_text_buffer_get_slice(sv->text_buffer,
145 &sv->text_iter_start, &sv->text_iter_end,
146 FALSE);
147 str = skip_spaces(str);
148 if (!str || !str[0])
149 return;
151 do_cscope_query(&gscope_info, str, type, nocase);
155 static void cp_popup_cscope_calling(gpointer user_data, void *dummy)
157 struct source_view *sv = (struct source_view *)user_data;
158 __cb_popup_cscope_query(sv, CSCOPE_KEY_QRY_CALLING_FUNC, 0);
161 static void cp_popup_cscope_called(gpointer user_data, void *dummy)
163 struct source_view *sv = (struct source_view *)user_data;
164 __cb_popup_cscope_query(sv, CSCOPE_KEY_QRY_CALLED_FUNC, 0);
167 static void cp_popup_cscope_ref(gpointer user_data, void *dummy)
169 struct source_view *sv = (struct source_view *)user_data;
170 __cb_popup_cscope_query(sv, CSCOPE_KEY_QRY_REFERENCES, 0);
173 static void cp_popup_cscope_def(gpointer user_data, void *dummy)
175 struct source_view *sv = (struct source_view *)user_data;
176 __cb_popup_cscope_query(sv, CSCOPE_KEY_QRY_DEFINITION, 0);
179 static void cb_populate_popup(GtkTextView *entry, GtkMenu *menu, gpointer user_data)
181 GtkWidget *mnuSep = gtk_separator_menu_item_new();
182 GtkWidget *mnuCScope_Def = gtk_menu_item_new_with_label("Definition");
183 GtkWidget *mnuCScope_Ref = gtk_menu_item_new_with_label("References");
184 GtkWidget *mnuCScope_Called = gtk_menu_item_new_with_label("Called By");
185 GtkWidget *mnuCScope_Calling = gtk_menu_item_new_with_label("Calling");
187 #define popup_push(mnu, callback) \
188 do { \
189 gtk_menu_shell_insert(GTK_MENU_SHELL(menu), mnu, 0); \
190 gtk_widget_show(mnu); \
191 g_signal_connect_swapped(GTK_OBJECT(mnu), "activate", \
192 G_CALLBACK(callback), (gpointer)user_data); \
193 } while (0)
195 gtk_menu_shell_insert(GTK_MENU_SHELL(menu), mnuSep, 0);
196 gtk_widget_show(mnuSep);
198 popup_push(mnuCScope_Calling, cp_popup_cscope_calling);
199 popup_push(mnuCScope_Called, cp_popup_cscope_called);
200 popup_push(mnuCScope_Ref, cp_popup_cscope_ref);
201 popup_push(mnuCScope_Def, cp_popup_cscope_def);
204 static gboolean cb_press_key(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
206 struct source_view *sv = (struct source_view *)user_data;
208 //debug("event->state: %x event->keyval: %x", event->state, event->keyval);
210 // GDK_Control_L, GDK_bracketleft
212 switch (event->keyval) {
213 case GDK_bracketleft:
214 if (!(event->state & GDK_CONTROL_MASK))
215 break;
216 break;
217 case GDK_bracketright:
218 if (!(event->state & GDK_CONTROL_MASK))
219 break;
220 break;
221 case GDK_W:
222 case GDK_w:
223 if (event->state & GDK_CONTROL_MASK) {
224 struct notebook_page *page;
225 page = notebook_page_find(gscope_info.notebook_source, sv->me);
226 if (page)
227 g_signal_emit_by_name(page->title.close_button, "clicked");
228 /* note after this point there is no user-data anymore */
230 break;
231 case GDK_E:
232 case GDK_e:
234 char *argv[5];
235 char line[16];
236 int pout;
237 GError *error = NULL;
239 if (!(event->state &
240 (GDK_CONTROL_MASK | GDK_SHIFT_MASK)))
241 break;
243 if (event->state & GDK_SHIFT_MASK) {
244 argv[0] = (char *)"gvim";
245 argv[1] = sv->file_path;
246 argv[2] = line;
247 argv[3] = NULL;
248 } else {
249 argv[0] = (char *)"gvim";
250 argv[1] = (char *)"-R";
251 argv[2] = sv->file_path;
252 argv[3] = line;
253 argv[4] = NULL;
256 snprintf(line, sizeof(line), "+%d",
257 source_view_current_line(sv) + 1);
259 g_spawn_async_with_pipes(NULL, argv, NULL,
260 G_SPAWN_SEARCH_PATH,
261 NULL, NULL, NULL,
262 NULL, &pout, NULL, &error);
263 return TRUE;
265 break;
267 return FALSE;
271 * creates tags tree and textbox (both filled
272 * owb way by a content from file @path) and
273 * embed them into container
275 struct source_view *source_view_create(char *path)
277 GtkWidget *scroll_tree;
278 GtkWidget *scroll_text;
279 PangoFontDescription *font_desc;
280 GtkCellRenderer *renderer;
281 GtkTreeViewColumn *column;
282 GtkTreeModel *model;
283 GtkTreeIter iter;
284 GError *err;
286 struct tag *t;
287 size_t size;
289 struct source_view *sv = xzalloc(sizeof(*sv));
292 * parse all things first
294 sv->etags = tag_parse_file(path);
295 if (!sv->etags)
296 goto out_free;
297 sv->file_path = strdup(path);
298 sv->file_name = g_path_get_basename(sv->file_path);
299 if (!sv->file_path)
300 goto out_free_dups;
302 if (!g_file_get_contents(path, &sv->text_contents, &size, &err))
303 goto out_free_etags;
306 * Now GUI things
308 sv->me = gtk_hpaned_new();
310 scroll_tree = gtk_scrolled_window_new(NULL, NULL);
311 scroll_text = gtk_scrolled_window_new(NULL, NULL);
313 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_tree),
314 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
315 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_text),
316 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
319 * etags Tree
321 sv->etags_tree_widget = gtk_tree_view_new();
322 gtk_container_add(GTK_CONTAINER(scroll_tree), sv->etags_tree_widget);
323 gtk_widget_set_size_request(scroll_tree, 20, -1);
325 renderer = gtk_cell_renderer_text_new();
326 column = gtk_tree_view_column_new_with_attributes("Name",
327 renderer, "text", COL_NAME, NULL);
328 gtk_tree_view_column_set_resizable(column, TRUE);
329 gtk_tree_view_column_set_sort_column_id(column, COL_NAME);
330 gtk_tree_view_append_column(GTK_TREE_VIEW(sv->etags_tree_widget), column);
332 renderer = gtk_cell_renderer_text_new();
333 column = gtk_tree_view_column_new_with_attributes("Line",
334 renderer, "text", COL_LINE, NULL);
335 gtk_tree_view_column_set_resizable(column, TRUE);
336 gtk_tree_view_column_set_sort_column_id(column, COL_LINE);
337 gtk_tree_view_append_column(GTK_TREE_VIEW(sv->etags_tree_widget), column);
339 renderer = gtk_cell_renderer_text_new();
340 column = gtk_tree_view_column_new_with_attributes("Type",
341 renderer, "text", COL_TYPE, NULL);
342 gtk_tree_view_column_set_resizable(column, TRUE);
343 gtk_tree_view_column_set_sort_column_id(column, COL_TYPE);
344 gtk_tree_view_append_column(GTK_TREE_VIEW(sv->etags_tree_widget), column);
346 sv->etags_store = gtk_list_store_new(NUM_COLS,
347 G_TYPE_STRING, G_TYPE_UINT,
348 G_TYPE_STRING, G_TYPE_ULONG);
350 list_for_each_entry(t, sv->etags, list) {
351 gtk_list_store_append(sv->etags_store, &iter);
352 gtk_list_store_set(sv->etags_store, &iter,
353 COL_NAME, t->name,
354 COL_LINE, t->line,
355 COL_TYPE, t->symtype,
356 COL_INDEX,t->key,
357 -1);
360 model = GTK_TREE_MODEL(sv->etags_store);
361 gtk_tree_view_set_model(GTK_TREE_VIEW(sv->etags_tree_widget), model);
363 /* there is own reference anyway */
364 g_object_unref(model);
366 /* bind events */
367 g_signal_connect(G_OBJECT(sv->etags_tree_widget), "button_press_event",
368 G_CALLBACK(on_tagsTree_button_press_event),
369 (gpointer)sv);
372 * Text widget
374 sv->text_widget = gtk_text_view_new();
375 gtk_container_add(GTK_CONTAINER(scroll_text), sv->text_widget);
377 //font_desc = pango_font_description_from_string("monospace 10");
378 font_desc = pango_font_description_from_string("Droid Sans Mono 11");
379 gtk_widget_modify_font(sv->text_widget, font_desc);
380 pango_font_description_free(font_desc);
381 gtk_text_view_set_editable(GTK_TEXT_VIEW(sv->text_widget), FALSE);
382 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(sv->text_widget), 5);
383 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(sv->text_widget), 5);
385 sv->text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(sv->text_widget));
386 gtk_text_buffer_set_text(sv->text_buffer, sv->text_contents, -1);
388 gtk_text_buffer_get_iter_at_line(sv->text_buffer, &sv->text_iter_start, 1);
389 gtk_text_buffer_get_iter_at_line(sv->text_buffer, &sv->text_iter_end, 1);
391 gtk_paned_pack1(GTK_PANED(sv->me), scroll_tree, TRUE, FALSE);
392 gtk_paned_pack2(GTK_PANED(sv->me), scroll_text, TRUE, FALSE);
395 * The popup we need
397 g_signal_connect(G_OBJECT(sv->text_widget),
398 "populate-popup",
399 G_CALLBACK(&cb_populate_popup),
400 (gpointer)sv);
403 * Editing -- Ctrl+E
405 g_signal_connect(G_OBJECT(sv->text_widget),
406 "key-press-event",
407 G_CALLBACK(&cb_press_key),
408 (gpointer)sv);
410 sv->delete_hook = &source_view_delete_hook;
412 return sv;
414 out_free_etags:
415 tags_free(sv->etags);
416 out_free_dups:
417 free(sv->file_path);
418 out_free:
419 free(sv);
420 return NULL;