2 * msgwindow.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2005-2010 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2006-2010 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
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.
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.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
25 * Message window functions (status, compiler, messages windows).
26 * Also compiler error message parsing and grep file and line parsing.
34 #include "callbacks.h"
38 #include "filetypes.h"
44 #include "msgwindow.h"
45 #include "keybindings.h"
51 #include <gdk/gdkkeysyms.h>
54 /* used for parse_file_line */
57 const gchar
*string
; /* line data */
58 const gchar
*pattern
; /* pattern to split the error message into some fields */
59 guint min_fields
; /* used to detect errors after parsing */
60 guint line_idx
; /* idx of the field where the line is */
61 gint file_idx
; /* idx of the field where the filename is or -1 */
65 MessageWindow msgwindow
;
68 static void prepare_msg_tree_view(void);
69 static void prepare_status_tree_view(void);
70 static void prepare_compiler_tree_view(void);
71 static GtkWidget
*create_message_popup_menu(gint type
);
72 static void msgwin_parse_grep_line(const gchar
*string
, gchar
**filename
, gint
*line
);
73 static gboolean
on_msgwin_button_press_event(GtkWidget
*widget
, GdkEventButton
*event
,
75 static void on_scribble_populate(GtkTextView
*textview
, GtkMenu
*arg1
, gpointer user_data
);
78 void msgwin_show_hide_tabs(void)
80 ui_widget_show_hide(gtk_widget_get_parent(msgwindow
.tree_status
), interface_prefs
.msgwin_status_visible
);
81 ui_widget_show_hide(gtk_widget_get_parent(msgwindow
.tree_compiler
), interface_prefs
.msgwin_compiler_visible
);
82 ui_widget_show_hide(gtk_widget_get_parent(msgwindow
.tree_msg
), interface_prefs
.msgwin_messages_visible
);
83 ui_widget_show_hide(gtk_widget_get_parent(msgwindow
.scribble
), interface_prefs
.msgwin_scribble_visible
);
87 void msgwin_init(void)
89 msgwindow
.notebook
= ui_lookup_widget(main_widgets
.window
, "notebook_info");
90 msgwindow
.tree_status
= ui_lookup_widget(main_widgets
.window
, "treeview3");
91 msgwindow
.tree_msg
= ui_lookup_widget(main_widgets
.window
, "treeview4");
92 msgwindow
.tree_compiler
= ui_lookup_widget(main_widgets
.window
, "treeview5");
93 msgwindow
.scribble
= ui_lookup_widget(main_widgets
.window
, "textview_scribble");
94 msgwindow
.find_in_files_dir
= NULL
;
96 prepare_status_tree_view();
97 prepare_msg_tree_view();
98 prepare_compiler_tree_view();
99 msgwindow
.popup_status_menu
= create_message_popup_menu(MSG_STATUS
);
100 msgwindow
.popup_msg_menu
= create_message_popup_menu(MSG_MESSAGE
);
101 msgwindow
.popup_compiler_menu
= create_message_popup_menu(MSG_COMPILER
);
103 ui_widget_modify_font_from_string(msgwindow
.scribble
, interface_prefs
.msgwin_font
);
104 g_signal_connect(msgwindow
.scribble
, "populate-popup", G_CALLBACK(on_scribble_populate
), NULL
);
108 void msgwin_finalize(void)
110 g_free(msgwindow
.find_in_files_dir
);
114 static gboolean
on_msgwin_key_press_event(GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
)
116 if (ui_is_keyval_enter_or_return(event
->keyval
) || event
->keyval
== GDK_space
)
118 switch (GPOINTER_TO_INT(data
))
121 { /* single click in the compiler treeview */
122 msgwin_goto_compiler_file_line(event
->keyval
);
126 { /* single click in the message treeview (results of 'Find usage') */
127 msgwin_goto_messages_file_line(event
->keyval
);
136 /* does some preparing things to the status message list widget */
137 static void prepare_status_tree_view(void)
139 GtkCellRenderer
*renderer
;
140 GtkTreeViewColumn
*column
;
142 msgwindow
.store_status
= gtk_list_store_new(1, G_TYPE_STRING
);
143 gtk_tree_view_set_model(GTK_TREE_VIEW(msgwindow
.tree_status
), GTK_TREE_MODEL(msgwindow
.store_status
));
144 g_object_unref(msgwindow
.store_status
);
146 renderer
= gtk_cell_renderer_text_new();
147 column
= gtk_tree_view_column_new_with_attributes(_("Status messages"), renderer
, "text", 0, NULL
);
148 gtk_tree_view_append_column(GTK_TREE_VIEW(msgwindow
.tree_status
), column
);
150 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(msgwindow
.tree_status
), FALSE
);
152 ui_widget_modify_font_from_string(msgwindow
.tree_status
, interface_prefs
.msgwin_font
);
154 g_signal_connect(msgwindow
.tree_status
, "button-press-event",
155 G_CALLBACK(on_msgwin_button_press_event
), GINT_TO_POINTER(MSG_STATUS
));
159 /* does some preparing things to the message list widget
160 * (currently used for showing results of 'Find usage') */
161 static void prepare_msg_tree_view(void)
163 GtkCellRenderer
*renderer
;
164 GtkTreeViewColumn
*column
;
165 GtkTreeSelection
*selection
;
167 /* line, doc, fg, str */
168 msgwindow
.store_msg
= gtk_list_store_new(4, G_TYPE_INT
, G_TYPE_POINTER
,
169 GDK_TYPE_COLOR
, G_TYPE_STRING
);
170 gtk_tree_view_set_model(GTK_TREE_VIEW(msgwindow
.tree_msg
), GTK_TREE_MODEL(msgwindow
.store_msg
));
171 g_object_unref(msgwindow
.store_msg
);
173 renderer
= gtk_cell_renderer_text_new();
174 column
= gtk_tree_view_column_new_with_attributes(NULL
, renderer
,
175 "foreground-gdk", 2, "text", 3, NULL
);
176 gtk_tree_view_append_column(GTK_TREE_VIEW(msgwindow
.tree_msg
), column
);
178 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(msgwindow
.tree_msg
), FALSE
);
180 ui_widget_modify_font_from_string(msgwindow
.tree_msg
, interface_prefs
.msgwin_font
);
182 /* use button-release-event so the selection has changed
183 * (connect_after button-press-event doesn't work) */
184 g_signal_connect(msgwindow
.tree_msg
, "button-release-event",
185 G_CALLBACK(on_msgwin_button_press_event
), GINT_TO_POINTER(MSG_MESSAGE
));
186 g_signal_connect(msgwindow
.tree_msg
, "key-press-event",
187 G_CALLBACK(on_msgwin_key_press_event
), GINT_TO_POINTER(MSG_MESSAGE
));
189 /* selection handling */
190 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(msgwindow
.tree_msg
));
191 gtk_tree_selection_set_mode(selection
, GTK_SELECTION_SINGLE
);
192 /*g_signal_connect(selection, "changed",G_CALLBACK(on_msg_tree_selection_changed), NULL);*/
196 /* does some preparing things to the compiler list widget */
197 static void prepare_compiler_tree_view(void)
199 GtkCellRenderer
*renderer
;
200 GtkTreeViewColumn
*column
;
201 GtkTreeSelection
*selection
;
203 msgwindow
.store_compiler
= gtk_list_store_new(2, GDK_TYPE_COLOR
, G_TYPE_STRING
);
204 gtk_tree_view_set_model(GTK_TREE_VIEW(msgwindow
.tree_compiler
), GTK_TREE_MODEL(msgwindow
.store_compiler
));
205 g_object_unref(msgwindow
.store_compiler
);
207 renderer
= gtk_cell_renderer_text_new();
208 column
= gtk_tree_view_column_new_with_attributes(NULL
, renderer
, "foreground-gdk", 0, "text", 1, NULL
);
209 gtk_tree_view_append_column(GTK_TREE_VIEW(msgwindow
.tree_compiler
), column
);
211 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(msgwindow
.tree_compiler
), FALSE
);
213 ui_widget_modify_font_from_string(msgwindow
.tree_compiler
, interface_prefs
.msgwin_font
);
215 /* use button-release-event so the selection has changed
216 * (connect_after button-press-event doesn't work) */
217 g_signal_connect(msgwindow
.tree_compiler
, "button-release-event",
218 G_CALLBACK(on_msgwin_button_press_event
), GINT_TO_POINTER(MSG_COMPILER
));
219 g_signal_connect(msgwindow
.tree_compiler
, "key-press-event",
220 G_CALLBACK(on_msgwin_key_press_event
), GINT_TO_POINTER(MSG_COMPILER
));
222 /* selection handling */
223 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(msgwindow
.tree_compiler
));
224 gtk_tree_selection_set_mode(selection
, GTK_SELECTION_SINGLE
);
225 /*g_signal_connect(selection, "changed", G_CALLBACK(on_msg_tree_selection_changed), NULL);*/
229 static const GdkColor color_error
= {0, 65535, 0, 0};
231 static const GdkColor
*get_color(gint msg_color
)
233 static const GdkColor dark_red
= {0, 65535 / 2, 0, 0};
234 static const GdkColor blue
= {0, 0, 0, 0xD000}; /* not too bright ;-) */
238 case COLOR_RED
: return &color_error
;
239 case COLOR_DARK_RED
: return &dark_red
;
240 case COLOR_BLUE
: return &blue
;
241 default: return NULL
;
247 * Adds a new message in the compiler tab treeview in the messages window.
249 * @param msg_color A color to be used for the text. It must be an element of #MsgColors.
250 * @param format @c printf()-style format string.
251 * @param ... Arguments for the @c format string.
253 void msgwin_compiler_add(gint msg_color
, const gchar
*format
, ...)
258 va_start(args
, format
);
259 string
= g_strdup_vprintf(format
, args
);
261 msgwin_compiler_add_string(msg_color
, string
);
266 void msgwin_compiler_add_string(gint msg_color
, const gchar
*msg
)
270 const GdkColor
*color
= get_color(msg_color
);
273 if (! g_utf8_validate(msg
, -1, NULL
))
274 utf8_msg
= utils_get_utf8_from_locale(msg
);
276 utf8_msg
= (gchar
*) msg
;
278 gtk_list_store_append(msgwindow
.store_compiler
, &iter
);
279 gtk_list_store_set(msgwindow
.store_compiler
, &iter
, 0, color
, 1, utf8_msg
, -1);
281 if (ui_prefs
.msgwindow_visible
&& interface_prefs
.compiler_tab_autoscroll
)
283 path
= gtk_tree_model_get_path(
284 gtk_tree_view_get_model(GTK_TREE_VIEW(msgwindow
.tree_compiler
)), &iter
);
285 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(msgwindow
.tree_compiler
), path
, NULL
, TRUE
, 0.5, 0.5);
286 gtk_tree_path_free(path
);
289 /* calling build_menu_update for every build message would be overkill, TODO really should call it once when all done */
290 gtk_widget_set_sensitive(build_get_menu_items(-1)->menu_item
[GBG_FIXED
][GBF_NEXT_ERROR
], TRUE
);
291 gtk_widget_set_sensitive(build_get_menu_items(-1)->menu_item
[GBG_FIXED
][GBF_PREV_ERROR
], TRUE
);
298 void msgwin_show_hide(gboolean show
)
300 ui_prefs
.msgwindow_visible
= show
;
301 ignore_callback
= TRUE
;
302 gtk_check_menu_item_set_active(
303 GTK_CHECK_MENU_ITEM(ui_lookup_widget(main_widgets
.window
, "menu_show_messages_window1")),
305 ignore_callback
= FALSE
;
306 ui_widget_show_hide(ui_lookup_widget(main_widgets
.window
, "scrolledwindow1"), show
);
307 /* set the input focus back to the editor */
308 keybindings_send_command(GEANY_KEY_GROUP_FOCUS
, GEANY_KEYS_FOCUS_EDITOR
);
313 * Adds a new message in the messages tab treeview in the messages window.
314 * If @a line and @a doc are set, clicking on this line jumps into the file which is specified
315 * by @a doc into the line specified with @a line.
317 * @param msg_color A color to be used for the text. It must be an element of #MsgColors.
318 * @param line The document's line where the message belongs to. Set to @c -1 to ignore.
319 * @param doc The document. Set to @c NULL to ignore.
320 * @param format @c printf()-style format string.
321 * @param ... Arguments for the @c format string.
325 void msgwin_msg_add(gint msg_color
, gint line
, GeanyDocument
*doc
, const gchar
*format
, ...)
330 va_start(args
, format
);
331 string
= g_strdup_vprintf(format
, args
);
334 msgwin_msg_add_string(msg_color
, line
, doc
, string
);
339 /* adds string to the msg treeview */
340 void msgwin_msg_add_string(gint msg_color
, gint line
, GeanyDocument
*doc
, const gchar
*string
)
343 const GdkColor
*color
= get_color(msg_color
);
348 if (! ui_prefs
.msgwindow_visible
)
349 msgwin_show_hide(TRUE
);
351 /* work around a strange problem when adding very long lines(greater than 4000 bytes)
352 * cut the string to a maximum of 1024 bytes and discard the rest */
353 /* TODO: find the real cause for the display problem / if it is GtkTreeView file a bug report */
354 len
= strlen(string
);
356 tmp
= g_strndup(string
, 1024);
358 tmp
= g_strdup(string
);
360 if (! g_utf8_validate(tmp
, -1, NULL
))
361 utf8_msg
= utils_get_utf8_from_locale(tmp
);
365 gtk_list_store_append(msgwindow
.store_msg
, &iter
);
366 gtk_list_store_set(msgwindow
.store_msg
, &iter
, 0, line
, 1, doc
, 2, color
, 3, utf8_msg
, -1);
375 * Logs a status message *without* setting the status bar.
376 * (Use ui_set_statusbar() to display text on the statusbar)
378 * @param format @c printf()-style format string.
379 * @param ... Arguments for the @c format string.
381 void msgwin_status_add(const gchar
*format
, ...)
385 gchar
*statusmsg
, *time_str
;
388 va_start(args
, format
);
389 string
= g_strdup_vprintf(format
, args
);
392 /* add a timestamp to status messages */
393 time_str
= utils_get_current_time_string();
394 statusmsg
= g_strconcat(time_str
, ": ", string
, NULL
);
398 /* add message to Status window */
399 gtk_list_store_append(msgwindow
.store_status
, &iter
);
400 gtk_list_store_set(msgwindow
.store_status
, &iter
, 0, statusmsg
, -1);
403 if (G_LIKELY(main_status
.main_window_realized
))
405 GtkTreePath
*path
= gtk_tree_model_get_path(gtk_tree_view_get_model(GTK_TREE_VIEW(msgwindow
.tree_status
)), &iter
);
407 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(msgwindow
.tree_status
), path
, NULL
, FALSE
, 0.0, 0.0);
408 if (prefs
.switch_to_status
)
409 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow
.notebook
), MSG_STATUS
);
410 gtk_tree_path_free(path
);
416 on_message_treeview_clear_activate (GtkMenuItem
*menuitem
,
419 gint tabnum
= GPOINTER_TO_INT(user_data
);
421 msgwin_clear_tab(tabnum
);
426 on_compiler_treeview_copy_activate (GtkMenuItem
*menuitem
,
429 GtkWidget
*tv
= NULL
;
430 GtkTreeSelection
*selection
;
435 switch (GPOINTER_TO_INT(user_data
))
438 tv
= msgwindow
.tree_status
;
443 tv
= msgwindow
.tree_compiler
;
447 tv
= msgwindow
.tree_msg
;
451 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(tv
));
453 if (gtk_tree_selection_get_selected(selection
, &model
, &iter
))
457 gtk_tree_model_get(model
, &iter
, str_idx
, &string
, -1);
460 gtk_clipboard_set_text(gtk_clipboard_get(gdk_atom_intern("CLIPBOARD", FALSE
)),
468 static void on_compiler_treeview_copy_all_activate(GtkMenuItem
*menuitem
, gpointer user_data
)
470 GtkListStore
*store
= msgwindow
.store_compiler
;
472 GString
*str
= g_string_new("");
476 switch (GPOINTER_TO_INT(user_data
))
479 store
= msgwindow
.store_status
;
488 store
= msgwindow
.store_msg
;
493 /* walk through the list and copy every line into a string */
494 valid
= gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store
), &iter
);
499 gtk_tree_model_get(GTK_TREE_MODEL(store
), &iter
, str_idx
, &line
, -1);
502 g_string_append(str
, line
);
503 g_string_append_c(str
, '\n');
507 valid
= gtk_tree_model_iter_next(GTK_TREE_MODEL(store
), &iter
);
510 /* copy the string into the clipboard */
513 gtk_clipboard_set_text(
514 gtk_clipboard_get(gdk_atom_intern("CLIPBOARD", FALSE
)),
518 g_string_free(str
, TRUE
);
523 on_hide_message_window (GtkMenuItem
*menuitem
,
526 msgwin_show_hide(FALSE
);
530 static GtkWidget
*create_message_popup_menu(gint type
)
532 GtkWidget
*message_popup_menu
, *clear
, *copy
, *copy_all
, *image
;
534 message_popup_menu
= gtk_menu_new();
536 clear
= gtk_image_menu_item_new_from_stock("gtk-clear", NULL
);
537 gtk_widget_show(clear
);
538 gtk_container_add(GTK_CONTAINER(message_popup_menu
), clear
);
539 g_signal_connect(clear
, "activate",
540 G_CALLBACK(on_message_treeview_clear_activate
), GINT_TO_POINTER(type
));
542 copy
= gtk_image_menu_item_new_with_mnemonic(_("C_opy"));
543 gtk_widget_show(copy
);
544 gtk_container_add(GTK_CONTAINER(message_popup_menu
), copy
);
545 image
= gtk_image_new_from_stock("gtk-copy", GTK_ICON_SIZE_MENU
);
546 gtk_widget_show(image
);
547 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(copy
), image
);
548 g_signal_connect(copy
, "activate",
549 G_CALLBACK(on_compiler_treeview_copy_activate
), GINT_TO_POINTER(type
));
551 copy_all
= gtk_image_menu_item_new_with_mnemonic(_("Copy _All"));
552 gtk_widget_show(copy_all
);
553 gtk_container_add(GTK_CONTAINER(message_popup_menu
), copy_all
);
554 image
= gtk_image_new_from_stock("gtk-copy", GTK_ICON_SIZE_MENU
);
555 gtk_widget_show(image
);
556 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(copy_all
), image
);
557 g_signal_connect(copy_all
, "activate",
558 G_CALLBACK(on_compiler_treeview_copy_all_activate
), GINT_TO_POINTER(type
));
560 msgwin_menu_add_common_items(GTK_MENU(message_popup_menu
));
562 return message_popup_menu
;
566 static void on_scribble_populate(GtkTextView
*textview
, GtkMenu
*arg1
, gpointer user_data
)
568 msgwin_menu_add_common_items(arg1
);
572 /* Menu items that should be on all message window popup menus */
573 void msgwin_menu_add_common_items(GtkMenu
*menu
)
577 item
= gtk_separator_menu_item_new();
578 gtk_widget_show(item
);
579 gtk_container_add(GTK_CONTAINER(menu
), item
);
581 item
= gtk_menu_item_new_with_mnemonic(_("_Hide Message Window"));
582 gtk_widget_show(item
);
583 gtk_container_add(GTK_CONTAINER(menu
), item
);
584 g_signal_connect(item
, "activate", G_CALLBACK(on_hide_message_window
), NULL
);
588 /* look back up from the current path and find the directory we came from */
590 find_prev_build_dir(GtkTreePath
*cur
, GtkTreeModel
*model
, gchar
**prefix
)
595 while (gtk_tree_path_prev(cur
))
597 if (gtk_tree_model_get_iter(model
, &iter
, cur
))
600 gtk_tree_model_get(model
, &iter
, 1, &string
, -1);
601 if (string
!= NULL
&& build_parse_make_dir(string
, prefix
))
614 static gboolean
goto_compiler_file_line(const gchar
*filename
, gint line
, guint keyval
)
616 if (!filename
|| line
<= -1)
619 /* If the path doesn't exist, try the current document.
620 * This happens when we receive build messages in the wrong order - after the
621 * 'Leaving directory' messages */
622 if (!g_file_test(filename
, G_FILE_TEST_EXISTS
))
624 gchar
*cur_dir
= utils_get_current_file_dir_utf8();
629 /* we let the user know we couldn't find the parsed filename from the message window */
630 setptr(cur_dir
, utils_get_locale_from_utf8(cur_dir
));
631 name
= g_path_get_basename(filename
);
632 setptr(name
, g_build_path(G_DIR_SEPARATOR_S
, cur_dir
, name
, NULL
));
635 if (g_file_test(name
, G_FILE_TEST_EXISTS
))
637 ui_set_statusbar(FALSE
, _("Could not find file '%s' - trying the current document path."),
647 gchar
*utf8_filename
= utils_get_utf8_from_locale(filename
);
648 GeanyDocument
*doc
= document_find_by_filename(utf8_filename
);
649 GeanyDocument
*old_doc
= document_get_current();
651 g_free(utf8_filename
);
653 if (doc
== NULL
) /* file not already open */
654 doc
= document_open_file(filename
, FALSE
, NULL
, NULL
);
660 if (! doc
->changed
&& editor_prefs
.use_indicators
) /* if modified, line may be wrong */
661 editor_indicator_set_on_line(doc
->editor
, GEANY_INDICATOR_ERROR
, line
- 1);
663 ret
= navqueue_goto_line(old_doc
, doc
, line
);
664 if (ret
&& ui_is_keyval_enter_or_return(keyval
))
665 gtk_widget_grab_focus(GTK_WIDGET(doc
->editor
->sci
));
674 gboolean
msgwin_goto_compiler_file_line(guint keyval
)
678 GtkTreeSelection
*selection
;
682 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(msgwindow
.tree_compiler
));
683 if (gtk_tree_selection_get_selected(selection
, &model
, &iter
))
685 /* if the item is not coloured red, it's not an error line */
686 gtk_tree_model_get(model
, &iter
, 0, &color
, -1);
687 if (color
== NULL
|| ! gdk_color_equal(color
, &color_error
))
690 gdk_color_free(color
);
693 gdk_color_free(color
);
695 gtk_tree_model_get(model
, &iter
, 1, &string
, -1);
699 gchar
*filename
, *dir
;
703 path
= gtk_tree_model_get_path(model
, &iter
);
704 find_prev_build_dir(path
, model
, &dir
);
705 gtk_tree_path_free(path
);
706 msgwin_parse_compiler_error_line(string
, dir
, &filename
, &line
);
710 ret
= goto_compiler_file_line(filename
, line
, keyval
);
719 static void make_absolute(gchar
**filename
, const gchar
*dir
)
721 guint skip_dot_slash
= 0; /* number of characters to skip at the beginning of the filename */
723 if (*filename
== NULL
)
726 /* skip some characters at the beginning of the filename, at the moment only "./"
727 * can be extended if other "trash" is known */
728 if (strncmp(*filename
, "./", 2) == 0)
732 if (! utils_is_absolute_path(*filename
))
733 setptr(*filename
, g_strconcat(dir
, G_DIR_SEPARATOR_S
,
734 *filename
+ skip_dot_slash
, NULL
));
738 /* try to parse the file and line number where the error occured described in line
739 * and when something useful is found, it stores the line number in *line and the
740 * relevant file with the error in *filename.
741 * *line will be -1 if no error was found in string.
742 * *filename must be freed unless it is NULL. */
743 static void parse_file_line(ParseData
*data
, gchar
**filename
, gint
*line
)
751 g_return_if_fail(data
->string
!= NULL
);
753 fields
= g_strsplit_set(data
->string
, data
->pattern
, data
->min_fields
);
756 if (g_strv_length(fields
) < data
->min_fields
)
762 *line
= strtol(fields
[data
->line_idx
], &end
, 10);
764 /* if the line could not be read, line is 0 and an error occurred, so we leave */
765 if (fields
[data
->line_idx
] == end
)
771 /* let's stop here if there is no filename in the error message */
772 if (data
->file_idx
== -1)
774 /* we have no filename in the error message, so take the current one and hope it's correct */
775 GeanyDocument
*doc
= document_get_current();
777 *filename
= g_strdup(doc
->file_name
);
782 *filename
= g_strdup(fields
[data
->file_idx
]);
787 static void parse_compiler_error_line(const gchar
*string
,
788 gchar
**filename
, gint
*line
)
790 ParseData data
= {NULL
, NULL
, 0, 0, 0};
792 data
.string
= string
;
794 switch (build_info
.file_type_id
)
796 case GEANY_FILETYPES_PHP
:
798 /* Parse error: parse error, unexpected T_CASE in brace_bug.php on line 3
799 * Parse error: syntax error, unexpected T_LNUMBER, expecting T_FUNCTION in bob.php on line 16 */
800 gchar
*tmp
= strstr(string
, " in ");
813 data
.min_fields
= 11;
819 case GEANY_FILETYPES_PERL
:
821 /* syntax error at test.pl line 7, near "{ */
828 /* the error output of python and tcl equals */
829 case GEANY_FILETYPES_TCL
:
830 case GEANY_FILETYPES_PYTHON
:
832 /* File "HyperArch.py", line 37, in ?
833 * (file "clrdial.tcl" line 12) */
834 data
.pattern
= " \"";
840 case GEANY_FILETYPES_BASIC
:
841 case GEANY_FILETYPES_PASCAL
:
842 case GEANY_FILETYPES_CS
:
844 /* getdrive.bas(52) error 18: Syntax error in '? GetAllDrives'
845 * bandit.pas(149,3) Fatal: Syntax error, ";" expected but "ELSE" found */
852 case GEANY_FILETYPES_D
:
854 /* GNU D compiler front-end, gdc
855 * gantry.d:18: variable gantry.main.c reference to auto class must be auto
856 * warning - gantry.d:20: statement is not reachable
857 * Digital Mars dmd compiler
858 * warning - pi.d(118): implicit conversion of expression (digit) of type int ...
859 * gantry.d(18): variable gantry.main.c reference to auto class must be auto */
860 if (strncmp(string
, "warning - ", 10) == 0)
862 data
.pattern
= " (:";
876 case GEANY_FILETYPES_FERITE
:
878 /* Error: Parse Error: on line 5 in "/tmp/hello.fe"
879 * Error: Compile Error: on line 24, in /test/class.fe */
880 if (strncmp(string
, "Error: Compile Error", 20) == 0)
889 data
.pattern
= " \"";
890 data
.min_fields
= 10;
896 case GEANY_FILETYPES_HTML
:
898 /* line 78 column 7 - Warning: <table> missing '>' for end of tag */
905 /* All GNU gcc-like error messages */
906 case GEANY_FILETYPES_C
:
907 case GEANY_FILETYPES_CPP
:
908 case GEANY_FILETYPES_RUBY
:
909 case GEANY_FILETYPES_JAVA
:
910 /* only gcc is supported, I don't know any other C(++) compilers and their error messages
911 * empty.h:4: Warnung: type defaults to `int' in declaration of `foo'
912 * empty.c:21: error: conflicting types for `foo'
913 * Only parse file and line, so that linker errors will also work (with -g) */
914 case GEANY_FILETYPES_F77
:
915 case GEANY_FILETYPES_FORTRAN
:
916 case GEANY_FILETYPES_LATEX
:
917 /* ./kommtechnik_2b.tex:18: Emergency stop. */
918 case GEANY_FILETYPES_MAKE
: /* Assume makefile is building with gcc */
919 case GEANY_FILETYPES_NONE
:
920 default: /* The default is a GNU gcc type error */
922 if (build_info
.file_type_id
== GEANY_FILETYPES_JAVA
&&
923 strncmp(string
, "[javac]", 7) == 0)
926 * [javac] <Full Path to File + extension>:<line n°>: <error> */
933 /* don't accidently find libtool versions x:y:x and think it is a file name */
934 if (strstr(string
, "libtool --mode=link") == NULL
)
945 if (data
.pattern
!= NULL
)
946 parse_file_line(&data
, filename
, line
);
950 /* try to parse the file and line number where the error occured described in string
951 * and when something useful is found, it stores the line number in *line and the
952 * relevant file with the error in *filename.
953 * *line will be -1 if no error was found in string.
954 * *filename must be freed unless it is NULL. */
955 void msgwin_parse_compiler_error_line(const gchar
*string
, const gchar
*dir
,
956 gchar
**filename
, gint
*line
)
959 gchar
*trimmed_string
;
964 if (G_UNLIKELY(string
== NULL
))
968 dir
= build_info
.dir
;
969 g_return_if_fail(dir
!= NULL
);
971 trimmed_string
= g_strdup(string
);
972 g_strchug(trimmed_string
); /* remove possible leading whitespace */
974 ft
= filetypes
[build_info
.file_type_id
];
976 /* try parsing with a custom regex */
977 if (!filetypes_parse_error_message(ft
, trimmed_string
, filename
, line
))
979 /* fallback to default old-style parsing */
980 parse_compiler_error_line(trimmed_string
, filename
, line
);
982 make_absolute(filename
, dir
);
983 g_free(trimmed_string
);
987 gboolean
msgwin_goto_messages_file_line(guint keyval
)
991 GtkTreeSelection
*selection
;
992 gboolean ret
= FALSE
;
994 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(msgwindow
.tree_msg
));
995 if (gtk_tree_selection_get_selected(selection
, &model
, &iter
))
1000 GeanyDocument
*old_doc
= document_get_current();
1002 gtk_tree_model_get(model
, &iter
, 0, &line
, 1, &doc
, 3, &string
, -1);
1003 /* doc may have been closed, so check doc->index: */
1004 if (line
>= 0 && DOC_VALID(doc
))
1006 ret
= navqueue_goto_line(old_doc
, doc
, line
);
1007 if (ret
&& ui_is_keyval_enter_or_return(keyval
))
1008 gtk_widget_grab_focus(GTK_WIDGET(doc
->editor
->sci
));
1010 else if (line
< 0 && string
!= NULL
)
1013 msgwin_parse_grep_line(string
, &filename
, &line
);
1014 if (filename
!= NULL
&& line
> -1)
1016 /* use document_open_file to find an already open file, or open it in place */
1017 doc
= document_open_file(filename
, FALSE
, NULL
, NULL
);
1020 ret
= navqueue_goto_line(old_doc
, doc
, line
);
1021 if (ret
&& ui_is_keyval_enter_or_return(keyval
))
1022 gtk_widget_grab_focus(GTK_WIDGET(doc
->editor
->sci
));
1033 /* Try to parse the file and line number for string and when something useful is
1034 * found, store the line number in *line and the relevant file with the error in
1036 * *line will be -1 if no error was found in string.
1037 * *filename must be freed unless NULL. */
1038 static void msgwin_parse_grep_line(const gchar
*string
, gchar
**filename
, gint
*line
)
1045 if (string
== NULL
|| msgwindow
.find_in_files_dir
== NULL
)
1048 /* conflict:3:conflicting types for `foo' */
1049 data
.string
= string
;
1051 data
.min_fields
= 3;
1055 parse_file_line(&data
, filename
, line
);
1056 make_absolute(filename
, msgwindow
.find_in_files_dir
);
1060 static gboolean
on_msgwin_button_press_event(GtkWidget
*widget
, GdkEventButton
*event
,
1063 /* user_data might be NULL, GPOINTER_TO_INT returns 0 if called with NULL */
1065 if (event
->button
== 1)
1067 switch (GPOINTER_TO_INT(user_data
))
1070 { /* single click in the compiler treeview */
1071 msgwin_goto_compiler_file_line(0);
1075 { /* single click in the message treeview (results of 'Find usage') */
1076 msgwin_goto_messages_file_line(0);
1082 if (event
->button
== 3)
1083 { /* popupmenu to hide or clear the active treeview */
1084 switch (GPOINTER_TO_INT(user_data
))
1088 gtk_menu_popup(GTK_MENU(msgwindow
.popup_status_menu
), NULL
, NULL
, NULL
, NULL
,
1089 event
->button
, event
->time
);
1094 gtk_menu_popup(GTK_MENU(msgwindow
.popup_msg_menu
), NULL
, NULL
, NULL
, NULL
,
1095 event
->button
, event
->time
);
1100 gtk_menu_popup(GTK_MENU(msgwindow
.popup_compiler_menu
), NULL
, NULL
, NULL
, NULL
,
1101 event
->button
, event
->time
);
1111 * Switches to the given notebook tab of the messages window and shows the messages window
1112 * if it was previously hidden and @a show is set to @c TRUE.
1114 * @param tabnum An index of a tab in the messages window. Valid values are all elements of
1115 * #MessageWindowTabNum.
1116 * @param show Whether to show the messages window at all if it was hidden before.
1120 void msgwin_switch_tab(gint tabnum
, gboolean show
)
1122 GtkWidget
*widget
= NULL
; /* widget to focus */
1126 case MSG_SCRATCH
: widget
= msgwindow
.scribble
; break;
1127 case MSG_COMPILER
: widget
= msgwindow
.tree_compiler
; break;
1128 case MSG_STATUS
: widget
= msgwindow
.tree_status
; break;
1129 case MSG_MESSAGE
: widget
= msgwindow
.tree_msg
; break;
1131 case MSG_VTE
: widget
= (vte_info
.have_vte
) ? vc
->vte
: NULL
; break;
1136 /* the msgwin must be visible before we switch to the VTE page so that
1137 * the font settings are applied on realization */
1139 msgwin_show_hide(TRUE
);
1140 gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow
.notebook
), tabnum
);
1142 gtk_widget_grab_focus(widget
);
1147 * Removes all messages from a tab specified by @a tabnum in the messages window.
1149 * @param tabnum An index of a tab in the messages window which should be cleared.
1150 * Valid values are @c MSG_STATUS, @c MSG_COMPILER and @c MSG_MESSAGE.
1154 void msgwin_clear_tab(gint tabnum
)
1156 GtkListStore
*store
= NULL
;
1161 store
= msgwindow
.store_msg
;
1165 gtk_list_store_clear(msgwindow
.store_compiler
);
1166 build_menu_update(NULL
); /* update next error items */
1169 case MSG_STATUS
: store
= msgwindow
.store_status
; break;
1174 gtk_list_store_clear(store
);