girara
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros
input-history.c
Go to the documentation of this file.
1 /* See LICENSE file for license and copyright information */
2 
3 #include "input-history.h"
4 #include "datastructures.h"
5 
6 G_DEFINE_TYPE(GiraraInputHistory, girara_input_history, G_TYPE_OBJECT)
7 
8 
11 typedef struct ih_private_s {
12  girara_list_t* history;
13  bool reset;
14  size_t current;
15  size_t current_match;
17  char* command_line;
18 } ih_private_t;
19 
20 #define GIRARA_INPUT_HISTORY_GET_PRIVATE(obj) \
21  (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GIRARA_TYPE_INPUT_HISTORY, \
22  ih_private_t))
23 
24 /* Methods */
25 static void ih_dispose(GObject* object);
26 static void ih_finalize(GObject* object);
27 static void ih_set_property(GObject* object, guint prop_id,
28  const GValue* value, GParamSpec* pspec);
29 static void ih_get_property(GObject* object, guint prop_id, GValue* value,
30  GParamSpec* pspec);
31 static void ih_append(GiraraInputHistory* history, const char* input);
32 static girara_list_t* ih_list(GiraraInputHistory* history);
33 static const char* ih_next(GiraraInputHistory* history,
34  const char* current_input);
35 static const char* ih_previous(GiraraInputHistory* history,
36  const char* current_input);
37 static void ih_reset(GiraraInputHistory* history);
38 
39 /* Properties */
40 enum {
43 };
44 
45 /* Class init */
46 static void
47 girara_input_history_class_init(GiraraInputHistoryClass* class)
48 {
49  /* add private members */
50  g_type_class_add_private(class, sizeof(ih_private_t));
51 
52  /* overwrite methods */
53  GObjectClass* object_class = G_OBJECT_CLASS(class);
54  object_class->dispose = ih_dispose;
55  object_class->finalize = ih_finalize;
56  object_class->set_property = ih_set_property;
57  object_class->get_property = ih_get_property;
58 
59  class->append = ih_append;
60  class->list = ih_list;
61  class->next = ih_next;
62  class->previous = ih_previous;
63  class->reset = ih_reset;
64 
65  /* properties */
66  g_object_class_install_property(object_class, PROP_IO,
67  g_param_spec_object("io", "history reader/writer",
68  "GiraraInputHistoryIO object used to read and write history",
70  G_PARAM_WRITABLE | G_PARAM_READABLE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
71 }
72 
73 /* Object init */
74 static void
75 girara_input_history_init(GiraraInputHistory* history)
76 {
79  priv->reset = true;
80  priv->io = NULL;
81 }
82 
83 /* GObject dispose */
84 static void
85 ih_dispose(GObject* object)
86 {
88 
89  g_clear_object(&priv->io);
90 
91  G_OBJECT_CLASS(girara_input_history_parent_class)->dispose(object);
92 }
93 
94 /* GObject finalize */
95 static void
96 ih_finalize(GObject* object)
97 {
100  g_free(priv->command_line);
101 
102  G_OBJECT_CLASS(girara_input_history_parent_class)->finalize(object);
103 }
104 
105 /* GObject set_property */
106 static void
107 ih_set_property(GObject* object, guint prop_id, const GValue* value,
108  GParamSpec* pspec)
109 {
111 
112  switch (prop_id) {
113  case PROP_IO: {
114  if (priv->io != NULL) {
115  g_object_unref(priv->io);
116  }
117 
118  gpointer* tmp = g_value_dup_object(value);
119  if (tmp != NULL) {
120  priv->io = GIRARA_INPUT_HISTORY_IO(tmp);
121  } else {
122  priv->io = NULL;
123  }
125  break;
126  }
127  default:
128  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
129  }
130 }
131 
132 /* GObject get_property */
133 static void
134 ih_get_property(GObject* object, guint prop_id, GValue* value,
135  GParamSpec* pspec)
136 {
138 
139  switch (prop_id) {
140  case PROP_IO:
141  g_value_set_object(value, priv->io);
142  break;
143  default:
144  G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
145  }
146 }
147 
148 /* Object new */
149 GiraraInputHistory*
151 {
152  return GIRARA_INPUT_HISTORY(g_object_new(GIRARA_TYPE_INPUT_HISTORY, "io",
153  io, NULL));
154 }
155 
156 /* Method implementions */
157 
158 static void
159 ih_append(GiraraInputHistory* history, const char* input)
160 {
161  if (input == NULL) {
162  return;
163  }
164 
165  girara_list_t* list = girara_input_history_list(history);
166  if (list == NULL) {
167  return;
168  }
169 
170  void* data = NULL;
171  while ((data = girara_list_find(list, (girara_compare_function_t) g_strcmp0, data)) != NULL) {
172  girara_list_remove(list, data);
173  }
174 
175  girara_list_append(list, g_strdup(input));
176 
178  if (priv->io != NULL) {
179  girara_input_history_io_append(priv->io, input);
180  }
181 
182  /* begin from the last command when navigating through history */
184 }
185 
186 static girara_list_t*
187 ih_list(GiraraInputHistory* history)
188 {
190  return priv->history;
191 }
192 
193 static const char*
194 find_next(GiraraInputHistory* history, const char* current_input, bool next)
195 {
197 
198  girara_list_t* list = girara_input_history_list(history);
199  if (list == NULL) {
200  return NULL;
201  }
202 
203  size_t length = girara_list_size(list);
204  if (length == 0) {
205  return NULL;
206  }
207 
208  if (priv->reset == true) {
209  priv->current = length;
210  priv->current_match = priv->current;
211  }
212 
213  /* Before moving into the history, save the current command-line. */
214  if (priv->current_match == length) {
215  g_free(priv->command_line);
216  priv->command_line = g_strdup(current_input);
217  }
218 
219  size_t i = 0;
220  const char* command = NULL;
221  while (i < length) {
222  if (priv->reset == true || next == false) {
223  if (priv->current < 1) {
224  priv->reset = false;
225  priv->current = priv->current_match;
226  return NULL;
227  } else {
228  --priv->current;
229  }
230  } else if (next == true) {
231  if (priv->current + 1 >= length) {
232  /* At the bottom of the history, return what the command-line was. */
233  priv->current_match = length;
234  priv->current = priv->current_match;
235  return priv->command_line;
236  } else {
237  ++priv->current;
238  }
239  } else {
240  return NULL;
241  }
242 
243  command = girara_list_nth(list, priv->current);
244  if (command == NULL) {
245  return NULL;
246  }
247 
248  /* Only match history items starting with what was on the command-line. */
249  if (g_str_has_prefix(command, priv->command_line)) {
250  priv->reset = false;
251  priv->current_match = priv->current;
252  break;
253  }
254 
255  ++i;
256  }
257 
258  if (i == length) {
259  return NULL;
260  }
261 
262  return command;
263 }
264 
265 static const char*
266 ih_next(GiraraInputHistory* history, const char* current_input)
267 {
268  return find_next(history, current_input, true);
269 }
270 
271 static const char*
272 ih_previous(GiraraInputHistory* history, const char* current_input)
273 {
274  return find_next(history, current_input, false);
275 }
276 
277 static void
278 ih_reset(GiraraInputHistory* history)
279 {
281  priv->reset = true;
282 
283  if (priv->io != NULL) {
284  girara_list_t* list = girara_input_history_list(history);
285  if (list == NULL) {
286  return;
287  }
288  girara_list_clear(list);
289 
290  girara_list_t* newlist = girara_input_history_io_read(priv->io);
291  if (newlist != NULL) {
292  GIRARA_LIST_FOREACH(newlist, const char*, iter, data)
293  girara_list_append(list, g_strdup(data));
294  GIRARA_LIST_FOREACH_END(newlist, const char*, iter, data);
295  girara_list_free(newlist);
296  }
297  }
298 }
299 
300 /* Wrapper functions for the members */
301 
302 void
303 girara_input_history_append(GiraraInputHistory* history, const char* input)
304 {
305  g_return_if_fail(GIRARA_IS_INPUT_HISTORY(history) == true);
306  GIRARA_INPUT_HISTORY_GET_CLASS(history)->append(history, input);
307 }
308 
309 girara_list_t*
310 girara_input_history_list(GiraraInputHistory* history)
311 {
312  g_return_val_if_fail(GIRARA_IS_INPUT_HISTORY(history) == true, NULL);
313  return GIRARA_INPUT_HISTORY_GET_CLASS(history)->list(history);
314 }
315 
316 const char*
317 girara_input_history_next(GiraraInputHistory* history, const char* current_input)
318 {
319  g_return_val_if_fail(GIRARA_IS_INPUT_HISTORY(history) == true, NULL);
320  return GIRARA_INPUT_HISTORY_GET_CLASS(history)->next(history, current_input);
321 }
322 
323 const char*
324 girara_input_history_previous(GiraraInputHistory* history, const char* current_input)
325 {
326  g_return_val_if_fail(GIRARA_IS_INPUT_HISTORY(history) == true, NULL);
327  return GIRARA_INPUT_HISTORY_GET_CLASS(history)->previous(history, current_input);
328 }
329 
330 void
331 girara_input_history_reset(GiraraInputHistory* history)
332 {
333  g_return_if_fail(GIRARA_IS_INPUT_HISTORY(history) == true);
334  GIRARA_INPUT_HISTORY_GET_CLASS(history)->reset(history);
335 }
void girara_list_remove(girara_list_t *list, void *data)
void * girara_list_nth(girara_list_t *list, size_t n)
#define GIRARA_INPUT_HISTORY(obj)
void girara_list_append(girara_list_t *list, void *data)
GType girara_input_history_io_get_type(void)
void(* girara_free_function_t)(void *data)
Definition: types.h:118
size_t girara_list_size(girara_list_t *list)
girara_list_t * girara_list_new2(girara_free_function_t gfree)
#define GIRARA_INPUT_HISTORY_IO(obj)
Definition: input-history.h:39
void girara_input_history_io_append(GiraraInputHistoryIO *io, const char *input)
#define GIRARA_INPUT_HISTORY_GET_PRIVATE(obj)
Definition: input-history.c:20
void girara_list_free(girara_list_t *list)
struct girara_input_history_io_s GiraraInputHistoryIO
Definition: types.h:213
void * girara_list_find(girara_list_t *list, girara_compare_function_t compare, const void *data)
size_t current
Definition: input-history.c:14
int(* girara_compare_function_t)(const void *data1, const void *data2)
Definition: types.h:134
GiraraInputHistoryIO * io
Definition: input-history.c:16
void girara_input_history_reset(GiraraInputHistory *history)
const char * girara_input_history_previous(GiraraInputHistory *history, const char *current_input)
#define GIRARA_IS_INPUT_HISTORY(obj)
girara_list_t * girara_input_history_list(GiraraInputHistory *history)
char * command_line
Definition: input-history.c:17
girara_list_t * history
Definition: input-history.c:12
const char * girara_input_history_next(GiraraInputHistory *history, const char *current_input)
size_t current_match
Definition: input-history.c:15
girara_list_t * girara_input_history_io_read(GiraraInputHistoryIO *io)
#define GIRARA_INPUT_HISTORY_GET_CLASS(obj)
GiraraInputHistory * girara_input_history_new(GiraraInputHistoryIO *io)
void girara_list_clear(girara_list_t *list)
#define GIRARA_TYPE_INPUT_HISTORY
void girara_input_history_append(GiraraInputHistory *history, const char *input)
#define GIRARA_LIST_FOREACH_END(list, type, iter, data)
#define GIRARA_LIST_FOREACH(list, type, iter, data)