Store project files in base directory by default
[geany-mirror.git] / src / navqueue.c
bloba4ada6932d6d3fbdae230eebd009a703b8eaf29c
1 /*
2 * navqueue.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2007 The Geany contributors
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.
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.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 * Simple code navigation
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
29 #include "navqueue.h"
31 #include "document.h"
32 #include "geanyobject.h"
33 #include "sciwrappers.h"
34 #include "toolbar.h"
35 #include "utils.h"
37 #include <gtk/gtk.h>
40 /* for the navigation history queue */
41 typedef struct
43 const gchar *file; /* This is the document's filename, in UTF-8 */
44 gint pos;
45 } filepos;
47 static GQueue *navigation_queue;
48 static guint nav_queue_pos;
50 static GtkAction *navigation_buttons[2];
54 void navqueue_init(void)
56 navigation_queue = g_queue_new();
57 nav_queue_pos = 0;
59 navigation_buttons[0] = toolbar_get_action_by_name("NavBack");
60 navigation_buttons[1] = toolbar_get_action_by_name("NavFor");
62 gtk_action_set_sensitive(navigation_buttons[0], FALSE);
63 gtk_action_set_sensitive(navigation_buttons[1], FALSE);
67 void navqueue_free(void)
69 while (! g_queue_is_empty(navigation_queue))
71 g_free(g_queue_pop_tail(navigation_queue));
73 g_queue_free(navigation_queue);
77 static void adjust_buttons(void)
79 if (g_queue_get_length(navigation_queue) < 2)
81 gtk_action_set_sensitive(navigation_buttons[0], FALSE);
82 gtk_action_set_sensitive(navigation_buttons[1], FALSE);
83 return;
85 if (nav_queue_pos == 0)
87 gtk_action_set_sensitive(navigation_buttons[0], TRUE);
88 gtk_action_set_sensitive(navigation_buttons[1], FALSE);
89 return;
91 /* forward should be sensitive since where not at the start */
92 gtk_action_set_sensitive(navigation_buttons[1], TRUE);
94 /* back should be sensitive if there's a place to go back to */
95 (nav_queue_pos < g_queue_get_length(navigation_queue) - 1) ?
96 gtk_action_set_sensitive(navigation_buttons[0], TRUE) :
97 gtk_action_set_sensitive(navigation_buttons[0], FALSE);
101 static gboolean
102 queue_pos_matches(guint queue_pos, const gchar *fname, gint pos)
104 if (queue_pos < g_queue_get_length(navigation_queue))
106 filepos *fpos = g_queue_peek_nth(navigation_queue, queue_pos);
108 return (utils_str_equal(fpos->file, fname) && fpos->pos == pos);
110 return FALSE;
114 static void add_new_position(const gchar *utf8_filename, gint pos)
116 filepos *npos;
117 guint i;
119 if (queue_pos_matches(nav_queue_pos, utf8_filename, pos))
120 return; /* prevent duplicates */
122 npos = g_new0(filepos, 1);
123 npos->file = utf8_filename;
124 npos->pos = pos;
126 /* if we've jumped to a new position from inside the queue rather than going forward */
127 if (nav_queue_pos > 0)
129 for (i = 0; i < nav_queue_pos; i++)
131 g_free(g_queue_pop_head(navigation_queue));
133 nav_queue_pos = 0;
135 g_queue_push_head(navigation_queue, npos);
136 adjust_buttons();
141 * Adds old file position and new file position to the navqueue, then goes to the new position.
143 * @param old_doc The document of the previous position, if set as invalid (@c NULL) then no old
144 * position is set
145 * @param new_doc The document of the new position, must be valid.
146 * @param line the line number of the new position. It is counted with 1 as the first line, not 0.
148 * @return @c TRUE if the cursor has changed the position to @a line or @c FALSE otherwise.
150 GEANY_API_SYMBOL
151 gboolean navqueue_goto_line(GeanyDocument *old_doc, GeanyDocument *new_doc, gint line)
153 gint pos;
155 g_return_val_if_fail(old_doc == NULL || old_doc->is_valid, FALSE);
156 g_return_val_if_fail(DOC_VALID(new_doc), FALSE);
157 g_return_val_if_fail(line >= 1, FALSE);
159 pos = sci_get_position_from_line(new_doc->editor->sci, line - 1);
161 /* first add old file position */
162 if (old_doc != NULL && old_doc->file_name)
164 gint cur_pos = sci_get_current_position(old_doc->editor->sci);
166 add_new_position(old_doc->file_name, cur_pos);
169 /* now add new file position */
170 if (new_doc->file_name)
172 add_new_position(new_doc->file_name, pos);
175 return editor_goto_pos(new_doc->editor, pos, TRUE);
179 static gboolean goto_file_pos(const gchar *file, gint pos)
181 GeanyDocument *doc = document_find_by_filename(file);
183 if (doc == NULL)
184 return FALSE;
186 return editor_goto_pos(doc->editor, pos, TRUE);
190 void navqueue_go_back(void)
192 filepos *fprev;
193 GeanyDocument *doc = document_get_current();
195 /* If the navqueue is currently at some position A, but the actual cursor is at some other
196 * place B, we should add B to the navqueue, so that (1) we go back to A, not to the next
197 * item in the queue; and (2) we can later restore B by going forward.
198 * (If A = B, add_new_position will ignore it.) */
199 if (doc)
201 if (doc->file_name)
202 add_new_position(doc->file_name, sci_get_current_position(doc->editor->sci));
204 else
205 /* see also https://github.com/geany/geany/pull/1537 */
206 g_warning("Attempted navigation when nothing is open");
208 /* return if theres no place to go back to */
209 if (g_queue_is_empty(navigation_queue) ||
210 nav_queue_pos >= g_queue_get_length(navigation_queue) - 1)
211 return;
213 /* jump back */
214 fprev = g_queue_peek_nth(navigation_queue, nav_queue_pos + 1);
215 if (goto_file_pos(fprev->file, fprev->pos))
217 nav_queue_pos++;
219 else
221 /** TODO: add option to re open the file */
222 g_free(g_queue_pop_nth(navigation_queue, nav_queue_pos + 1));
224 adjust_buttons();
228 void navqueue_go_forward(void)
230 filepos *fnext;
232 if (nav_queue_pos < 1 ||
233 nav_queue_pos >= g_queue_get_length(navigation_queue))
234 return;
236 /* jump forward */
237 fnext = g_queue_peek_nth(navigation_queue, nav_queue_pos - 1);
238 if (goto_file_pos(fnext->file, fnext->pos))
240 nav_queue_pos--;
242 else
244 /** TODO: add option to re open the file */
245 g_free(g_queue_pop_nth(navigation_queue, nav_queue_pos - 1));
248 adjust_buttons();
252 static gint find_by_filename(gconstpointer a, gconstpointer b)
254 if (utils_str_equal(((const filepos*)a)->file, (const gchar*) b))
255 return 0;
256 else
257 return 1;
261 /* Remove all elements with the given filename */
262 void navqueue_remove_file(const gchar *filename)
264 GList *match;
266 if (filename == NULL)
267 return;
269 while ((match = g_queue_find_custom(navigation_queue, filename, find_by_filename)))
271 g_free(match->data);
272 g_queue_delete_link(navigation_queue, match);
275 adjust_buttons();