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
32 #include "geanyobject.h"
33 #include "sciwrappers.h"
40 /* for the navigation history queue */
43 const gchar
*file
; /* This is the document's filename, in UTF-8 */
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();
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
);
85 if (nav_queue_pos
== 0)
87 gtk_action_set_sensitive(navigation_buttons
[0], TRUE
);
88 gtk_action_set_sensitive(navigation_buttons
[1], FALSE
);
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
);
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
);
114 static void add_new_position(const gchar
*utf8_filename
, gint pos
)
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
;
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
));
135 g_queue_push_head(navigation_queue
, npos
);
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
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.
151 gboolean
navqueue_goto_line(GeanyDocument
*old_doc
, GeanyDocument
*new_doc
, gint line
)
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
);
186 return editor_goto_pos(doc
->editor
, pos
, TRUE
);
190 void navqueue_go_back(void)
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.) */
202 add_new_position(doc
->file_name
, sci_get_current_position(doc
->editor
->sci
));
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)
214 fprev
= g_queue_peek_nth(navigation_queue
, nav_queue_pos
+ 1);
215 if (goto_file_pos(fprev
->file
, fprev
->pos
))
221 /** TODO: add option to re open the file */
222 g_free(g_queue_pop_nth(navigation_queue
, nav_queue_pos
+ 1));
228 void navqueue_go_forward(void)
232 if (nav_queue_pos
< 1 ||
233 nav_queue_pos
>= g_queue_get_length(navigation_queue
))
237 fnext
= g_queue_peek_nth(navigation_queue
, nav_queue_pos
- 1);
238 if (goto_file_pos(fnext
->file
, fnext
->pos
))
244 /** TODO: add option to re open the file */
245 g_free(g_queue_pop_nth(navigation_queue
, nav_queue_pos
- 1));
252 static gint
find_by_filename(gconstpointer a
, gconstpointer b
)
254 if (utils_str_equal(((const filepos
*)a
)->file
, (const gchar
*) b
))
261 /* Remove all elements with the given filename */
262 void navqueue_remove_file(const gchar
*filename
)
266 if (filename
== NULL
)
269 while ((match
= g_queue_find_custom(navigation_queue
, filename
, find_by_filename
)))
272 g_queue_delete_link(navigation_queue
, match
);