Make Ctrl-click on any notebook tab switch to the last used
[geany-mirror.git] / src / navqueue.c
blob46d86c22de24a2a67d7e5e474f8db28cf6c9af4d
1 /*
2 * navqueue.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2007 Dave Moore <wrex006(at)gmail(dot)com>
5 * Copyright 2007-2010 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
6 * Copyright 2007-2010 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 * $Id$
26 * Simple code navigation
29 #include "geany.h"
31 #include "sciwrappers.h"
32 #include "document.h"
33 #include "utils.h"
34 #include "support.h"
35 #include "ui_utils.h"
36 #include "editor.h"
37 #include "navqueue.h"
38 #include "toolbar.h"
41 /* for the navigation history queue */
42 typedef struct
44 const gchar *file; /* This is the document's filename, in UTF-8 */
45 gint pos;
46 } filepos;
48 static GQueue *navigation_queue;
49 static guint nav_queue_pos;
51 static GtkAction *navigation_buttons[2];
55 void navqueue_init()
57 navigation_queue = g_queue_new();
58 nav_queue_pos = 0;
60 navigation_buttons[0] = toolbar_get_action_by_name("NavBack");
61 navigation_buttons[1] = toolbar_get_action_by_name("NavFor");
65 void navqueue_free()
67 while (! g_queue_is_empty(navigation_queue))
69 g_free(g_queue_pop_tail(navigation_queue));
71 g_queue_free(navigation_queue);
75 static void adjust_buttons(void)
77 if (g_queue_get_length(navigation_queue) < 2)
79 gtk_action_set_sensitive(navigation_buttons[0], FALSE);
80 gtk_action_set_sensitive(navigation_buttons[1], FALSE);
81 return;
83 if (nav_queue_pos == 0)
85 gtk_action_set_sensitive(navigation_buttons[0], TRUE);
86 gtk_action_set_sensitive(navigation_buttons[1], FALSE);
87 return;
89 /* forward should be sensitive since where not at the start */
90 gtk_action_set_sensitive(navigation_buttons[1], TRUE);
92 /* back should be sensitive if there's a place to go back to */
93 (nav_queue_pos < g_queue_get_length(navigation_queue) - 1) ?
94 gtk_action_set_sensitive(navigation_buttons[0], TRUE) :
95 gtk_action_set_sensitive(navigation_buttons[0], FALSE);
99 static gboolean
100 queue_pos_matches(guint queue_pos, const gchar *fname, gint pos)
102 if (queue_pos < g_queue_get_length(navigation_queue))
104 filepos *fpos = g_queue_peek_nth(navigation_queue, queue_pos);
106 return (utils_str_equal(fpos->file, fname) && fpos->pos == pos);
108 return FALSE;
112 static void add_new_position(const gchar *utf8_filename, gint pos)
114 filepos *npos;
115 guint i;
117 if (queue_pos_matches(nav_queue_pos, utf8_filename, pos))
118 return; /* prevent duplicates */
120 npos = g_new0(filepos, 1);
121 npos->file = utf8_filename;
122 npos->pos = pos;
124 /* if we've jumped to a new position from inside the queue rather than going forward */
125 if (nav_queue_pos > 0)
127 for (i = 0; i < nav_queue_pos; i++)
129 g_free(g_queue_pop_head(navigation_queue));
131 nav_queue_pos = 0;
133 g_queue_push_head(navigation_queue, npos);
134 adjust_buttons();
139 * Adds old file position and new file position to the navqueue, then goes to the new position.
141 * @param old_doc The document of the previous position, if set as invalid (@c NULL) then no old
142 * position is set
143 * @param new_doc The document of the new position, must be valid.
144 * @param line the line number of the new position. It is counted with 1 as the first line, not 0.
146 * @return @c TRUE if the cursor has changed the position to @a line or @c FALSE otherwise.
148 gboolean navqueue_goto_line(GeanyDocument *old_doc, GeanyDocument *new_doc, gint line)
150 gint pos;
152 g_return_val_if_fail(new_doc != NULL, FALSE);
153 g_return_val_if_fail(line >= 1, FALSE);
155 pos = sci_get_position_from_line(new_doc->editor->sci, line - 1);
157 /* first add old file position */
158 if (old_doc != NULL && old_doc->file_name)
160 gint cur_pos = sci_get_current_position(old_doc->editor->sci);
162 add_new_position(old_doc->file_name, cur_pos);
165 /* now add new file position */
166 if (new_doc->file_name)
168 add_new_position(new_doc->file_name, pos);
171 return editor_goto_pos(new_doc->editor, pos, TRUE);
175 static gboolean goto_file_pos(const gchar *file, gint pos)
177 GeanyDocument *doc = document_find_by_filename(file);
179 if (doc == NULL)
180 return FALSE;
182 return editor_goto_pos(doc->editor, pos, TRUE);
186 void navqueue_go_back()
188 filepos *fprev;
190 /* return if theres no place to go back to */
191 if (g_queue_is_empty(navigation_queue) ||
192 nav_queue_pos >= g_queue_get_length(navigation_queue) - 1)
193 return;
195 /* jump back */
196 fprev = g_queue_peek_nth(navigation_queue, nav_queue_pos + 1);
197 if (goto_file_pos(fprev->file, fprev->pos))
199 nav_queue_pos++;
201 else
203 /** TODO: add option to re open the file */
204 g_free(g_queue_pop_nth(navigation_queue, nav_queue_pos + 1));
206 adjust_buttons();
210 void navqueue_go_forward()
212 filepos *fnext;
214 if (nav_queue_pos < 1 ||
215 nav_queue_pos >= g_queue_get_length(navigation_queue))
216 return;
218 /* jump forward */
219 fnext = g_queue_peek_nth(navigation_queue, nav_queue_pos - 1);
220 if (goto_file_pos(fnext->file, fnext->pos))
222 nav_queue_pos--;
224 else
226 /** TODO: add option to re open the file */
227 g_free(g_queue_pop_nth(navigation_queue, nav_queue_pos - 1));
230 adjust_buttons();
234 static gint find_by_filename(gconstpointer a, gconstpointer b)
236 if (utils_str_equal(((const filepos*)a)->file, (const gchar*) b))
237 return 0;
238 else
239 return 1;
243 /* Remove all elements with the given filename */
244 void navqueue_remove_file(const gchar *filename)
246 GList *match;
248 if (filename == NULL)
249 return;
251 while ((match = g_queue_find_custom(navigation_queue, filename, find_by_filename)))
253 g_free(match->data);
254 g_queue_delete_link(navigation_queue, match);
257 adjust_buttons();