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 #ifdef WITH_LIBNOTIFY
7 #include <libnotify/notify.h>
8 #endif
9 
10 #include "session.h"
11 
12 #include "callbacks.h"
13 #include "commands.h"
14 #include "config.h"
15 #include "css-definitions.h"
16 #include "datastructures.h"
17 #include "entry.h"
18 #include "input-history.h"
19 #include "internal.h"
20 #include "settings.h"
21 #include "shortcuts.h"
22 #include "template.h"
23 #include "utils.h"
24 
25 static int
26 cb_sort_settings(girara_setting_t* lhs, girara_setting_t* rhs)
27 {
28  return g_strcmp0(girara_setting_get_name(lhs), girara_setting_get_name(rhs));
29 }
30 
31 static void
32 ensure_gettext_initialized(void)
33 {
34  static gsize initialized = 0;
35  if (g_once_init_enter(&initialized) == TRUE) {
36  bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
37  bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
38  g_once_init_leave(&initialized, 1);
39  }
40 }
41 
42 static void
43 init_template_engine(GiraraTemplate* csstemplate)
44 {
45  static const char* variable_names[] = {
46  "session",
47  "font",
48  "default-fg",
49  "default-bg",
50  "inputbar-fg",
51  "inputbar-bg",
52  "statusbar-fg",
53  "statusbar-bg",
54  "completion-fg",
55  "completion-bg",
56  "completion-group-fg",
57  "completion-group-bg",
58  "completion-highlight-fg",
59  "completion-highlight-bg",
60  "notification-error-fg",
61  "notification-error-bg",
62  "notification-warning-fg",
63  "notification-warning-bg",
64  "notification-fg",
65  "notification-bg",
66  "tabbar-fg",
67  "tabbar-bg",
68  "tabbar-focus-fg",
69  "tabbar-focus-bg",
70  "bottombox-padding1",
71  "bottombox-padding2",
72  "bottombox-padding3",
73  "bottombox-padding4"
74  };
75 
76  for (size_t idx = 0; idx < LENGTH(variable_names); ++idx) {
77  girara_template_add_variable(csstemplate, variable_names[idx]);
78  }
79 }
80 
81 static void
82 fill_template_with_values(girara_session_t* session)
83 {
84  GiraraTemplate* csstemplate = session->private_data->csstemplate;
85 
86  girara_template_set_variable_value(csstemplate, "session",
87  session->private_data->session_name);
88 
89  char* font = NULL;
90  girara_setting_get(session, "font", &font);
91  if (font != NULL) {
93  pango_font_description_free(session->style.font);
94  session->style.font = pango_font_description_from_string(font);
96  g_free(font);
97  }
99  if (session->style.font == NULL) {
100  girara_template_set_variable_value(csstemplate, "font", "monospace normal 9");
101  } else {
102  char* fontname = pango_font_description_to_string(session->style.font);
103  girara_template_set_variable_value(csstemplate, "font", fontname);
104  g_free(fontname);
105  }
107 
108  /* parse color values */
109  typedef struct color_setting_mapping_s {
110  const char* identifier;
111  GdkRGBA *color;
112  } color_setting_mapping_t;
113 
115  const color_setting_mapping_t color_setting_mappings[] = {
116  {"default-fg", &(session->style.default_foreground)},
117  {"default-bg", &(session->style.default_background)},
118  {"inputbar-fg", &(session->style.inputbar_foreground)},
119  {"inputbar-bg", &(session->style.inputbar_background)},
120  {"statusbar-fg", &(session->style.statusbar_foreground)},
121  {"statusbar-bg", &(session->style.statusbar_background)},
122  {"completion-fg", &(session->style.completion_foreground)},
123  {"completion-bg", &(session->style.completion_background)},
124  {"completion-group-fg", &(session->style.completion_group_foreground)},
125  {"completion-group-bg", &(session->style.completion_group_background)},
126  {"completion-highlight-fg", &(session->style.completion_highlight_foreground)},
127  {"completion-highlight-bg", &(session->style.completion_highlight_background)},
128  {"notification-error-fg", &(session->style.notification_error_foreground)},
129  {"notification-error-bg", &(session->style.notification_error_background)},
130  {"notification-warning-fg", &(session->style.notification_warning_foreground)},
131  {"notification-warning-bg", &(session->style.notification_warning_background)},
132  {"notification-fg", &(session->style.notification_default_foreground)},
133  {"notification-bg", &(session->style.notification_default_background)},
134  {"tabbar-fg", &(session->style.tabbar_foreground)},
135  {"tabbar-bg", &(session->style.tabbar_background)},
136  {"tabbar-focus-fg", &(session->style.tabbar_focus_foreground)},
137  {"tabbar-focus-bg", &(session->style.tabbar_focus_background)},
138  };
140 
141  for (size_t i = 0; i < LENGTH(color_setting_mappings); i++) {
142  char* tmp_value = NULL;
143  girara_setting_get(session, color_setting_mappings[i].identifier, &tmp_value);
144  if (tmp_value != NULL) {
145  gdk_rgba_parse(color_setting_mappings[i].color, tmp_value);
146  g_free(tmp_value);
147  }
148 
149  char* color = gdk_rgba_to_string(color_setting_mappings[i].color);
151  color_setting_mappings[i].identifier, color);
152  g_free(color);
153  }
154 
155  /* we want inputbar_entry the same height as notification_text and statusbar,
156  so that when inputbar_entry is hidden, the size of the bottom_box remains
157  the same. We need to get rid of the builtin padding in the GtkEntry
158  widget. */
159 
160  int ypadding = 2; /* total amount of padding (top + bottom) */
161  int xpadding = 8; /* total amount of padding (left + right) */
162  girara_setting_get(session, "statusbar-h-padding", &xpadding);
163  girara_setting_get(session, "statusbar-v-padding", &ypadding);
164 
165  typedef struct padding_mapping_s {
166  const char* identifier;
167  char* value;
168  } padding_mapping_t;
169 
170  const padding_mapping_t padding_mapping[] = {
171  {"bottombox-padding1", g_strdup_printf("%d", ypadding - ypadding/2)},
172  {"bottombox-padding2", g_strdup_printf("%d", xpadding/2)},
173  {"bottombox-padding3", g_strdup_printf("%d", ypadding/2)},
174  {"bottombox-padding4", g_strdup_printf("%d", xpadding - xpadding/2)},
175  };
176 
177  for (size_t i = 0; i < LENGTH(padding_mapping); ++i) {
179  padding_mapping[i].identifier, padding_mapping[i].value);
180  g_free(padding_mapping[i].value);
181  }
182 }
183 
184 static void
185 css_template_changed(GiraraTemplate* csstemplate, girara_session_t* session)
186 {
187  GtkCssProvider* old = session->private_data->gtk.cssprovider;
188  char* css_data = girara_template_evaluate(csstemplate);
189  if (css_data == NULL) {
190  girara_error("Error while evaluating templates.");
191  return;
192  }
193 
194  GtkCssProvider* provider = gtk_css_provider_new();
195  GError* error = NULL;
196  if (gtk_css_provider_load_from_data(provider, css_data, -1, &error) == FALSE) {
197  girara_error("Unable to load CSS: %s", error->message);
198  g_free(css_data);
199  g_error_free(error);
200  g_object_unref(provider);
201  return;
202  }
203  g_free(css_data);
204 
205  /* add CSS style provider */
206  GdkDisplay* display = gdk_display_get_default();
207  GdkScreen* screen = gdk_display_get_default_screen(display);
208  gtk_style_context_add_provider_for_screen(screen,
209  GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
210  if (old != NULL) {
211  gtk_style_context_remove_provider_for_screen(screen, GTK_STYLE_PROVIDER(old));
212  g_object_unref(old);
213 
214  gtk_widget_queue_draw(GTK_WIDGET(session->gtk.window));
215  }
216  session->private_data->gtk.cssprovider = provider;
217 }
218 
219 girara_session_t*
221 {
222  ensure_gettext_initialized();
223 
224  girara_session_t* session = g_slice_alloc0(sizeof(girara_session_t));
225  session->private_data = g_slice_alloc0(sizeof(girara_session_private_t));
226 
227  /* init values */
228  session->bindings.mouse_events = girara_list_new2(
230  session->bindings.commands = girara_list_new2(
232  session->bindings.special_commands = girara_list_new2(
234  session->bindings.shortcuts = girara_list_new2(
236  session->bindings.inputbar_shortcuts = girara_list_new2(
238 
239  session->elements.statusbar_items = girara_list_new2(
241 
242  /* settings */
243  session->private_data->settings = girara_sorted_list_new2(
244  (girara_compare_function_t) cb_sort_settings,
246 
247  /* CSS style provider */
248  session->private_data->csstemplate = girara_template_new(CSS_TEMPLATE);
249  session->private_data->gtk.cssprovider = NULL;
250  init_template_engine(session->private_data->csstemplate);
251 
252  /* init modes */
253  session->modes.identifiers = girara_list_new2(
255  girara_mode_t normal_mode = girara_mode_add(session, "normal");
256  girara_mode_t inputbar_mode = girara_mode_add(session, "inputbar");
257  session->modes.normal = normal_mode;
258  session->modes.current_mode = normal_mode;
259  session->modes.inputbar = inputbar_mode;
260 
261  /* config handles */
262  session->config.handles = girara_list_new2(
264  session->config.shortcut_mappings = girara_list_new2(
266  session->config.argument_mappings = girara_list_new2(
268 
269  /* command history */
270  session->command_history = girara_input_history_new(NULL);
271 
272  /* load default values */
274 
275  /* create widgets */
276  session->gtk.box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));
277  session->private_data->gtk.overlay = gtk_overlay_new();
278  session->private_data->gtk.bottom_box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));
279  session->gtk.statusbar_entries = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0));
280  session->gtk.tabbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
281  session->gtk.inputbar_box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0));
282  gtk_box_set_homogeneous(GTK_BOX(session->gtk.tabbar), TRUE);
283  gtk_box_set_homogeneous(session->gtk.inputbar_box, TRUE);
284  session->gtk.view = gtk_scrolled_window_new(NULL, NULL);
285  session->gtk.viewport = gtk_viewport_new(NULL, NULL);
286 #if GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION >= 4
287  gtk_widget_add_events(session->gtk.viewport, GDK_SCROLL_MASK);
288 #endif
289  session->gtk.statusbar = gtk_event_box_new();
290  session->gtk.notification_area = gtk_event_box_new();
291  session->gtk.notification_text = gtk_label_new(NULL);
292  session->gtk.inputbar_dialog = GTK_LABEL(gtk_label_new(NULL));
293  session->gtk.inputbar_entry = GTK_ENTRY(girara_entry_new());
294  session->gtk.inputbar = gtk_event_box_new();
295  session->gtk.tabs = GTK_NOTEBOOK(gtk_notebook_new());
296 
297  /* deprecated members */
299  session->settings = session->private_data->settings;
300  session->global.command_history = girara_get_command_history(session);
302 
303  return session;
304 }
305 
306 bool
307 girara_session_init(girara_session_t* session, const char* sessionname)
308 {
309  if (session == NULL) {
310  return false;
311  }
312 
313 #if GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION >= 4
314  bool smooth_scroll = false;
315  girara_setting_get(session, "smooth-scroll", &smooth_scroll);
316  if (smooth_scroll) {
317  gtk_widget_add_events(session->gtk.viewport, GDK_SMOOTH_SCROLL_MASK);
318  }
319 #endif
320 
321  session->private_data->session_name = g_strdup(
322  (sessionname == NULL) ? "girara" : sessionname);
323 
324  /* load CSS style */
325  fill_template_with_values(session);
326  g_signal_connect(G_OBJECT(session->private_data->csstemplate), "changed",
327  G_CALLBACK(css_template_changed), session);
328 
329  /* window */
330  if (session->gtk.embed != 0) {
331  session->gtk.window = gtk_plug_new(session->gtk.embed);
332  } else {
333  session->gtk.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
334  }
335 
336  gtk_widget_set_name(GTK_WIDGET(session->gtk.window),
337  session->private_data->session_name);
338 
339  /* apply CSS style */
340  css_template_changed(session->private_data->csstemplate, session);
341 
342  GdkGeometry hints = {
343  .base_height = 1,
344  .base_width = 1,
345  .height_inc = 0,
346  .max_aspect = 0,
347  .max_height = 0,
348  .max_width = 0,
349  .min_aspect = 0,
350  .min_height = 0,
351  .min_width = 0,
352  .width_inc = 0
353  };
354 
355  gtk_window_set_geometry_hints(GTK_WINDOW(session->gtk.window), NULL, &hints, GDK_HINT_MIN_SIZE);
356 
357  /* view */
358  session->signals.view_key_pressed = g_signal_connect(G_OBJECT(session->gtk.view), "key-press-event",
359  G_CALLBACK(girara_callback_view_key_press_event), session);
360 
361  session->signals.view_button_press_event = g_signal_connect(G_OBJECT(session->gtk.view), "button-press-event",
362  G_CALLBACK(girara_callback_view_button_press_event), session);
363 
364  session->signals.view_button_release_event = g_signal_connect(G_OBJECT(session->gtk.view), "button-release-event",
365  G_CALLBACK(girara_callback_view_button_release_event), session);
366 
367  session->signals.view_motion_notify_event = g_signal_connect(G_OBJECT(session->gtk.view), "motion-notify-event",
369 
370  session->signals.view_scroll_event = g_signal_connect(G_OBJECT(session->gtk.view), "scroll-event",
371  G_CALLBACK(girara_callback_view_scroll_event), session);
372 
373  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(session->gtk.view), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
374 
375  /* invisible scrollbars */
376  GtkWidget *vscrollbar = gtk_scrolled_window_get_vscrollbar(GTK_SCROLLED_WINDOW(session->gtk.view));
377  GtkWidget *hscrollbar = gtk_scrolled_window_get_hscrollbar(GTK_SCROLLED_WINDOW(session->gtk.view));
378 
379  char* guioptions = NULL;
380  girara_setting_get(session, "guioptions", &guioptions);
381 
382  if (vscrollbar != NULL && strchr(guioptions, 'v') == NULL) {
383  gtk_widget_set_state_flags(vscrollbar, GTK_STATE_FLAG_INSENSITIVE, false);
384  }
385  if (hscrollbar != NULL) {
386  if (strchr(guioptions, 'h') == NULL) {
387  gtk_widget_set_state_flags(hscrollbar, GTK_STATE_FLAG_INSENSITIVE, false);
388  }
389  }
390  g_free(guioptions);
391 
392  /* viewport */
393  gtk_container_add(GTK_CONTAINER(session->gtk.view), session->gtk.viewport);
394  gtk_viewport_set_shadow_type(GTK_VIEWPORT(session->gtk.viewport), GTK_SHADOW_NONE);
395 
396  /* statusbar */
397  gtk_container_add(GTK_CONTAINER(session->gtk.statusbar), GTK_WIDGET(session->gtk.statusbar_entries));
398 
399  /* notification area */
400  gtk_container_add(GTK_CONTAINER(session->gtk.notification_area), session->gtk.notification_text);
401  gtk_widget_set_halign(session->gtk.notification_text, GTK_ALIGN_START);
402  gtk_widget_set_valign(session->gtk.notification_text, GTK_ALIGN_CENTER);
403  gtk_label_set_use_markup(GTK_LABEL(session->gtk.notification_text), TRUE);
404 
405  /* inputbar */
406  gtk_entry_set_has_frame(session->gtk.inputbar_entry, FALSE);
407  gtk_editable_set_editable(GTK_EDITABLE(session->gtk.inputbar_entry), TRUE);
408 
409  widget_add_class(GTK_WIDGET(session->gtk.inputbar_entry), "bottom_box");
410  widget_add_class(session->gtk.notification_text, "bottom_box");
411 
412  session->signals.inputbar_key_pressed = g_signal_connect(
413  G_OBJECT(session->gtk.inputbar_entry),
414  "key-press-event",
416  session
417  );
418 
419  session->signals.inputbar_changed = g_signal_connect(
420  G_OBJECT(session->gtk.inputbar_entry),
421  "changed",
423  session
424  );
425 
426  session->signals.inputbar_activate = g_signal_connect(
427  G_OBJECT(session->gtk.inputbar_entry),
428  "activate",
430  session
431  );
432 
433  gtk_box_set_homogeneous(session->gtk.inputbar_box, FALSE);
434  gtk_box_set_spacing(session->gtk.inputbar_box, 5);
435 
436  /* inputbar box */
437  gtk_box_pack_start(GTK_BOX(session->gtk.inputbar_box), GTK_WIDGET(session->gtk.inputbar_dialog), FALSE, FALSE, 0);
438  gtk_box_pack_start(GTK_BOX(session->gtk.inputbar_box), GTK_WIDGET(session->gtk.inputbar_entry), TRUE, TRUE, 0);
439  gtk_container_add(GTK_CONTAINER(session->gtk.inputbar), GTK_WIDGET(session->gtk.inputbar_box));
440 
441  /* bottom box */
442  gtk_box_set_spacing(session->private_data->gtk.bottom_box, 0);
443 
444  gtk_box_pack_end(GTK_BOX(session->private_data->gtk.bottom_box), GTK_WIDGET(session->gtk.inputbar), TRUE, TRUE, 0);
445  gtk_box_pack_end(GTK_BOX(session->private_data->gtk.bottom_box), GTK_WIDGET(session->gtk.notification_area), TRUE, TRUE, 0);
446  gtk_box_pack_end(GTK_BOX(session->private_data->gtk.bottom_box), GTK_WIDGET(session->gtk.statusbar), TRUE, TRUE, 0);
447 
448  /* tabs */
449  gtk_notebook_set_show_border(session->gtk.tabs, FALSE);
450  gtk_notebook_set_show_tabs(session->gtk.tabs, FALSE);
451 
452  /* packing */
453  gtk_box_set_spacing(session->gtk.box, 0);
454  gtk_box_pack_start(session->gtk.box, GTK_WIDGET(session->gtk.tabbar), FALSE, FALSE, 0);
455  gtk_box_pack_start(session->gtk.box, GTK_WIDGET(session->gtk.view), TRUE, TRUE, 0);
456 
457  /* box */
458  gtk_container_add(GTK_CONTAINER(session->private_data->gtk.overlay), GTK_WIDGET(session->gtk.box));
459  /* overlay */
460  g_object_set(session->private_data->gtk.bottom_box, "halign", GTK_ALIGN_FILL, NULL);
461  g_object_set(session->private_data->gtk.bottom_box, "valign", GTK_ALIGN_END, NULL);
462 
463  gtk_overlay_add_overlay(GTK_OVERLAY(session->private_data->gtk.overlay), GTK_WIDGET(session->private_data->gtk.bottom_box));
464  gtk_container_add(GTK_CONTAINER(session->gtk.window), GTK_WIDGET(session->private_data->gtk.overlay));
465 
466  /* statusbar */
467  widget_add_class(GTK_WIDGET(session->gtk.statusbar), "statusbar");
468 
469  /* inputbar */
470  widget_add_class(GTK_WIDGET(session->gtk.inputbar_box), "inputbar");
471  widget_add_class(GTK_WIDGET(session->gtk.inputbar_entry), "inputbar");
472  widget_add_class(GTK_WIDGET(session->gtk.inputbar), "inputbar");
473  widget_add_class(GTK_WIDGET(session->gtk.inputbar_dialog), "inputbar");
474 
475  /* notification area */
476  widget_add_class(session->gtk.notification_area, "notification");
477  widget_add_class(session->gtk.notification_text, "notification");
478  gtk_style_context_save(gtk_widget_get_style_context(session->gtk.notification_area));
479  gtk_style_context_save(gtk_widget_get_style_context(session->gtk.notification_text));
480 
481  /* set window size */
482  int window_width = 0;
483  int window_height = 0;
484  girara_setting_get(session, "window-width", &window_width);
485  girara_setting_get(session, "window-height", &window_height);
486 
487  if (window_width > 0 && window_height > 0) {
488  gtk_window_set_default_size(GTK_WINDOW(session->gtk.window), window_width, window_height);
489  }
490 
491  gtk_widget_show_all(GTK_WIDGET(session->gtk.window));
492  gtk_widget_hide(GTK_WIDGET(session->gtk.notification_area));
493  gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar_dialog));
494 
495  if (session->global.autohide_inputbar == true) {
496  gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar));
497  }
498 
499  if (session->global.hide_statusbar == true) {
500  gtk_widget_hide(GTK_WIDGET(session->gtk.statusbar));
501  }
502 
503  char* window_icon = NULL;
504  girara_setting_get(session, "window-icon", &window_icon);
505  if (window_icon != NULL) {
506  if (strlen(window_icon) != 0) {
507  girara_setting_set(session, "window-icon", window_icon);
508  }
509  g_free(window_icon);
510  }
511 
512  gtk_widget_grab_focus(GTK_WIDGET(session->gtk.view));
513 
514  return true;
515 }
516 
517 static void
518 girara_session_private_free(girara_session_private_t* session)
519 {
520  g_return_if_fail(session != NULL);
521 
522  if (session->session_name != NULL) {
523  g_free(session->session_name);
524  }
525 
526  /* clean up CSS style provider */
527  if (session->gtk.cssprovider != NULL) {
528  g_object_unref(session->gtk.cssprovider);
529  }
530  session->gtk.cssprovider = NULL;
531  if (session->csstemplate != NULL) {
532  g_object_unref(session->csstemplate);
533  }
534  session->csstemplate = NULL;
535 
536  /* clean up settings */
537  girara_list_free(session->settings);
538  session->settings = NULL;
539 
540  g_slice_free(girara_session_private_t, session);
541 }
542 
543 bool
544 girara_session_destroy(girara_session_t* session)
545 {
546  g_return_val_if_fail(session != NULL, FALSE);
547 
548  /* clean up style */
550  if (session->style.font != NULL) {
551  pango_font_description_free(session->style.font);
552  }
554 
555  /* clean up shortcuts */
556  girara_list_free(session->bindings.shortcuts);
557  session->bindings.shortcuts = NULL;
558 
559  /* clean up inputbar shortcuts */
560  girara_list_free(session->bindings.inputbar_shortcuts);
561  session->bindings.inputbar_shortcuts = NULL;
562 
563  /* clean up commands */
564  girara_list_free(session->bindings.commands);
565  session->bindings.commands = NULL;
566 
567  /* clean up special commands */
568  girara_list_free(session->bindings.special_commands);
569  session->bindings.special_commands = NULL;
570 
571  /* clean up mouse events */
572  girara_list_free(session->bindings.mouse_events);
573  session->bindings.mouse_events = NULL;
574 
575  /* clean up input histry */
576  g_object_unref(session->command_history);
577  session->command_history = NULL;
578 
579  /* clean up statusbar items */
580  girara_list_free(session->elements.statusbar_items);
581  session->elements.statusbar_items = NULL;
582 
583  /* clean up config handles */
584  girara_list_free(session->config.handles);
585  session->config.handles = NULL;
586 
587  /* clean up shortcut mappings */
588  girara_list_free(session->config.shortcut_mappings);
589  session->config.shortcut_mappings = NULL;
590 
591  /* clean up argument mappings */
592  girara_list_free(session->config.argument_mappings);
593  session->config.argument_mappings = NULL;
594 
595  /* clean up modes */
596  girara_list_free(session->modes.identifiers);
597  session->modes.identifiers = NULL;
598 
599  /* clean up buffer */
600  if (session->buffer.command) {
601  g_string_free(session->buffer.command, TRUE);
602  }
603 
604  if (session->global.buffer) {
605  g_string_free(session->global.buffer, TRUE);
606  }
607 
608  session->buffer.command = NULL;
609  session->global.buffer = NULL;
610 
611  /* clean up private data */
612  girara_session_private_free(session->private_data);
613  session->private_data = NULL;
615  session->settings = NULL;
617 
618  /* clean up session */
619  g_slice_free(girara_session_t, session);
620 
621  return TRUE;
622 }
623 
624 char*
625 girara_buffer_get(girara_session_t* session)
626 {
627  g_return_val_if_fail(session != NULL, NULL);
628 
629  return (session->global.buffer) ? g_strdup(session->global.buffer->str) : NULL;
630 }
631 
632 void
633 girara_libnotify(girara_session_t* session, const char *summary,
634  const char *body)
635 {
636  if (session == NULL
637  || summary == NULL
638  || body == NULL) {
639  return;
640  }
641 
642 #ifdef WITH_LIBNOTIFY
643 
644  bool was_initialized = notify_is_initted();
645 
646  if (!was_initialized) {
647  notify_init(session->private_data->session_name);
648  }
649 
650  NotifyNotification* libnotify_notification = NULL;
651  char* icon_name = NULL;
652 
653  /* We use the NotifyNotification constructor at many branches because
654  * libnotify does not have a notify_notification_set_image_from_name()
655  * function, and accessing private fields is frowned upon and subject to API
656  * changes.
657  */
658  icon_name = g_strdup(gtk_window_get_icon_name(GTK_WINDOW(session->gtk.window)));
659  if (icon_name != NULL) {
660  /* Icon can be loaded from theme with adequate quality for notification */
661  libnotify_notification = notify_notification_new(summary, body, icon_name);
662  g_free(icon_name);
663  } else {
664  /* Or extracted from the current window */
665  GdkPixbuf* icon_pix = gtk_window_get_icon(GTK_WINDOW(session->gtk.window));
666  if (icon_pix != NULL) {
667  libnotify_notification = notify_notification_new(summary, body, NULL);
668  notify_notification_set_image_from_pixbuf(libnotify_notification, icon_pix);
669  g_object_unref(G_OBJECT(icon_pix));
670  } else {
671  /* Or from a default image as a last resort */
672  libnotify_notification = notify_notification_new(summary, body, "info");
673  }
674  }
675 
676  g_return_if_fail(libnotify_notification != NULL);
677  notify_notification_show(libnotify_notification, NULL);
678  g_object_unref(G_OBJECT(libnotify_notification));
679 
680  if (!was_initialized) {
681  notify_uninit();
682  }
683 
684 #else
685 
686  girara_notify(session, GIRARA_WARNING, "Girara was compiled without libnotify support.");
687 
688 #endif
689 }
690 
691 void
692 girara_notify(girara_session_t* session, int level, const char* format, ...)
693 {
694  if (session == NULL
695  || session->gtk.notification_text == NULL
696  || session->gtk.notification_area == NULL
697  || session->gtk.inputbar == NULL
698  || session->gtk.view == NULL) {
699  return;
700  }
701 
702  GtkStyleContext* area_context = gtk_widget_get_style_context(session->gtk.notification_area);
703  GtkStyleContext* text_context = gtk_widget_get_style_context(session->gtk.notification_text);
704 
705  gtk_style_context_restore(area_context);
706  gtk_style_context_restore(text_context);
707  gtk_style_context_save(area_context);
708  gtk_style_context_save(text_context);
709 
710  const char* cssclass = NULL;
711  switch (level) {
712  case GIRARA_ERROR:
713  cssclass = "notification-error";
714  break;
715  case GIRARA_WARNING:
716  cssclass = "notification-warning";
717  break;
718  case GIRARA_INFO:
719  break;
720  default:
721  return;
722  }
723 
724  if (cssclass != NULL) {
725  widget_add_class(session->gtk.notification_area, cssclass);
726  widget_add_class(session->gtk.notification_text, cssclass);
727  }
728 
729  /* prepare message */
730  va_list ap;
731  va_start(ap, format);
732  char* message = g_strdup_vprintf(format, ap);
733  va_end(ap);
734 
735  gtk_label_set_markup(GTK_LABEL(session->gtk.notification_text), message);
736  g_free(message);
737 
738  /* update visibility */
739  gtk_widget_show(GTK_WIDGET(session->gtk.notification_area));
740  gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar));
741 
742  gtk_widget_grab_focus(GTK_WIDGET(session->gtk.view));
743 }
744 
745 void
746 girara_dialog(girara_session_t* session, const char* dialog, bool
747  invisible, girara_callback_inputbar_key_press_event_t key_press_event,
748  girara_callback_inputbar_activate_t activate_event, void* data)
749 {
750  if (session == NULL || session->gtk.inputbar == NULL
751  || session->gtk.inputbar_dialog == NULL
752  || session->gtk.inputbar_entry == NULL) {
753  return;
754  }
755 
756  gtk_widget_show(GTK_WIDGET(session->gtk.inputbar_dialog));
757 
758  /* set dialog message */
759  if (dialog != NULL) {
760  gtk_label_set_markup(session->gtk.inputbar_dialog, dialog);
761  }
762 
763  /* set input visibility */
764  if (invisible == true) {
765  gtk_entry_set_visibility(session->gtk.inputbar_entry, FALSE);
766  } else {
767  gtk_entry_set_visibility(session->gtk.inputbar_entry, TRUE);
768  }
769 
770  /* set handler */
771  session->signals.inputbar_custom_activate = activate_event;
772  session->signals.inputbar_custom_key_press_event = key_press_event;
773  session->signals.inputbar_custom_data = data;
774 
775  /* focus inputbar */
776  girara_sc_focus_inputbar(session, NULL, NULL, 0);
777 }
778 
779 bool
780 girara_set_view(girara_session_t* session, GtkWidget* widget)
781 {
782  g_return_val_if_fail(session != NULL, false);
783 
784  GtkWidget* child = gtk_bin_get_child(GTK_BIN(session->gtk.viewport));
785 
786  if (child != NULL) {
787  g_object_ref(child);
788  gtk_container_remove(GTK_CONTAINER(session->gtk.viewport), child);
789  }
790 
791  gtk_container_add(GTK_CONTAINER(session->gtk.viewport), widget);
792  gtk_widget_show_all(widget);
793  gtk_widget_grab_focus(session->gtk.view);
794 
795  return true;
796 }
797 
798 void
799 girara_mode_set(girara_session_t* session, girara_mode_t mode)
800 {
801  g_return_if_fail(session != NULL);
802 
803  session->modes.current_mode = mode;
804 }
805 
807 girara_mode_add(girara_session_t* session, const char* name)
808 {
809  g_return_val_if_fail(session != NULL, FALSE);
810  g_return_val_if_fail(name != NULL && name[0] != '\0', FALSE);
811 
812  girara_mode_t last_index = 0;
813  GIRARA_LIST_FOREACH(session->modes.identifiers, girara_mode_string_t*, iter, mode)
814  if (mode->index > last_index) {
815  last_index = mode->index;
816  }
817  GIRARA_LIST_FOREACH_END(session->modes.identifiers, girara_mode_string_t*, iter, mode);
818 
819  /* create new mode identifier */
820  girara_mode_string_t* mode = g_slice_new(girara_mode_string_t);
821  mode->index = last_index + 1;
822  mode->name = g_strdup(name);
823  girara_list_append(session->modes.identifiers, mode);
824 
825  return mode->index;
826 }
827 
828 void
829 girara_mode_string_free(girara_mode_string_t* mode)
830 {
831  if (mode == NULL) {
832  return;
833  }
834 
835  g_free(mode->name);
836  g_slice_free(girara_mode_string_t, mode);
837 }
838 
840 girara_mode_get(girara_session_t* session)
841 {
842  g_return_val_if_fail(session != NULL, 0);
843 
844  return session->modes.current_mode;
845 }
846 
847 bool
848 girara_set_window_title(girara_session_t* session, const char* name)
849 {
850  if (session == NULL || session->gtk.window == NULL || name == NULL) {
851  return false;
852  }
853 
854  gtk_window_set_title(GTK_WINDOW(session->gtk.window), name);
855 
856  return true;
857 }
858 
859 bool
860 girara_set_window_icon(girara_session_t* session, const char* name)
861 {
862  if (session == NULL || session->gtk.window == NULL || name == NULL) {
863  return false;
864  }
865 
866  gtk_window_set_icon_name(GTK_WINDOW(session->gtk.window), name);
867 
868  return true;
869 }
870 
871 girara_list_t*
872 girara_get_command_history(girara_session_t* session)
873 {
874  g_return_val_if_fail(session != NULL, NULL);
875  return girara_input_history_list(session->command_history);
876 }
877 
878 GiraraTemplate*
879 girara_session_get_template(girara_session_t* session)
880 {
881  g_return_val_if_fail(session != NULL, NULL);
882 
883  return session->private_data->csstemplate;
884 }
885 
void girara_config_handle_free(girara_config_handle_t *handle)
Definition: config.c:329
int girara_mode_t
Definition: types.h:69
void girara_special_command_free(girara_special_command_t *special_command)
Definition: commands.c:595
#define LENGTH(x)
Definition: internal.h:18
HIDDEN void girara_setting_free(girara_setting_t *setting)
Definition: settings.c:153
bool girara_session_init(girara_session_t *session, const char *sessionname)
Definition: session.c:307
bool girara_set_window_title(girara_session_t *session, const char *name)
Definition: session.c:848
void girara_list_append(girara_list_t *list, void *data)
HIDDEN void girara_argument_mapping_free(girara_argument_mapping_t *argument_mapping)
Definition: shortcuts.c:676
void(* girara_free_function_t)(void *data)
Definition: types.h:118
bool girara_session_destroy(girara_session_t *session)
Definition: session.c:544
bool girara_callback_view_button_release_event(GtkWidget *UNUSED(widget), GdkEventButton *button, girara_session_t *session)
Definition: callbacks.c:252
girara_list_t * girara_list_new2(girara_free_function_t gfree)
bool girara_callback_inputbar_changed_event(GtkEditable *entry, girara_session_t *session)
Definition: callbacks.c:560
bool girara_sc_focus_inputbar(girara_session_t *session, girara_argument_t *argument, girara_event_t *UNUSED(event), unsigned int UNUSED(t))
Definition: shortcuts.c:284
HIDDEN void girara_statusbar_item_free(girara_statusbar_item_t *statusbaritem)
Definition: statusbar.c:50
bool girara_callback_view_key_press_event(GtkWidget *UNUSED(widget), GdkEventKey *event, girara_session_t *session)
Definition: callbacks.c:76
void girara_list_free(girara_list_t *list)
void girara_dialog(girara_session_t *session, const char *dialog, bool invisible, girara_callback_inputbar_key_press_event_t key_press_event, girara_callback_inputbar_activate_t activate_event, void *data)
Definition: session.c:746
GiraraTemplate * girara_session_get_template(girara_session_t *session)
Definition: session.c:879
bool girara_callback_inputbar_key_press_event(GtkWidget *entry, GdkEventKey *event, girara_session_t *session)
Definition: callbacks.c:509
girara_mode_t girara_mode_add(girara_session_t *session, const char *name)
Definition: session.c:807
HIDDEN void girara_inputbar_shortcut_free(girara_inputbar_shortcut_t *shortcut)
Definition: shortcuts.c:146
bool girara_set_window_icon(girara_session_t *session, const char *name)
Definition: session.c:860
int(* girara_compare_function_t)(const void *data1, const void *data2)
Definition: types.h:134
void girara_config_load_default(girara_session_t *session)
Definition: config.c:192
GiraraTemplate * girara_template_new(const char *base)
Definition: template.c:263
bool(* girara_callback_inputbar_key_press_event_t)(GtkWidget *widget, GdkEventKey *event, void *data)
Definition: callbacks.h:17
bool girara_set_view(girara_session_t *session, GtkWidget *widget)
Definition: session.c:780
bool(* girara_callback_inputbar_activate_t)(GtkEntry *entry, void *data)
Definition: callbacks.h:27
void girara_template_set_variable_value(GiraraTemplate *object, const char *name, const char *value)
Definition: template.c:398
void girara_libnotify(girara_session_t *session, const char *summary, const char *body)
Definition: session.c:633
HIDDEN void girara_mouse_event_free(girara_mouse_event_t *mouse_event)
Definition: shortcuts.c:742
HIDDEN void girara_shortcut_mapping_free(girara_shortcut_mapping_t *mapping)
Definition: shortcuts.c:638
char * girara_template_evaluate(GiraraTemplate *object)
Definition: template.c:441
HIDDEN void widget_add_class(GtkWidget *widget, const char *styleclass)
Definition: utils.c:532
bool girara_setting_get(girara_session_t *session, const char *name, void *dest)
Definition: settings.c:140
girara_list_t * girara_sorted_list_new2(girara_compare_function_t cmp, girara_free_function_t gfree)
girara_list_t * girara_input_history_list(GiraraInputHistory *history)
#define girara_error(...)
Definition: utils.h:134
bool girara_callback_view_button_motion_notify_event(GtkWidget *UNUSED(widget), GdkEventMotion *button, girara_session_t *session)
Definition: callbacks.c:283
void girara_mode_string_free(girara_mode_string_t *mode)
Definition: session.c:829
bool girara_template_add_variable(GiraraTemplate *object, const char *name)
Definition: template.c:365
void girara_notify(girara_session_t *session, int level, const char *format,...)
Definition: session.c:692
void girara_command_free(girara_command_t *command)
Definition: commands.c:604
bool girara_callback_inputbar_activate(GtkEntry *entry, girara_session_t *session)
Definition: callbacks.c:366
PangoFontDescription * font
Definition: session.h:302
bool girara_setting_set(girara_session_t *session, const char *name, void *value)
Definition: settings.c:95
GiraraInputHistory * girara_input_history_new(GiraraInputHistoryIO *io)
const char * girara_setting_get_name(girara_setting_t *setting)
Definition: settings.c:185
void girara_mode_set(girara_session_t *session, girara_mode_t mode)
Definition: session.c:799
#define GIRARA_UNIGNORE
Definition: macros.h:68
bool girara_callback_view_scroll_event(GtkWidget *UNUSED(widget), GdkEventScroll *scroll, girara_session_t *session)
Definition: callbacks.c:314
#define GIRARA_IGNORE_DEPRECATED
Definition: macros.h:62
girara_session_t * girara_session_create()
Definition: session.c:220
bool girara_callback_view_button_press_event(GtkWidget *UNUSED(widget), GdkEventButton *button, girara_session_t *session)
Definition: callbacks.c:205
GiraraEntry * girara_entry_new(void)
Definition: entry.c:41
#define GIRARA_LIST_FOREACH_END(list, type, iter, data)
#define GIRARA_LIST_FOREACH(list, type, iter, data)
girara_list_t * girara_get_command_history(girara_session_t *session)
Definition: session.c:872
char * girara_buffer_get(girara_session_t *session)
Definition: session.c:625
girara_mode_t girara_mode_get(girara_session_t *session)
Definition: session.c:840
HIDDEN void girara_shortcut_free(girara_shortcut_t *shortcut)
Definition: shortcuts.c:89