1 /*
2 * fm-nav-history.c
3 *
4 * Copyright 2009 PCMan <pcman.tw@gmail.com>
5 * Copyright 2012 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
6 *
7 * This file is a part of the Libfm library.
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 /**
25 * SECTION:fm-nav-history
26 * @short_description: Simple navigation history management.
27 * @title: FmNavHistory
28 *
29 * @include: libfm/fm.h
30 *
31 * The #FmNavHistory object implements history for paths that were
32 * entered in some input bar and allows to add, remove or move items in it.
33 */
34
35 #include "fm-nav-history.h"
36
37 struct _FmNavHistory
38 {
39 GObject parent;
40 GQueue items;
41 GList* cur;
42 gint n_max;
43 guint n_cur;
44 };
45
46 struct _FmNavHistoryClass
47 {
48 GObjectClass parent_class;
49 };
50
51 static void fm_nav_history_finalize (GObject *object);
52
53 G_DEFINE_TYPE(FmNavHistory, fm_nav_history, G_TYPE_OBJECT);
54
55
fm_nav_history_class_init(FmNavHistoryClass * klass)56 static void fm_nav_history_class_init(FmNavHistoryClass *klass)
57 {
58 GObjectClass *g_object_class;
59
60 g_object_class = G_OBJECT_CLASS(klass);
61 g_object_class->finalize = fm_nav_history_finalize;
62 }
63
fm_nav_history_item_free(FmNavHistoryItem * item)64 static void fm_nav_history_item_free(FmNavHistoryItem* item)
65 {
66 fm_path_unref(item->path);
67 g_slice_free(FmNavHistoryItem, item);
68 }
69
fm_nav_history_finalize(GObject * object)70 static void fm_nav_history_finalize(GObject *object)
71 {
72 FmNavHistory *self;
73
74 g_return_if_fail(object != NULL);
75 g_return_if_fail(FM_IS_NAV_HISTORY(object));
76
77 self = FM_NAV_HISTORY(object);
78 g_queue_foreach(&self->items, (GFunc)fm_nav_history_item_free, NULL);
79 g_queue_clear(&self->items);
80
81 G_OBJECT_CLASS(fm_nav_history_parent_class)->finalize(object);
82 }
83
fm_nav_history_init(FmNavHistory * self)84 static void fm_nav_history_init(FmNavHistory *self)
85 {
86 g_queue_init(&self->items);
87 self->n_max = FM_NAV_HISTORY_DEFAULT_SIZE;
88 }
89
90 /**
91 * fm_nav_history_new
92 *
93 * Creates a new #FmNavHistory object with empty history.
94 *
95 * Returns: a new #FmNavHistory object.
96 *
97 * Since: 0.1.0
98 */
fm_nav_history_new(void)99 FmNavHistory *fm_nav_history_new(void)
100 {
101 return g_object_new(FM_NAV_HISTORY_TYPE, NULL);
102 }
103
104 /**
105 * fm_nav_history_list
106 * @nh: the history
107 *
108 * Retrieves full list of the history as #GList of #FmNavHistoryItem.
109 * The returned #GList belongs to #FmNavHistory and shouldn't be freed.
110 *
111 * Returns: (transfer none) (element-type FmNavHistoryItem): full history.
112 *
113 * Since: 0.1.0
114 *
115 * Deprecated: 1.0.2:
116 */
fm_nav_history_list(FmNavHistory * nh)117 const GList* fm_nav_history_list(FmNavHistory* nh)
118 {
119 return nh->items.head;
120 }
121
122 /**
123 * fm_nav_history_get_cur
124 * @nh: the history
125 *
126 * Retrieves current selected item of the history. The returned item
127 * belongs to #FmNavHistory and shouldn't be freed by caller.
128 *
129 * Returns: (transfer none): current item.
130 *
131 * Since: 0.1.0
132 *
133 * Deprecated: 1.0.2: Use fm_nav_history_get_nth_path() instead.
134 */
fm_nav_history_get_cur(FmNavHistory * nh)135 const FmNavHistoryItem* fm_nav_history_get_cur(FmNavHistory* nh)
136 {
137 return nh->cur ? (FmNavHistoryItem*)nh->cur->data : NULL;
138 }
139
140 /**
141 * fm_nav_history_get_cur_link
142 * @nh: the history
143 *
144 * Retrieves current selected item as #GList element containing
145 * #FmNavHistoryItem. The returned item belongs to #FmNavHistory and
146 * shouldn't be freed by caller.
147 *
148 * Returns: (transfer none) (element-type FmNavHistoryItem): current item.
149 *
150 * Since: 0.1.0
151 *
152 * Deprecated: 1.0.2:
153 */
fm_nav_history_get_cur_link(FmNavHistory * nh)154 const GList* fm_nav_history_get_cur_link(FmNavHistory* nh)
155 {
156 return nh->cur;
157 }
158
159 /**
160 * fm_nav_history_can_forward
161 * @nh: the history
162 *
163 * Checks if current selected item is the last item in the history.
164 *
165 * Before 1.0.0 this call had name fm_nav_history_get_can_forward.
166 *
167 * Returns: %TRUE if cursor can go forward in history.
168 *
169 * Since: 0.1.0
170 *
171 * Deprecated: 1.0.2: Use fm_nav_history_get_cur_index() instead.
172 */
fm_nav_history_can_forward(FmNavHistory * nh)173 gboolean fm_nav_history_can_forward(FmNavHistory* nh)
174 {
175 return nh->n_cur > 0;
176 }
177
178 /**
179 * fm_nav_history_forward
180 * @nh: the history
181 * @old_scroll_pos: the scroll position to associate with current item
182 *
183 * If there is a previous item in the history then sets @old_scroll_pos
184 * into current item data and marks previous item current.
185 *
186 * Since: 0.1.0
187 *
188 * Deprecated: 1.0.2: Use fm_nav_history_go_to() instead.
189 */
fm_nav_history_forward(FmNavHistory * nh,int old_scroll_pos)190 void fm_nav_history_forward(FmNavHistory* nh, int old_scroll_pos)
191 {
192 if(nh->cur && nh->cur->prev)
193 {
194 FmNavHistoryItem* tmp = (FmNavHistoryItem*)nh->cur->data;
195 if(tmp) /* remember current scroll pos */
196 tmp->scroll_pos = old_scroll_pos;
197 nh->cur = nh->cur->prev;
198 nh->n_cur--;
199 }
200 }
201
202 /**
203 * fm_nav_history_can_back
204 * @nh: the history
205 *
206 * Checks if current selected item is the first item in the history.
207 *
208 * Before 1.0.0 this call had name fm_nav_history_get_can_back.
209 *
210 * Returns: %TRUE if cursor can go backward in history.
211 *
212 * Since: 0.1.0
213 */
fm_nav_history_can_back(FmNavHistory * nh)214 gboolean fm_nav_history_can_back(FmNavHistory* nh)
215 {
216 g_return_val_if_fail(nh != NULL && FM_IS_NAV_HISTORY(nh), FALSE);
217
218 return nh->cur ? (nh->cur->next != NULL) : FALSE;
219 }
220
221 /**
222 * fm_nav_history_back
223 * @nh: the history
224 * @old_scroll_pos: the scroll position to associate with current item
225 *
226 * If there is a next item in the history then sets @old_scroll_pos into
227 * current item data and marks next item current.
228 *
229 * Since: 0.1.0
230 *
231 * Deprecated: 1.0.2: Use fm_nav_history_go_to() instead.
232 */
fm_nav_history_back(FmNavHistory * nh,int old_scroll_pos)233 void fm_nav_history_back(FmNavHistory* nh, int old_scroll_pos)
234 {
235 if(nh->cur && nh->cur->next)
236 {
237 FmNavHistoryItem* tmp = (FmNavHistoryItem*)nh->cur->data;
238 if(tmp) /* remember current scroll pos */
239 tmp->scroll_pos = old_scroll_pos;
240 nh->cur = nh->cur->next;
241 nh->n_cur++;
242 }
243 }
244
cut_history(FmNavHistory * nh,guint num)245 static inline void cut_history(FmNavHistory* nh, guint num)
246 {
247 while(g_queue_get_length(&nh->items) > num)
248 {
249 FmNavHistoryItem* item = (FmNavHistoryItem*)g_queue_pop_tail(&nh->items);
250 fm_nav_history_item_free(item);
251 }
252 }
253
254 /**
255 * fm_nav_history_chdir
256 * @nh: the history
257 * @path: new path to add
258 * @old_scroll_pos: the scroll position to associate with current item
259 *
260 * Sets @old_scroll_pos into current item data and then adds new @path
261 * to the beginning of the @nh.
262 *
263 * Since: 0.1.0
264 */
fm_nav_history_chdir(FmNavHistory * nh,FmPath * path,gint old_scroll_pos)265 void fm_nav_history_chdir(FmNavHistory* nh, FmPath* path, gint old_scroll_pos)
266 {
267 FmNavHistoryItem* tmp;
268
269 g_return_if_fail(nh != NULL && FM_IS_NAV_HISTORY(nh));
270
271 /* now we're at the top of the queue. */
272 tmp = nh->cur ? (FmNavHistoryItem*)nh->cur->data : NULL;
273 if(tmp) /* remember current scroll pos */
274 tmp->scroll_pos = old_scroll_pos;
275
276 if( !tmp || !fm_path_equal(tmp->path, path) ) /* we're not chdir to the same path */
277 {
278 /* if we're not at the top of the queue, remove all items beyond us. */
279 while(nh->n_cur > 0)
280 {
281 tmp = (FmNavHistoryItem*)g_queue_pop_head(&nh->items);
282 if(tmp)
283 fm_nav_history_item_free(tmp);
284 nh->n_cur--;
285 }
286
287 /* add a new item */
288 tmp = g_slice_new0(FmNavHistoryItem);
289 tmp->path = fm_path_ref(path);
290 g_queue_push_head(&nh->items, tmp);
291 nh->cur = g_queue_peek_head_link(&nh->items);
292 cut_history(nh, nh->n_max);
293 }
294 }
295
296 /**
297 * fm_nav_history_jump
298 * @nh: the history
299 * @l: (element-type FmNavHistoryItem): new current item
300 * @old_scroll_pos: the scroll position to associate with current item
301 *
302 * Sets @old_scroll_pos into current item data and then
303 * sets current item of @nh to one from @l.
304 *
305 * Since: 0.1.0
306 *
307 * Deprecated: 1.0.2: Use fm_nav_history_go_to() instead.
308 */
fm_nav_history_jump(FmNavHistory * nh,GList * l,int old_scroll_pos)309 void fm_nav_history_jump(FmNavHistory* nh, GList* l, int old_scroll_pos)
310 {
311 gint n = g_queue_index(&nh->items, l->data);
312 if(n >= 0)
313 fm_nav_history_go_to(nh, n, old_scroll_pos);
314 }
315
316 /**
317 * fm_nav_history_clear
318 * @nh: the history
319 *
320 * Removes all items from the history @nh.
321 *
322 * Since: 0.1.0
323 */
fm_nav_history_clear(FmNavHistory * nh)324 void fm_nav_history_clear(FmNavHistory* nh)
325 {
326 g_return_if_fail(nh != NULL && FM_IS_NAV_HISTORY(nh));
327 g_queue_foreach(&nh->items, (GFunc)fm_nav_history_item_free, NULL);
328 g_queue_clear(&nh->items);
329 nh->cur = NULL;
330 nh->n_cur = 0;
331 }
332
333 /**
334 * fm_nav_history_set_max
335 * @nh: the history
336 * @num: new size of history
337 *
338 * Sets maximum length of the history @nh to be @num.
339 *
340 * Since: 0.1.0
341 */
fm_nav_history_set_max(FmNavHistory * nh,guint num)342 void fm_nav_history_set_max(FmNavHistory* nh, guint num)
343 {
344 g_return_if_fail(nh != NULL && FM_IS_NAV_HISTORY(nh));
345 if(num <= nh->n_cur)
346 {
347 nh->cur = NULL;
348 nh->n_cur = 0;
349 }
350 nh->n_max = num;
351 if(num < 1)
352 num = 1;
353 cut_history(nh, num);
354 }
355
356 /**
357 * fm_nav_history_get_cur_index
358 * @nh: the history
359 *
360 * Retrieves index of current item in the history @nh. 0 means current
361 * item is at top.
362 *
363 * Returns: index of current item.
364 *
365 * Since: 1.0.2
366 */
fm_nav_history_get_cur_index(FmNavHistory * nh)367 guint fm_nav_history_get_cur_index(FmNavHistory* nh)
368 {
369 g_return_val_if_fail(nh != NULL && FM_IS_NAV_HISTORY(nh), 0);
370 return nh->n_cur;
371 }
372
373 /**
374 * fm_nav_history_get_nth_path
375 * @nh: the history
376 * @n: index of item
377 *
378 * Retrieves path of the item @n in the history @nh.
379 *
380 * Returns: path of the item or %NULL if no such item was found.
381 *
382 * Since: 1.0.2
383 */
fm_nav_history_get_nth_path(FmNavHistory * nh,guint n)384 FmPath* fm_nav_history_get_nth_path(FmNavHistory* nh, guint n)
385 {
386 FmNavHistoryItem *item;
387
388 g_debug("fm_nav_history_get_nth_path %u", n);
389 g_return_val_if_fail(nh != NULL && FM_IS_NAV_HISTORY(nh), NULL);
390 if(n == nh->n_cur)
391 item = nh->cur->data;
392 else
393 item = g_queue_peek_nth(&nh->items, n);
394 if(item == NULL)
395 return NULL;
396 return item->path;
397 }
398
399 /**
400 * fm_nav_history_go_to
401 * @nh: the history
402 * @n: new index
403 * @old_scroll_pos: scroll position of current folder view
404 *
405 * Saves the current scroll position into the history. If item with index
406 * @n exists in the history then sets it as current item.
407 *
408 * Returns: path of selected item or %NULL if no such item was found.
409 *
410 * Since: 1.0.2
411 */
fm_nav_history_go_to(FmNavHistory * nh,guint n,gint old_scroll_pos)412 FmPath* fm_nav_history_go_to(FmNavHistory* nh, guint n, gint old_scroll_pos)
413 {
414 GList *link;
415
416 g_return_val_if_fail(nh != NULL && FM_IS_NAV_HISTORY(nh), NULL);
417 if(nh->cur)
418 ((FmNavHistoryItem*)nh->cur->data)->scroll_pos = old_scroll_pos;
419 if(n == nh->n_cur)
420 return ((FmNavHistoryItem*)nh->cur->data)->path;
421 link = g_queue_peek_nth_link(&nh->items, n);
422 if(link == NULL)
423 return NULL;
424 nh->n_cur = n;
425 nh->cur = link;
426 return ((FmNavHistoryItem*)link->data)->path;
427 }
428
429 /**
430 * fm_nav_history_get_scroll_pos
431 * @nh: the history
432 *
433 * Retrieves saved scroll position for current item.
434 *
435 * Returns: saved scroll position.
436 *
437 * Since: 1.0.2
438 */
fm_nav_history_get_scroll_pos(FmNavHistory * nh)439 gint fm_nav_history_get_scroll_pos(FmNavHistory* nh)
440 {
441 g_return_val_if_fail(nh != NULL && FM_IS_NAV_HISTORY(nh), -1);
442 return ((FmNavHistoryItem*)nh->cur->data)->scroll_pos;
443 }
444