girara
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros
session.c
Go to the documentation of this file.
1 /* See LICENSE file for license and copyright information */
2 
3 #include <stdlib.h>
4 #include <glib/gi18n-lib.h>
5 
6 #include "session.h"
7 #include "settings.h"
8 #include "datastructures.h"
9 #include "internal.h"
10 #include "commands.h"
11 #include "callbacks.h"
12 #include "shortcuts.h"
13 #include "config.h"
14 #include "utils.h"
15 #include "input-history.h"
16 
17 #if GTK_MAJOR_VERSION == 2
18 #include "gtk2-compat.h"
19 #endif
20 
21 #if defined(__GNUC__) || defined(__clang__)
22 #define DO_PRAGMA(x) _Pragma(#x)
23 #else
24 #define DO_PRAGMA(x)
25 #endif
26 
27 #define IGNORE_DEPRECATED \
28  DO_PRAGMA(GCC diagnostic push) \
29  DO_PRAGMA(GCC diagnostic ignored "-Wdeprecated-declarations")
30 #define UNIGNORE \
31  DO_PRAGMA(GCC diagnostic pop)
32 
33 static int
34 cb_sort_settings(girara_setting_t* lhs, girara_setting_t* rhs)
35 {
36  return g_strcmp0(girara_setting_get_name(lhs), girara_setting_get_name(rhs));
37 }
38 
39 static void
40 ensure_gettext_initialized(void)
41 {
42  static gsize initialized = 0;
43  if (g_once_init_enter(&initialized) == TRUE) {
44  bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
45  bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
46  g_once_init_leave(&initialized, 1);
47  }
48 }
49 
50 girara_session_t*
52 {
53  ensure_gettext_initialized();
54 
55  girara_session_t* session = g_slice_alloc0(sizeof(girara_session_t));
56  session->private_data = g_slice_alloc0(sizeof(girara_session_private_t));
57 
58  /* init values */
59  session->bindings.mouse_events = girara_list_new2(
61  session->bindings.commands = girara_list_new2(
63  session->bindings.special_commands = girara_list_new2(
65  session->bindings.shortcuts = girara_list_new2(
67  session->bindings.inputbar_shortcuts = girara_list_new2(
69 
70  session->elements.statusbar_items = girara_list_new2(
72 
73  /* settings */
74  session->private_data->settings = girara_sorted_list_new2(
75  (girara_compare_function_t) cb_sort_settings,
77 
78  /* init modes */
79  session->modes.identifiers = girara_list_new2(
81  girara_mode_t normal_mode = girara_mode_add(session, "normal");
82  girara_mode_t inputbar_mode = girara_mode_add(session, "inputbar");
83  session->modes.normal = normal_mode;
84  session->modes.current_mode = normal_mode;
85  session->modes.inputbar = inputbar_mode;
86 
87  /* config handles */
88  session->config.handles = girara_list_new2(
90  session->config.shortcut_mappings = girara_list_new2(
92  session->config.argument_mappings = girara_list_new2(
94 
95  /* command history */
96  session->command_history = girara_input_history_new(NULL);
97 
98  /* load default values */
100 
101  /* create widgets */
102 #if GTK_MAJOR_VERSION == 2
103  session->gtk.box = GTK_BOX(gtk_vbox_new(FALSE, 0));
104  session->private_data->gtk.bottom_box = GTK_BOX(gtk_vbox_new(FALSE, 0));
105  session->gtk.statusbar_entries = GTK_BOX(gtk_hbox_new(FALSE, 0));
106  session->gtk.tabbar = gtk_hbox_new(TRUE, 0);
107  session->gtk.inputbar_box = GTK_BOX(gtk_hbox_new(TRUE, 0));
108 #else
109  session->gtk.box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));
110  session->private_data->gtk.overlay = gtk_overlay_new();
111  session->private_data->gtk.bottom_box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));
112  session->gtk.statusbar_entries = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0));
113  session->gtk.tabbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
114  session->gtk.inputbar_box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0));
115  gtk_box_set_homogeneous(GTK_BOX(session->gtk.tabbar), TRUE);
116  gtk_box_set_homogeneous(session->gtk.inputbar_box, TRUE);
117 #endif
118  session->gtk.view = gtk_scrolled_window_new(NULL, NULL);
119  session->gtk.viewport = gtk_viewport_new(NULL, NULL);
120  session->gtk.statusbar = gtk_event_box_new();
121  session->gtk.notification_area = gtk_event_box_new();
122  session->gtk.notification_text = gtk_label_new(NULL);
123  session->gtk.inputbar_dialog = GTK_LABEL(gtk_label_new(NULL));
124  session->gtk.inputbar_entry = GTK_ENTRY(gtk_entry_new());
125  session->gtk.inputbar = gtk_event_box_new();
126  session->gtk.tabs = GTK_NOTEBOOK(gtk_notebook_new());
127 
128  /* deprecated members */
130  session->settings = session->private_data->settings;
131  session->global.command_history = girara_get_command_history(session);
132  UNIGNORE
133 
134  return session;
135 }
136 
137 bool
138 girara_session_init(girara_session_t* session, const char* sessionname)
139 {
140  if (session == NULL) {
141  return false;
142  }
143 
144  /* window */
145  if (session->gtk.embed){
146  session->gtk.window = gtk_plug_new(session->gtk.embed);
147  } else {
148  session->gtk.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
149  }
150 
151  if (sessionname != NULL) {
152  gtk_widget_set_name(GTK_WIDGET(session->gtk.window), sessionname);
153  }
154 
155  GdkGeometry hints = {
156  .base_height = 1,
157  .base_width = 1,
158  .height_inc = 0,
159  .max_aspect = 0,
160  .max_height = 0,
161  .max_width = 0,
162  .min_aspect = 0,
163  .min_height = 0,
164  .min_width = 0,
165  .width_inc = 0
166  };
167 
168  gtk_window_set_geometry_hints(GTK_WINDOW(session->gtk.window), NULL, &hints, GDK_HINT_MIN_SIZE);
169 
170 #if (GTK_MAJOR_VERSION == 3)
171  gtk_window_set_has_resize_grip(GTK_WINDOW(session->gtk.window), FALSE);
172 #endif
173 
174 #if (GTK_MAJOR_VERSION == 3)
175  gtk_widget_override_background_color(GTK_WIDGET(session->gtk.window), GTK_STATE_NORMAL, &(session->style.default_background));
176 #else
177  gtk_widget_modify_bg(GTK_WIDGET(session->gtk.window), GTK_STATE_NORMAL, &(session->style.default_background));
178 #endif
179 
180  /* view */
181  session->signals.view_key_pressed = g_signal_connect(G_OBJECT(session->gtk.view), "key-press-event",
182  G_CALLBACK(girara_callback_view_key_press_event), session);
183 
184  session->signals.view_button_press_event = g_signal_connect(G_OBJECT(session->gtk.view), "button-press-event",
185  G_CALLBACK(girara_callback_view_button_press_event), session);
186 
187  session->signals.view_button_release_event = g_signal_connect(G_OBJECT(session->gtk.view), "button-release-event",
188  G_CALLBACK(girara_callback_view_button_release_event), session);
189 
190  session->signals.view_motion_notify_event = g_signal_connect(G_OBJECT(session->gtk.view), "motion-notify-event",
192 
193  session->signals.view_scroll_event = g_signal_connect(G_OBJECT(session->gtk.view), "scroll-event",
194  G_CALLBACK(girara_callback_view_scroll_event), session);
195 
196  bool show_hscrollbar = false;
197  bool show_vscrollbar = false;
198 
199  girara_setting_get(session, "show-h-scrollbar", &show_hscrollbar);
200  girara_setting_get(session, "show-v-scrollbar", &show_vscrollbar);
201 
202 #if (GTK_MAJOR_VERSION == 3)
203  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(session->gtk.view), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
204 
205  GtkWidget *vscrollbar = gtk_scrolled_window_get_vscrollbar(GTK_SCROLLED_WINDOW(session->gtk.view));
206  GtkWidget *hscrollbar = gtk_scrolled_window_get_hscrollbar(GTK_SCROLLED_WINDOW(session->gtk.view));
207 
208  if (vscrollbar != NULL) {
209  gtk_widget_set_visible(GTK_WIDGET(vscrollbar), show_vscrollbar);
210  }
211 
212  if (hscrollbar != NULL) {
213  gtk_widget_set_visible(GTK_WIDGET(hscrollbar), show_hscrollbar);
214  }
215 #else
216  GtkPolicyType h_policy, v_policy;
217  h_policy = show_hscrollbar ? GTK_POLICY_AUTOMATIC : GTK_POLICY_NEVER;
218  v_policy = show_vscrollbar ? GTK_POLICY_AUTOMATIC : GTK_POLICY_NEVER;
219  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(session->gtk.view), h_policy, v_policy);
220 #endif
221 
222  /* viewport */
223  gtk_container_add(GTK_CONTAINER(session->gtk.view), session->gtk.viewport);
224  gtk_viewport_set_shadow_type(GTK_VIEWPORT(session->gtk.viewport), GTK_SHADOW_NONE);
225 
226 #if (GTK_MAJOR_VERSION == 3)
227  gtk_widget_override_background_color(GTK_WIDGET(session->gtk.viewport), GTK_STATE_NORMAL, &(session->style.default_background));
228 #else
229  gtk_widget_modify_bg(GTK_WIDGET(session->gtk.viewport), GTK_STATE_NORMAL, &(session->style.default_background));
230 #endif
231 
232  /* statusbar */
233  gtk_container_add(GTK_CONTAINER(session->gtk.statusbar), GTK_WIDGET(session->gtk.statusbar_entries));
234 
235  /* notification area */
236  gtk_container_add(GTK_CONTAINER(session->gtk.notification_area), GTK_WIDGET(session->gtk.notification_text));
237  gtk_misc_set_alignment(GTK_MISC(session->gtk.notification_text), 0.0, 0.5);
238  gtk_label_set_use_markup(GTK_LABEL(session->gtk.notification_text), TRUE);
239 
240  /* inputbar */
241  gtk_entry_set_has_frame(session->gtk.inputbar_entry, FALSE);
242  gtk_editable_set_editable(GTK_EDITABLE(session->gtk.inputbar_entry), TRUE);
243 
244  /* we want inputbar_entry the same height as notification_text and statusbar,
245  so that when inputbar_entry is hidden, the size of the bottom_box remains
246  the same. We need to get rid of the builtin padding in the GtkEntry
247  widget. */
248 
249  guint ypadding = 2; /* total amount of padding (top + bottom) */
250  guint leftpadding = 4; /* left padding */
251  girara_setting_get(session, "statusbar-padding", &ypadding);
252 
253 #if (GTK_MAJOR_VERSION == 3)
254  /* gtk_entry_set_inner_border is deprecated since gtk 3.4 and does nothing. */
255  GtkCssProvider* provider = gtk_css_provider_new();
256 
257  static const char CSS_PATTERN[] = "#bottom_box { border-style: none; margin: 0px 0px 0px 0px; padding:%dpx 0px %dpx %dpx; }";
258  char* css = g_strdup_printf(CSS_PATTERN, ypadding - ypadding/2, ypadding/2, leftpadding);
259  gtk_css_provider_load_from_data(provider, css, strlen(css), NULL);
260  g_free(css);
261 
262  GdkDisplay* display = gdk_display_get_default();
263  GdkScreen* screen = gdk_display_get_default_screen(display);
264  gtk_style_context_add_provider_for_screen(screen,
265  GTK_STYLE_PROVIDER(provider),
266  GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
267  g_object_unref(provider);
268 
269  gtk_widget_set_name(GTK_WIDGET(session->gtk.inputbar_entry), "bottom_box");
270  gtk_widget_set_name(GTK_WIDGET(session->gtk.notification_text), "bottom_box");
271 #else
272  GtkBorder inner_border = {
273  .left = leftpadding,
274  .right = 0,
275  .top = ypadding - ypadding/2,
276  .bottom = ypadding/2
277  };
278 
279  gtk_entry_set_inner_border(session->gtk.inputbar_entry, &inner_border);
280 
281  /* obtain the actual inputbar height */
282  /* TODO: for some reason does not match */
283  GtkRequisition req;
284  gtk_widget_size_request(GTK_WIDGET(session->gtk.inputbar_entry), &req);
285  /* have no idea where the extra 5 pixels come from. without this, the other
286  widgets get larger than the inputbar */
287  guint statusbar_height = req.height - 5;
288 
289  /* force all widgets to have the same height as inputbar */
290  gtk_widget_set_size_request(GTK_WIDGET(session->gtk.inputbar_entry), -1, statusbar_height);
291  gtk_widget_set_size_request(GTK_WIDGET(session->gtk.statusbar_entries), -1, statusbar_height);
292  gtk_widget_set_size_request(GTK_WIDGET(session->gtk.notification_text), -1, statusbar_height);
293 
294  /* set horizontal padding. same for statusbar entries in statusbar.c */
295  gtk_misc_set_padding(GTK_MISC(session->gtk.notification_text), leftpadding, 0);
296 #endif
297 
298  session->signals.inputbar_key_pressed = g_signal_connect(
299  G_OBJECT(session->gtk.inputbar_entry),
300  "key-press-event",
302  session
303  );
304 
305  session->signals.inputbar_changed = g_signal_connect(
306  G_OBJECT(session->gtk.inputbar_entry),
307  "changed",
309  session
310  );
311 
312  session->signals.inputbar_activate = g_signal_connect(
313  G_OBJECT(session->gtk.inputbar_entry),
314  "activate",
316  session
317  );
318 
319  gtk_box_set_homogeneous(session->gtk.inputbar_box, FALSE);
320  gtk_box_set_spacing(session->gtk.inputbar_box, 5);
321 
322  /* inputbar box */
323  gtk_box_pack_start(GTK_BOX(session->gtk.inputbar_box), GTK_WIDGET(session->gtk.inputbar_dialog), FALSE, FALSE, 0);
324  gtk_box_pack_start(GTK_BOX(session->gtk.inputbar_box), GTK_WIDGET(session->gtk.inputbar_entry), TRUE, TRUE, 0);
325  gtk_container_add(GTK_CONTAINER(session->gtk.inputbar), GTK_WIDGET(session->gtk.inputbar_box));
326 
327  /* bottom box */
328  gtk_box_set_spacing(session->private_data->gtk.bottom_box, 0);
329 
330  gtk_box_pack_end(GTK_BOX(session->private_data->gtk.bottom_box), GTK_WIDGET(session->gtk.inputbar), TRUE, TRUE, 0);
331  gtk_box_pack_end(GTK_BOX(session->private_data->gtk.bottom_box), GTK_WIDGET(session->gtk.notification_area), TRUE, TRUE, 0);
332  gtk_box_pack_end(GTK_BOX(session->private_data->gtk.bottom_box), GTK_WIDGET(session->gtk.statusbar), TRUE, TRUE, 0);
333 
334  /* tabs */
335  gtk_notebook_set_show_border(session->gtk.tabs, FALSE);
336  gtk_notebook_set_show_tabs(session->gtk.tabs, FALSE);
337 
338 #if (GTK_MAJOR_VERSION == 3)
339  /* packing */
340  gtk_box_set_spacing(session->gtk.box, 0);
341  gtk_box_pack_start(session->gtk.box, GTK_WIDGET(session->gtk.tabbar), FALSE, FALSE, 0);
342  gtk_box_pack_start(session->gtk.box, GTK_WIDGET(session->gtk.view), TRUE, TRUE, 0);
343 
344  /* box */
345  gtk_container_add(GTK_CONTAINER(session->private_data->gtk.overlay), GTK_WIDGET(session->gtk.box));
346  /* overlay */
347  g_object_set(session->private_data->gtk.bottom_box, "halign", GTK_ALIGN_FILL, NULL);
348  g_object_set(session->private_data->gtk.bottom_box, "valign", GTK_ALIGN_END, NULL);
349 
350  gtk_overlay_add_overlay(GTK_OVERLAY(session->private_data->gtk.overlay), GTK_WIDGET(session->private_data->gtk.bottom_box));
351  gtk_container_add(GTK_CONTAINER(session->gtk.window), GTK_WIDGET(session->private_data->gtk.overlay));
352 
353 #else
354  /* packing */
355  gtk_box_set_spacing(session->gtk.box, 0);
356  gtk_box_pack_start(session->gtk.box, GTK_WIDGET(session->gtk.tabbar), FALSE, FALSE, 0);
357  gtk_box_pack_start(session->gtk.box, GTK_WIDGET(session->gtk.view), TRUE, TRUE, 0);
358  gtk_box_pack_end(session->gtk.box, GTK_WIDGET(session->private_data->gtk.bottom_box), FALSE, FALSE, 0);
359 
360  /* box */
361  gtk_container_add(GTK_CONTAINER(session->gtk.window), GTK_WIDGET(session->gtk.box));
362 #endif
363 
364  /* parse color values */
365  typedef struct color_setting_mapping_s
366  {
367  char* identifier;
368  GdkRGBA *color;
369  } color_setting_mapping_t;
370 
371  const color_setting_mapping_t color_setting_mappings[] = {
372  {"default-fg", &(session->style.default_foreground)},
373  {"default-bg", &(session->style.default_background)},
374  {"inputbar-fg", &(session->style.inputbar_foreground)},
375  {"inputbar-bg", &(session->style.inputbar_background)},
376  {"statusbar-fg", &(session->style.statusbar_foreground)},
377  {"statusbar-bg", &(session->style.statusbar_background)},
378  {"completion-fg", &(session->style.completion_foreground)},
379  {"completion-bg", &(session->style.completion_background)},
380  {"completion-group-fg", &(session->style.completion_group_foreground)},
381  {"completion-group-bg", &(session->style.completion_group_background)},
382  {"completion-highlight-fg", &(session->style.completion_highlight_foreground)},
383  {"completion-highlight-bg", &(session->style.completion_highlight_background)},
384  {"notification-error-fg", &(session->style.notification_error_foreground)},
385  {"notification-error-bg", &(session->style.notification_error_background)},
386  {"notification-warning-fg", &(session->style.notification_warning_foreground)},
387  {"notification-warning-bg", &(session->style.notification_warning_background)},
388  {"notification-fg", &(session->style.notification_default_foreground)},
389  {"notification-bg", &(session->style.notification_default_background)},
390  {"tabbar-fg", &(session->style.tabbar_foreground)},
391  {"tabbar-bg", &(session->style.tabbar_background)},
392  {"tabbar-focus-fg", &(session->style.tabbar_focus_foreground)},
393  {"tabbar-focus-bg", &(session->style.tabbar_focus_background)},
394  };
395 
396  for (size_t i = 0; i < LENGTH(color_setting_mappings); i++) {
397  char* tmp_value = NULL;
398  girara_setting_get(session, color_setting_mappings[i].identifier, &tmp_value);
399  if (tmp_value != NULL) {
400  gdk_rgba_parse(color_setting_mappings[i].color, tmp_value);
401  g_free(tmp_value);
402  }
403  }
404 
405  /* view */
406  gtk_widget_override_background_color(GTK_WIDGET(session->gtk.viewport),
407  GTK_STATE_NORMAL, &(session->style.default_background));
408 
409  /* statusbar */
410  gtk_widget_override_background_color(GTK_WIDGET(session->gtk.statusbar),
411  GTK_STATE_NORMAL, &(session->style.statusbar_background));
412 
413  /* inputbar */
414 #if (GTK_MAJOR_VERSION == 3)
415  gtk_widget_override_background_color(GTK_WIDGET(session->gtk.inputbar_entry),
416  GTK_STATE_NORMAL, &(session->style.inputbar_background));
417  gtk_widget_override_color(GTK_WIDGET(session->gtk.inputbar_entry),
418  GTK_STATE_NORMAL, &(session->style.inputbar_foreground));
419 
420  gtk_widget_override_background_color(GTK_WIDGET(session->gtk.inputbar),
421  GTK_STATE_NORMAL, &(session->style.inputbar_background));
422  gtk_widget_override_color(GTK_WIDGET(session->gtk.inputbar_dialog),
423  GTK_STATE_NORMAL, &(session->style.inputbar_foreground));
424 
425  /* notification area */
426  gtk_widget_override_background_color(GTK_WIDGET(session->gtk.notification_area),
427  GTK_STATE_NORMAL, &(session->style.notification_default_background));
428  gtk_widget_override_color(GTK_WIDGET(session->gtk.notification_text),
429  GTK_STATE_NORMAL, &(session->style.notification_default_foreground));
430 #else
431  /* inputbar */
432  gtk_widget_modify_base(GTK_WIDGET(session->gtk.inputbar_entry), GTK_STATE_NORMAL, &(session->style.inputbar_background));
433  gtk_widget_modify_text(GTK_WIDGET(session->gtk.inputbar_entry), GTK_STATE_NORMAL, &(session->style.inputbar_foreground));
434 
435  gtk_widget_modify_bg(GTK_WIDGET(session->gtk.inputbar), GTK_STATE_NORMAL, &(session->style.inputbar_background));
436  gtk_widget_modify_fg(GTK_WIDGET(session->gtk.inputbar_dialog), GTK_STATE_NORMAL, &(session->style.inputbar_foreground));
437 
438  /* notification area */
439  gtk_widget_modify_bg(GTK_WIDGET(session->gtk.notification_area),
440  GTK_STATE_NORMAL, &(session->style.notification_default_background));
441  gtk_widget_modify_text(GTK_WIDGET(session->gtk.notification_text),
442  GTK_STATE_NORMAL, &(session->style.notification_default_foreground));
443 #endif
444 
445  if (session->style.font == NULL) {
446  /* set default font */
447  girara_setting_set(session, "font", "monospace normal 9");
448  } else {
449  gtk_widget_override_font(GTK_WIDGET(session->gtk.inputbar_entry), session->style.font);
450  gtk_widget_override_font(GTK_WIDGET(session->gtk.inputbar_dialog), session->style.font);
451  gtk_widget_override_font(GTK_WIDGET(session->gtk.notification_text), session->style.font);
452  }
453 
454  /* set window size */
455  int window_width = 0;
456  int window_height = 0;
457  girara_setting_get(session, "window-width", &window_width);
458  girara_setting_get(session, "window-height", &window_height);
459 
460  if (window_width > 0 && window_height > 0) {
461  gtk_window_set_default_size(GTK_WINDOW(session->gtk.window), window_width, window_height);
462  }
463 
464  gtk_widget_show_all(GTK_WIDGET(session->gtk.window));
465  gtk_widget_hide(GTK_WIDGET(session->gtk.notification_area));
466  gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar_dialog));
467 
468  if (session->global.autohide_inputbar == true) {
469  gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar));
470  }
471 
472  if (session->global.hide_statusbar == true) {
473  gtk_widget_hide(GTK_WIDGET(session->gtk.statusbar));
474  }
475 
476  char* window_icon = NULL;
477  girara_setting_get(session, "window-icon", &window_icon);
478  if (window_icon != NULL) {
479  if (strlen(window_icon) != 0) {
480  girara_setting_set(session, "window-icon", window_icon);
481  }
482  g_free(window_icon);
483  }
484 
485  gtk_widget_grab_focus(GTK_WIDGET(session->gtk.view));
486 
487  return true;
488 }
489 
490 static void
491 girara_session_private_free(girara_session_private_t* session)
492 {
493  g_return_if_fail(session != NULL);
494 
495  /* clean up settings */
496  girara_list_free(session->settings);
497  session->settings = NULL;
498 
499  g_slice_free(girara_session_private_t, session);
500 }
501 
502 bool
503 girara_session_destroy(girara_session_t* session)
504 {
505  g_return_val_if_fail(session != NULL, FALSE);
506 
507  /* clean up style */
508  if (session->style.font) {
509  pango_font_description_free(session->style.font);
510  }
511 
512  /* clean up shortcuts */
513  girara_list_free(session->bindings.shortcuts);
514  session->bindings.shortcuts = NULL;
515 
516  /* clean up inputbar shortcuts */
517  girara_list_free(session->bindings.inputbar_shortcuts);
518  session->bindings.inputbar_shortcuts = NULL;
519 
520  /* clean up commands */
521  girara_list_free(session->bindings.commands);
522  session->bindings.commands = NULL;
523 
524  /* clean up special commands */
525  girara_list_free(session->bindings.special_commands);
526  session->bindings.special_commands = NULL;
527 
528  /* clean up mouse events */
529  girara_list_free(session->bindings.mouse_events);
530  session->bindings.mouse_events = NULL;
531 
532  /* clean up input histry */
533  g_object_unref(session->command_history);
534  session->command_history = NULL;
535 
536  /* clean up statusbar items */
537  girara_list_free(session->elements.statusbar_items);
538  session->elements.statusbar_items = NULL;
539 
540  /* clean up config handles */
541  girara_list_free(session->config.handles);
542  session->config.handles = NULL;
543 
544  /* clean up shortcut mappings */
545  girara_list_free(session->config.shortcut_mappings);
546  session->config.shortcut_mappings = NULL;
547 
548  /* clean up argument mappings */
549  girara_list_free(session->config.argument_mappings);
550  session->config.argument_mappings = NULL;
551 
552  /* clean up modes */
553  girara_list_free(session->modes.identifiers);
554  session->modes.identifiers = NULL;
555 
556  /* clean up buffer */
557  if (session->buffer.command) {
558  g_string_free(session->buffer.command, TRUE);
559  }
560 
561  if (session->global.buffer) {
562  g_string_free(session->global.buffer, TRUE);
563  }
564 
565  session->buffer.command = NULL;
566  session->global.buffer = NULL;
567 
568  /* clean up private data */
569  girara_session_private_free(session->private_data);
570  session->private_data = NULL;
572  session->settings = NULL;
573  UNIGNORE
574 
575  /* clean up session */
576  g_slice_free(girara_session_t, session);
577 
578  return TRUE;
579 }
580 
581 char*
582 girara_buffer_get(girara_session_t* session)
583 {
584  g_return_val_if_fail(session != NULL, NULL);
585 
586  return (session->global.buffer) ? g_strdup(session->global.buffer->str) : NULL;
587 }
588 
589 void
590 girara_notify(girara_session_t* session, int level, const char* format, ...)
591 {
592  if (session == NULL
593  || session->gtk.notification_text == NULL
594  || session->gtk.notification_area == NULL
595  || session->gtk.inputbar == NULL
596  || session->gtk.view == NULL) {
597  return;
598  }
599 
600  switch (level) {
601  case GIRARA_ERROR:
602  gtk_widget_override_background_color(GTK_WIDGET(session->gtk.notification_area),
603  GTK_STATE_NORMAL, &(session->style.notification_error_background));
604  gtk_widget_override_color(GTK_WIDGET(session->gtk.notification_text),
605  GTK_STATE_NORMAL, &(session->style.notification_error_foreground));
606  break;
607  case GIRARA_WARNING:
608  gtk_widget_override_background_color(GTK_WIDGET(session->gtk.notification_area),
609  GTK_STATE_NORMAL, &(session->style.notification_warning_background));
610  gtk_widget_override_color(GTK_WIDGET(session->gtk.notification_text),
611  GTK_STATE_NORMAL, &(session->style.notification_warning_foreground));
612  break;
613  case GIRARA_INFO:
614  gtk_widget_override_background_color(GTK_WIDGET(session->gtk.notification_area),
615  GTK_STATE_NORMAL, &(session->style.notification_default_background));
616  gtk_widget_override_color(GTK_WIDGET(session->gtk.notification_text),
617  GTK_STATE_NORMAL, &(session->style.notification_default_foreground));
618  break;
619  default:
620  return;
621  }
622 
623  /* prepare message */
624  va_list ap;
625  va_start(ap, format);
626  char* message = g_strdup_vprintf(format, ap);
627  va_end(ap);
628 
629  gtk_label_set_markup(GTK_LABEL(session->gtk.notification_text), message);
630  g_free(message);
631 
632  /* update visibility */
633  gtk_widget_show(GTK_WIDGET(session->gtk.notification_area));
634  gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar));
635 
636  gtk_widget_grab_focus(GTK_WIDGET(session->gtk.view));
637 }
638 
639 void girara_dialog(girara_session_t* session, const char* dialog, bool
640  invisible, girara_callback_inputbar_key_press_event_t key_press_event,
641  girara_callback_inputbar_activate_t activate_event, void* data)
642 {
643  if (session == NULL || session->gtk.inputbar == NULL
644  || session->gtk.inputbar_dialog == NULL
645  || session->gtk.inputbar_entry == NULL) {
646  return;
647  }
648 
649  gtk_widget_show(GTK_WIDGET(session->gtk.inputbar_dialog));
650 
651  /* set dialog message */
652  if (dialog != NULL) {
653  gtk_label_set_markup(session->gtk.inputbar_dialog, dialog);
654  }
655 
656  /* set input visibility */
657  if (invisible == true) {
658  gtk_entry_set_visibility(session->gtk.inputbar_entry, FALSE);
659  } else {
660  gtk_entry_set_visibility(session->gtk.inputbar_entry, TRUE);
661  }
662 
663  /* set handler */
664  session->signals.inputbar_custom_activate = activate_event;
665  session->signals.inputbar_custom_key_press_event = key_press_event;
666  session->signals.inputbar_custom_data = data;
667 
668  /* focus inputbar */
669  girara_sc_focus_inputbar(session, NULL, NULL, 0);
670 }
671 
672 bool
673 girara_set_view(girara_session_t* session, GtkWidget* widget)
674 {
675  g_return_val_if_fail(session != NULL, false);
676 
677  GtkWidget* child = gtk_bin_get_child(GTK_BIN(session->gtk.viewport));
678 
679  if (child) {
680  g_object_ref(child);
681  gtk_container_remove(GTK_CONTAINER(session->gtk.viewport), child);
682  }
683 
684  gtk_container_add(GTK_CONTAINER(session->gtk.viewport), widget);
685  gtk_widget_show_all(widget);
686 
687  return true;
688 }
689 
690 void
691 girara_mode_set(girara_session_t* session, girara_mode_t mode)
692 {
693  g_return_if_fail(session != NULL);
694 
695  session->modes.current_mode = mode;
696 }
697 
699 girara_mode_add(girara_session_t* session, const char* name)
700 {
701  g_return_val_if_fail(session != NULL, FALSE);
702  g_return_val_if_fail(name != NULL && name[0] != 0x0, FALSE);
703 
704  girara_mode_t last_index = 0;
705  GIRARA_LIST_FOREACH(session->modes.identifiers, girara_mode_string_t*, iter, mode)
706  if (mode->index > last_index) {
707  last_index = mode->index;
708  }
709  GIRARA_LIST_FOREACH_END(session->modes.identifiers, girara_mode_string_t*, iter, mode);
710 
711  /* create new mode identifier */
712  girara_mode_string_t* mode = g_slice_new(girara_mode_string_t);
713  mode->index = last_index + 1;
714  mode->name = g_strdup(name);
715  girara_list_append(session->modes.identifiers, mode);
716 
717  return mode->index;
718 }
719 
720 void
721 girara_mode_string_free(girara_mode_string_t* mode)
722 {
723  if (mode == NULL) {
724  return;
725  }
726 
727  g_free(mode->name);
728  g_slice_free(girara_mode_string_t, mode);
729 }
730 
732 girara_mode_get(girara_session_t* session)
733 {
734  g_return_val_if_fail(session != NULL, 0);
735 
736  return session->modes.current_mode;
737 }
738 
739 bool
740 girara_set_window_title(girara_session_t* session, const char* name)
741 {
742  if (session == NULL || session->gtk.window == NULL || name == NULL) {
743  return false;
744  }
745 
746  gtk_window_set_title(GTK_WINDOW(session->gtk.window), name);
747 
748  return true;
749 }
750 
751 girara_list_t*
752 girara_get_command_history(girara_session_t* session)
753 {
754  g_return_val_if_fail(session != NULL, NULL);
755  return girara_input_history_list(session->command_history);
756 }