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