diff --git a/src/build.c b/src/build.c index 56ec9d6133..ecb7902ccc 100644 --- a/src/build.c +++ b/src/build.c @@ -168,6 +168,7 @@ static void run_exit_cb(GPid child_pid, gint status, gpointer user_data); static void on_set_build_commands_activate(GtkWidget *w, gpointer u); static void on_build_next_error(GtkWidget *menuitem, gpointer user_data); static void on_build_previous_error(GtkWidget *menuitem, gpointer user_data); +static void on_build_current_line_error(GtkWidget *menuitem, gpointer user_data); static void kill_process(GPid *pid); static void show_build_result_message(gboolean failure); static void process_build_output_line(gchar *msg, gint color); @@ -1030,6 +1031,7 @@ static void process_build_output_line(gchar *msg, gint color) { gtk_widget_set_sensitive(build_get_menu_items(-1)->menu_item[GBG_FIXED][GBF_NEXT_ERROR], TRUE); gtk_widget_set_sensitive(build_get_menu_items(-1)->menu_item[GBG_FIXED][GBF_PREV_ERROR], TRUE); + gtk_widget_set_sensitive(build_get_menu_items(-1)->menu_item[GBG_FIXED][GBF_CUR_LINE_ERROR], TRUE); } } g_free(filename); @@ -1318,7 +1320,8 @@ static void on_build_menu_item(GtkWidget *w, gpointer user_data) /* the fixed items */ #define MENU_NEXT_ERROR (MENU_SEPARATOR + 1) #define MENU_PREV_ERROR (MENU_NEXT_ERROR + 1) -#define MENU_COMMANDS (MENU_PREV_ERROR + 1) +#define MENU_CUR_LINE_ERROR (MENU_PREV_ERROR + 1) +#define MENU_COMMANDS (MENU_CUR_LINE_ERROR + 1) #define MENU_DONE (MENU_COMMANDS + 1) @@ -1352,6 +1355,8 @@ static struct BuildMenuItemSpec { GBF_NEXT_ERROR, N_("_Next Error"), on_build_next_error}, {GTK_STOCK_GO_UP, GEANY_KEYS_BUILD_PREVIOUSERROR, MENU_PREV_ERROR, GBF_PREV_ERROR, N_("_Previous Error"), on_build_previous_error}, + {NULL, GEANY_KEYS_BUILD_CURRENTLINEERROR, MENU_CUR_LINE_ERROR, + GBF_CUR_LINE_ERROR, N_("Current _Line Error"), on_build_current_line_error}, {NULL, -1, MENU_SEPARATOR, GBF_SEP_3, NULL, NULL}, {GTK_STOCK_EXECUTE, GEANY_KEYS_BUILD_RUN, GBO_TO_GBG(GEANY_GBO_EXEC), @@ -1489,6 +1494,7 @@ void build_menu_update(GeanyDocument *doc) break; case MENU_NEXT_ERROR: case MENU_PREV_ERROR: + case MENU_CUR_LINE_ERROR: gtk_widget_set_sensitive(menu_items.menu_item[GBG_FIXED][bs->build_cmd], have_errors); vis |= TRUE; break; @@ -1702,6 +1708,18 @@ static void on_build_previous_error(GtkWidget *menuitem, gpointer user_data) } +static void on_build_current_line_error(GtkWidget *menuitem, gpointer user_data) +{ + if (ui_tree_view_find_cur_line(GTK_TREE_VIEW(msgwindow.tree_compiler), + msgwin_goto_compiler_file_cur_line)) + { + gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER); + } + else + ui_set_statusbar(FALSE, _("No more build errors.")); +} + + void build_toolbutton_build_clicked(GtkAction *action, gpointer unused) { if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_BUILD)) @@ -2807,6 +2825,9 @@ gboolean build_keybinding(guint key_id) case GEANY_KEYS_BUILD_PREVIOUSERROR: item = menu_items->menu_item[GBG_FIXED][GBF_PREV_ERROR]; break; + case GEANY_KEYS_BUILD_CURRENTLINEERROR: + item = menu_items->menu_item[GBG_FIXED][GBF_CUR_LINE_ERROR]; + break; case GEANY_KEYS_BUILD_RUN: item = menu_items->menu_item[GEANY_GBG_EXEC][GBO_TO_CMD(GEANY_GBO_EXEC)]; break; diff --git a/src/build.h b/src/build.h index f9d720d06a..eab27bfe2a 100644 --- a/src/build.h +++ b/src/build.h @@ -82,6 +82,7 @@ enum GeanyBuildFixedMenuItems { GBF_NEXT_ERROR, GBF_PREV_ERROR, + GBF_CUR_LINE_ERROR, GBF_COMMANDS, GBF_SEP_1, GBF_SEP_2, diff --git a/src/keybindings.c b/src/keybindings.c index 1721fcd1b3..439a212d46 100644 --- a/src/keybindings.c +++ b/src/keybindings.c @@ -705,6 +705,8 @@ static void init_default_kb(void) 0, 0, "build_nexterror", _("Next error"), NULL); add_kb(group, GEANY_KEYS_BUILD_PREVIOUSERROR, NULL, 0, 0, "build_previouserror", _("Previous error"), NULL); + add_kb(group, GEANY_KEYS_BUILD_CURRENTLINEERROR, NULL, + GDK_KEY_F3, 0, "build_currentlineerror", _("Current line error"), NULL); add_kb(group, GEANY_KEYS_BUILD_RUN, NULL, GDK_KEY_F5, 0, "build_run", _("Run"), NULL); add_kb(group, GEANY_KEYS_BUILD_OPTIONS, NULL, diff --git a/src/keybindings.h b/src/keybindings.h index e88bebc7f8..787c0ff411 100644 --- a/src/keybindings.h +++ b/src/keybindings.h @@ -229,6 +229,7 @@ enum GeanyKeyBindingID GEANY_KEYS_SELECT_ALL, /**< Keybinding. */ GEANY_KEYS_DOCUMENT_RELOADTAGLIST, /**< Keybinding. */ GEANY_KEYS_BUILD_NEXTERROR, /**< Keybinding. */ + GEANY_KEYS_BUILD_CURRENTLINEERROR, /**< Keybinding. */ GEANY_KEYS_NOTEBOOK_MOVETABLAST, /**< Keybinding. */ GEANY_KEYS_SELECT_PARAGRAPH, /**< Keybinding. */ GEANY_KEYS_EDITOR_DELETELINE, /**< Keybinding. */ diff --git a/src/msgwindow.c b/src/msgwindow.c index 350ec3880e..7a75128e1d 100644 --- a/src/msgwindow.c +++ b/src/msgwindow.c @@ -40,6 +40,7 @@ #include "main.h" #include "navqueue.h" #include "prefs.h" +#include "sciwrappers.h" #include "support.h" #include "ui_utils.h" #include "utils.h" @@ -793,7 +794,7 @@ static gboolean goto_compiler_file_line(const gchar *fname, gint line, gboolean } -gboolean msgwin_goto_compiler_file_line(gboolean focus_editor) +gboolean msgwin_goto_compiler_file_line_mode(gboolean focus_editor, gboolean cur_line_mode) { GtkTreeIter iter; GtkTreeModel *model; @@ -817,10 +818,13 @@ gboolean msgwin_goto_compiler_file_line(gboolean focus_editor) gtk_tree_model_get(model, &iter, COMPILER_COL_STRING, &string, -1); if (string != NULL) { - gint line; + guint line; gchar *filename, *dir; GtkTreePath *path; gboolean ret; + ScintillaObject *sci = document_get_current()->editor->sci; + gint pos = sci_get_current_position(sci); + guint cur_line = sci_get_line_from_position(sci, pos) + 1; path = gtk_tree_model_get_path(model, &iter); find_prev_build_dir(path, model, &dir); @@ -829,7 +833,10 @@ gboolean msgwin_goto_compiler_file_line(gboolean focus_editor) g_free(string); g_free(dir); - ret = goto_compiler_file_line(filename, line, focus_editor); + if (cur_line_mode) + ret = line == cur_line; + else + ret = goto_compiler_file_line(filename, line, focus_editor); g_free(filename); return ret; } @@ -838,6 +845,18 @@ gboolean msgwin_goto_compiler_file_line(gboolean focus_editor) } +gboolean msgwin_goto_compiler_file_line(gboolean focus_editor) +{ + return msgwin_goto_compiler_file_line_mode(focus_editor, FALSE); // cur_line_mode +} + + +gboolean msgwin_goto_compiler_file_cur_line(gboolean focus_editor) +{ + return msgwin_goto_compiler_file_line_mode(focus_editor, TRUE); // cur_line_mode +} + + static void make_absolute(gchar **filename, const gchar *dir) { guint skip_dot_slash = 0; /* number of characters to skip at the beginning of the filename */ diff --git a/src/msgwindow.h b/src/msgwindow.h index 07b06dbc7d..f25aabacad 100644 --- a/src/msgwindow.h +++ b/src/msgwindow.h @@ -102,6 +102,10 @@ void msgwin_menu_add_common_items(GtkMenu *menu); gboolean msgwin_goto_compiler_file_line(gboolean focus_editor); +gboolean msgwin_goto_compiler_file_cur_line(gboolean focus_editor); + +gboolean msgwin_goto_compiler_file_cur_line(gboolean focus_editor); + void msgwin_parse_compiler_error_line(const gchar *string, const gchar *dir, gchar **filename, gint *line); diff --git a/src/ui_utils.c b/src/ui_utils.c index 729feae94d..90e2f65ba7 100644 --- a/src/ui_utils.c +++ b/src/ui_utils.c @@ -1768,23 +1768,36 @@ static gboolean tree_model_iter_get_next(GtkTreeModel *model, GtkTreeIter *iter, /* note: the while loop might be more efficient when searching upwards if it * used tree paths instead of tree iters, but in practice it probably doesn't matter much. */ -static gboolean tree_view_find(GtkTreeView *treeview, TVMatchCallback cb, gboolean down) +static gboolean tree_view_find(GtkTreeView *treeview, TVMatchCallback cb, + GeanyMenuCompileErrorSearchMode err_search_mode) { GtkTreeSelection *treesel; GtkTreeIter iter; GtkTreeModel *model; treesel = gtk_tree_view_get_selection(treeview); - if (gtk_tree_selection_get_selected(treesel, &model, &iter)) + gboolean active_selection = gtk_tree_selection_get_selected(treesel, &model, &iter); + gboolean down = err_search_mode != GEANY_MENU_COMPILE_PREV_ERROR; + if (err_search_mode == GEANY_MENU_COMPILE_CUR_LINE_ERROR) { - /* get the next selected item */ - if (! tree_model_iter_get_next(model, &iter, down)) - return FALSE; /* no more items */ + if (active_selection) + tree_model_iter_get_next(model, &iter, TRUE); + if (! gtk_tree_model_get_iter_first(model, &iter)) + return TRUE; } - else /* no selection */ + else { - if (! gtk_tree_model_get_iter_first(model, &iter)) - return TRUE; /* no items */ + if (active_selection) + { + /* get the next selected item */ + if (! tree_model_iter_get_next(model, &iter, down)) + return FALSE; /* no more items */ + } + else /* no selection */ + { + if (! gtk_tree_model_get_iter_first(model, &iter)) + return TRUE; /* no items */ + } } while (TRUE) { @@ -1811,14 +1824,21 @@ static gboolean tree_view_find(GtkTreeView *treeview, TVMatchCallback cb, gboole /* Returns FALSE if the treeview has items but no matching next item. */ gboolean ui_tree_view_find_next(GtkTreeView *treeview, TVMatchCallback cb) { - return tree_view_find(treeview, cb, TRUE); + return tree_view_find(treeview, cb, GEANY_MENU_COMPILE_NEXT_ERROR); } /* Returns FALSE if the treeview has items but no matching next item. */ gboolean ui_tree_view_find_previous(GtkTreeView *treeview, TVMatchCallback cb) { - return tree_view_find(treeview, cb, FALSE); + return tree_view_find(treeview, cb, GEANY_MENU_COMPILE_PREV_ERROR); +} + + +/* Returns FALSE if the treeview has items but no matching next item. */ +gboolean ui_tree_view_find_cur_line(GtkTreeView *treeview, TVMatchCallback cb) +{ + return tree_view_find(treeview, cb, GEANY_MENU_COMPILE_CUR_LINE_ERROR); } diff --git a/src/ui_utils.h b/src/ui_utils.h index f71790a54c..6e70faaf45 100644 --- a/src/ui_utils.h +++ b/src/ui_utils.h @@ -232,6 +232,15 @@ typedef enum GeanyUIEditorFeatures; +typedef enum +{ + GEANY_MENU_COMPILE_NEXT_ERROR, + GEANY_MENU_COMPILE_PREV_ERROR, + GEANY_MENU_COMPILE_CUR_LINE_ERROR +} +GeanyMenuCompileErrorSearchMode; + + void ui_widget_show_hide(GtkWidget *widget, gboolean show); gchar *ui_menu_item_get_text(GtkMenuItem *menu_item); @@ -346,6 +355,8 @@ gboolean ui_tree_view_find_next(GtkTreeView *treeview, TVMatchCallback cb); gboolean ui_tree_view_find_previous(GtkTreeView *treeview, TVMatchCallback cb); +gboolean ui_tree_view_find_cur_line(GtkTreeView *treeview, TVMatchCallback cb); + gboolean ui_tree_model_iter_any_next(GtkTreeModel *model, GtkTreeIter *iter, gboolean down); void ui_statusbar_showhide(gboolean state);