session.c 26.7 KB
Newer Older
Sebastian Ramacher's avatar
Sebastian Ramacher committed
1 2 3
/* See LICENSE file for license and copyright information */

#include <stdlib.h>
4
#include <glib/gi18n-lib.h>
Sebastian Ramacher's avatar
Sebastian Ramacher committed
5

6 7 8 9
#ifdef WITH_LIBNOTIFY
#include <libnotify/notify.h>
#endif

Sebastian Ramacher's avatar
Sebastian Ramacher committed
10
#include "session.h"
11 12 13 14 15

#include "callbacks.h"
#include "commands.h"
#include "config.h"
#include "css-definitions.h"
Sebastian Ramacher's avatar
Sebastian Ramacher committed
16
#include "datastructures.h"
17
#include "entry.h"
18
#include "input-history.h"
Sebastian Ramacher's avatar
Sebastian Ramacher committed
19
#include "internal.h"
20
#include "settings.h"
Sebastian Ramacher's avatar
Sebastian Ramacher committed
21
#include "shortcuts.h"
22
#include "template.h"
23 24
#include "utils.h"

25 26 27 28 29
static int
cb_sort_settings(girara_setting_t* lhs, girara_setting_t* rhs)
{
  return g_strcmp0(girara_setting_get_name(lhs), girara_setting_get_name(rhs));
}
Moritz Lipp's avatar
Moritz Lipp committed
30

31 32 33 34 35 36 37 38 39 40 41
static void
ensure_gettext_initialized(void)
{
  static gsize initialized = 0;
  if (g_once_init_enter(&initialized) == TRUE) {
    bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
    bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
    g_once_init_leave(&initialized, 1);
  }
}

42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
static void
init_template_engine(GiraraTemplate* csstemplate)
{
  static const char* variable_names[] = {
    "session",
    "font",
    "default-fg",
    "default-bg",
    "inputbar-fg",
    "inputbar-bg",
    "statusbar-fg",
    "statusbar-bg",
    "completion-fg",
    "completion-bg",
    "completion-group-fg",
    "completion-group-bg",
    "completion-highlight-fg",
    "completion-highlight-bg",
    "notification-error-fg",
    "notification-error-bg",
    "notification-warning-fg",
    "notification-warning-bg",
    "notification-fg",
    "notification-bg",
William G Hatch's avatar
William G Hatch committed
66 67
    "scrollbar-fg",
    "scrollbar-bg",
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
    "tabbar-fg",
    "tabbar-bg",
    "tabbar-focus-fg",
    "tabbar-focus-bg",
    "bottombox-padding1",
    "bottombox-padding2",
    "bottombox-padding3",
    "bottombox-padding4"
  };

  for (size_t idx = 0; idx < LENGTH(variable_names); ++idx) {
    girara_template_add_variable(csstemplate, variable_names[idx]);
  }
}

Sebastian Ramacher's avatar
Sebastian Ramacher committed
83
static void
84
fill_template_with_values(girara_session_t* session)
85
{
86
  GiraraTemplate* csstemplate = session->private_data->csstemplate;
87

88 89
  girara_template_set_variable_value(csstemplate, "session",
      session->private_data->session_name);
90

91 92 93
  char* font = NULL;
  girara_setting_get(session, "font", &font);
  if (font != NULL) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
94
    girara_template_set_variable_value(csstemplate, "font", font);
95
    g_free(font);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
96
  } else {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
97 98
    girara_template_set_variable_value(csstemplate, "font", "monospace normal 9");
  };
99 100

  /* parse color values */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
  const char* color_settings[] = {
    "default-fg",
    "default-bg",
    "inputbar-fg",
    "inputbar-bg",
    "statusbar-fg",
    "statusbar-bg",
    "completion-fg",
    "completion-bg",
    "completion-group-fg",
    "completion-group-bg",
    "completion-highlight-fg",
    "completion-highlight-bg",
    "notification-error-fg",
    "notification-error-bg",
    "notification-warning-fg",
    "notification-warning-bg",
    "notification-fg",
    "notification-bg",
William G Hatch's avatar
William G Hatch committed
120 121
    "scrollbar-fg",
    "scrollbar-bg",
Sebastian Ramacher's avatar
Sebastian Ramacher committed
122 123 124 125
    "tabbar-fg",
    "tabbar-bg",
    "tabbar-focus-fg",
    "tabbar-focus-bg",
126 127
  };

Sebastian Ramacher's avatar
Sebastian Ramacher committed
128
  for (size_t i = 0; i < LENGTH(color_settings); i++) {
129
    char* tmp_value = NULL;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
130
    girara_setting_get(session, color_settings[i], &tmp_value);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
131 132

    GdkRGBA color = { 0, 0, 0, 0 };
133
    if (tmp_value != NULL) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
134
      gdk_rgba_parse(&color, tmp_value);
135 136 137
      g_free(tmp_value);
    }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
138
    char* colorstr = gdk_rgba_to_string(&color);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
139 140
    girara_template_set_variable_value(csstemplate, color_settings[i],
        colorstr);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
141
    g_free(colorstr);
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
  }

  /* we want inputbar_entry the same height as notification_text and statusbar,
    so that when inputbar_entry is hidden, the size of the bottom_box remains
    the same. We need to get rid of the builtin padding in the GtkEntry
    widget. */

  int ypadding = 2;         /* total amount of padding (top + bottom) */
  int xpadding = 8;         /* total amount of padding (left + right) */
  girara_setting_get(session, "statusbar-h-padding", &xpadding);
  girara_setting_get(session, "statusbar-v-padding", &ypadding);

  typedef struct padding_mapping_s {
    const char* identifier;
    char* value;
  } padding_mapping_t;

  const padding_mapping_t padding_mapping[] = {
160 161 162 163
    {"bottombox-padding1", g_strdup_printf("%d", ypadding - ypadding/2)},
    {"bottombox-padding2", g_strdup_printf("%d", xpadding/2)},
    {"bottombox-padding3", g_strdup_printf("%d", ypadding/2)},
    {"bottombox-padding4", g_strdup_printf("%d", xpadding - xpadding/2)},
164 165 166
  };

  for (size_t i = 0; i < LENGTH(padding_mapping); ++i) {
167 168
    girara_template_set_variable_value(csstemplate,
        padding_mapping[i].identifier, padding_mapping[i].value);
169 170
    g_free(padding_mapping[i].value);
  }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
}

static void
css_template_changed(GiraraTemplate* csstemplate, girara_session_t* session)
{
  GtkCssProvider* old = session->private_data->gtk.cssprovider;
  char* css_data      = girara_template_evaluate(csstemplate);
  if (css_data == NULL) {
    girara_error("Error while evaluating templates.");
    return;
  }

  GtkCssProvider* provider = gtk_css_provider_new();
  GError* error            = NULL;
  if (gtk_css_provider_load_from_data(provider, css_data, -1, &error) == FALSE) {
    girara_error("Unable to load CSS: %s", error->message);
    g_free(css_data);
    g_error_free(error);
    g_object_unref(provider);
    return;
  }
  g_free(css_data);

  /* add CSS style provider */
  GdkDisplay* display = gdk_display_get_default();
  GdkScreen* screen = gdk_display_get_default_screen(display);
  gtk_style_context_add_provider_for_screen(screen,
      GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
  if (old != NULL) {
    gtk_style_context_remove_provider_for_screen(screen, GTK_STYLE_PROVIDER(old));
    g_object_unref(old);
202

Sebastian Ramacher's avatar
Sebastian Ramacher committed
203 204 205
    gtk_widget_queue_draw(GTK_WIDGET(session->gtk.window));
  }
  session->private_data->gtk.cssprovider = provider;
206 207
}

Sebastian Ramacher's avatar
Sebastian Ramacher committed
208 209 210
girara_session_t*
girara_session_create()
{
211
  ensure_gettext_initialized();
212

213
  girara_session_t* session = g_slice_alloc0(sizeof(girara_session_t));
214
  session->private_data     = g_slice_alloc0(sizeof(girara_session_private_t));
Sebastian Ramacher's avatar
Sebastian Ramacher committed
215 216

  /* init values */
217 218 219 220 221 222
  session->bindings.mouse_events       = girara_list_new2(
      (girara_free_function_t) girara_mouse_event_free);
  session->bindings.commands           = girara_list_new2(
      (girara_free_function_t) girara_command_free);
  session->bindings.special_commands   = girara_list_new2(
      (girara_free_function_t) girara_special_command_free);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
223 224 225 226
  session->bindings.shortcuts          = girara_list_new2(
      (girara_free_function_t) girara_shortcut_free);
  session->bindings.inputbar_shortcuts = girara_list_new2(
      (girara_free_function_t) girara_inputbar_shortcut_free);
Moritz Lipp's avatar
Moritz Lipp committed
227 228

  session->elements.statusbar_items = girara_list_new2(
Sebastian Ramacher's avatar
Sebastian Ramacher committed
229
      (girara_free_function_t) girara_statusbar_item_free);
Moritz Lipp's avatar
Moritz Lipp committed
230

231 232
  /* settings */
  session->private_data->settings = girara_sorted_list_new2(
233
      (girara_compare_function_t) cb_sort_settings,
Sebastian Ramacher's avatar
Sebastian Ramacher committed
234 235
      (girara_free_function_t) girara_setting_free);

236
  /* CSS style provider */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
237 238
  session->private_data->csstemplate     = girara_template_new(CSS_TEMPLATE);
  session->private_data->gtk.cssprovider = NULL;
239
  init_template_engine(session->private_data->csstemplate);
240

Moritz Lipp's avatar
Moritz Lipp committed
241
  /* init modes */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
242
  session->modes.identifiers  = girara_list_new2(
Sebastian Ramacher's avatar
Sebastian Ramacher committed
243
      (girara_free_function_t) girara_mode_string_free);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
244
  girara_mode_t normal_mode   = girara_mode_add(session, "normal");
245
  girara_mode_t inputbar_mode = girara_mode_add(session, "inputbar");
Sebastian Ramacher's avatar
Sebastian Ramacher committed
246 247
  session->modes.normal       = normal_mode;
  session->modes.current_mode = normal_mode;
248
  session->modes.inputbar     = inputbar_mode;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
249

Moritz Lipp's avatar
Moritz Lipp committed
250
  /* config handles */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
251 252 253 254
  session->config.handles           = girara_list_new2(
      (girara_free_function_t) girara_config_handle_free);
  session->config.shortcut_mappings = girara_list_new2(
      (girara_free_function_t) girara_shortcut_mapping_free);
255 256
  session->config.argument_mappings = girara_list_new2(
      (girara_free_function_t) girara_argument_mapping_free);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
257

Moritz Lipp's avatar
Moritz Lipp committed
258
  /* command history */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
259
  session->command_history = girara_input_history_new(NULL);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
260

261 262
  /* load default values */
  girara_config_load_default(session);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
263

264
  /* create widgets */
265 266 267 268 269 270
  session->gtk.box                      = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));
  session->private_data->gtk.overlay    = gtk_overlay_new();
  session->private_data->gtk.bottom_box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));
  session->gtk.statusbar_entries        = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0));
  session->gtk.tabbar                   = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
  session->gtk.inputbar_box             = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0));
271 272
  gtk_box_set_homogeneous(GTK_BOX(session->gtk.tabbar), TRUE);
  gtk_box_set_homogeneous(session->gtk.inputbar_box, TRUE);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
273 274
  session->gtk.view              = gtk_scrolled_window_new(NULL, NULL);
  session->gtk.viewport          = gtk_viewport_new(NULL, NULL);
275
  gtk_widget_add_events(session->gtk.viewport, GDK_SCROLL_MASK);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
276 277 278
  session->gtk.statusbar         = gtk_event_box_new();
  session->gtk.notification_area = gtk_event_box_new();
  session->gtk.notification_text = gtk_label_new(NULL);
Moritz Lipp's avatar
Moritz Lipp committed
279
  session->gtk.inputbar_dialog   = GTK_LABEL(gtk_label_new(NULL));
280
  session->gtk.inputbar_entry    = GTK_ENTRY(girara_entry_new());
Moritz Lipp's avatar
Moritz Lipp committed
281
  session->gtk.inputbar          = gtk_event_box_new();
Sebastian Ramacher's avatar
Sebastian Ramacher committed
282 283
  session->gtk.tabs              = GTK_NOTEBOOK(gtk_notebook_new());

284 285 286 287 288 289 290 291 292 293
  return session;
}

bool
girara_session_init(girara_session_t* session, const char* sessionname)
{
  if (session == NULL) {
    return false;
  }

294 295 296 297 298 299
  bool smooth_scroll = false;
  girara_setting_get(session, "smooth-scroll", &smooth_scroll);
  if (smooth_scroll) {
    gtk_widget_add_events(session->gtk.viewport, GDK_SMOOTH_SCROLL_MASK);
  }

300 301 302
  session->private_data->session_name = g_strdup(
      (sessionname == NULL) ? "girara" : sessionname);

303
  /* load CSS style */
304
  fill_template_with_values(session);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
305 306
  g_signal_connect(G_OBJECT(session->private_data->csstemplate), "changed",
      G_CALLBACK(css_template_changed), session);
307

Sebastian Ramacher's avatar
Sebastian Ramacher committed
308
  /* window */
309
#ifdef GDK_WINDOWING_X11
310
  if (session->gtk.embed != 0) {
311 312
    session->gtk.window = gtk_plug_new(session->gtk.embed);
  } else {
Moritz Lipp's avatar
Moritz Lipp committed
313
#endif
314
    session->gtk.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
315
#ifdef GDK_WINDOWING_X11
316
  }
Moritz Lipp's avatar
Moritz Lipp committed
317
#endif
318

319 320
  gtk_widget_set_name(GTK_WIDGET(session->gtk.window),
      session->private_data->session_name);
321

Sebastian Ramacher's avatar
Sebastian Ramacher committed
322 323
  /* apply CSS style */
  css_template_changed(session->private_data->csstemplate, session);
324

Sebastian Ramacher's avatar
Sebastian Ramacher committed
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
  GdkGeometry hints = {
    .base_height = 1,
    .base_width  = 1,
    .height_inc  = 0,
    .max_aspect  = 0,
    .max_height  = 0,
    .max_width   = 0,
    .min_aspect  = 0,
    .min_height  = 0,
    .min_width   = 0,
    .width_inc   = 0
  };

  gtk_window_set_geometry_hints(GTK_WINDOW(session->gtk.window), NULL, &hints, GDK_HINT_MIN_SIZE);

  /* view */
  session->signals.view_key_pressed = g_signal_connect(G_OBJECT(session->gtk.view), "key-press-event",
      G_CALLBACK(girara_callback_view_key_press_event), session);

Moritz Lipp's avatar
Moritz Lipp committed
344 345 346 347 348 349 350 351 352
  session->signals.view_button_press_event = g_signal_connect(G_OBJECT(session->gtk.view), "button-press-event",
      G_CALLBACK(girara_callback_view_button_press_event), session);

  session->signals.view_button_release_event = g_signal_connect(G_OBJECT(session->gtk.view), "button-release-event",
      G_CALLBACK(girara_callback_view_button_release_event), session);

  session->signals.view_motion_notify_event = g_signal_connect(G_OBJECT(session->gtk.view), "motion-notify-event",
      G_CALLBACK(girara_callback_view_button_motion_notify_event), session);

353
  session->signals.view_scroll_event = g_signal_connect(G_OBJECT(session->gtk.view), "scroll-event",
354 355
      G_CALLBACK(girara_callback_view_scroll_event), session);

356 357
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(session->gtk.view), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

358
  /* invisible scrollbars */
359 360 361
  GtkWidget *vscrollbar = gtk_scrolled_window_get_vscrollbar(GTK_SCROLLED_WINDOW(session->gtk.view));
  GtkWidget *hscrollbar = gtk_scrolled_window_get_hscrollbar(GTK_SCROLLED_WINDOW(session->gtk.view));

362 363 364
  char* guioptions = NULL;
  girara_setting_get(session, "guioptions", &guioptions);

365 366
  if (vscrollbar != NULL && strchr(guioptions, 'v') == NULL) {
    gtk_widget_set_state_flags(vscrollbar, GTK_STATE_FLAG_INSENSITIVE, false);
367
  }
368
  if (hscrollbar != NULL) {
369 370 371
    if (strchr(guioptions, 'h') == NULL) {
     gtk_widget_set_state_flags(hscrollbar, GTK_STATE_FLAG_INSENSITIVE, false);
    }
372
  }
373
  g_free(guioptions);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
374 375 376 377 378 379 380 381 382

  /* viewport */
  gtk_container_add(GTK_CONTAINER(session->gtk.view), session->gtk.viewport);
  gtk_viewport_set_shadow_type(GTK_VIEWPORT(session->gtk.viewport), GTK_SHADOW_NONE);

  /* statusbar */
  gtk_container_add(GTK_CONTAINER(session->gtk.statusbar), GTK_WIDGET(session->gtk.statusbar_entries));

  /* notification area */
383
  gtk_container_add(GTK_CONTAINER(session->gtk.notification_area), session->gtk.notification_text);
384 385
  gtk_widget_set_halign(session->gtk.notification_text, GTK_ALIGN_START);
  gtk_widget_set_valign(session->gtk.notification_text, GTK_ALIGN_CENTER);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
386 387 388
  gtk_label_set_use_markup(GTK_LABEL(session->gtk.notification_text), TRUE);

  /* inputbar */
Moritz Lipp's avatar
Moritz Lipp committed
389 390 391
  gtk_entry_set_has_frame(session->gtk.inputbar_entry, FALSE);
  gtk_editable_set_editable(GTK_EDITABLE(session->gtk.inputbar_entry), TRUE);

Sebastian Ramacher's avatar
Sebastian Ramacher committed
392 393
  widget_add_class(GTK_WIDGET(session->gtk.inputbar_entry), "bottom_box");
  widget_add_class(session->gtk.notification_text, "bottom_box");
394

Moritz Lipp's avatar
Moritz Lipp committed
395 396 397 398 399 400 401
  session->signals.inputbar_key_pressed = g_signal_connect(
      G_OBJECT(session->gtk.inputbar_entry),
      "key-press-event",
      G_CALLBACK(girara_callback_inputbar_key_press_event),
      session
    );

402 403 404 405 406 407 408
  session->signals.inputbar_changed = g_signal_connect(
      G_OBJECT(session->gtk.inputbar_entry),
      "changed",
      G_CALLBACK(girara_callback_inputbar_changed_event),
      session
    );

Moritz Lipp's avatar
Moritz Lipp committed
409 410 411 412 413 414 415
  session->signals.inputbar_activate = g_signal_connect(
      G_OBJECT(session->gtk.inputbar_entry),
      "activate",
      G_CALLBACK(girara_callback_inputbar_activate),
      session
    );

Moritz Lipp's avatar
Moritz Lipp committed
416 417
  gtk_box_set_homogeneous(session->gtk.inputbar_box, FALSE);
  gtk_box_set_spacing(session->gtk.inputbar_box, 5);
Moritz Lipp's avatar
Moritz Lipp committed
418

419
  /* inputbar box */
Moritz Lipp's avatar
Moritz Lipp committed
420 421 422
  gtk_box_pack_start(GTK_BOX(session->gtk.inputbar_box),  GTK_WIDGET(session->gtk.inputbar_dialog), FALSE, FALSE, 0);
  gtk_box_pack_start(GTK_BOX(session->gtk.inputbar_box),  GTK_WIDGET(session->gtk.inputbar_entry),  TRUE,  TRUE,  0);
  gtk_container_add(GTK_CONTAINER(session->gtk.inputbar), GTK_WIDGET(session->gtk.inputbar_box));
Sebastian Ramacher's avatar
Sebastian Ramacher committed
423

424
  /* bottom box */
425
  gtk_box_set_spacing(session->private_data->gtk.bottom_box, 0);
426

427 428 429
  gtk_box_pack_end(GTK_BOX(session->private_data->gtk.bottom_box), GTK_WIDGET(session->gtk.inputbar), TRUE, TRUE, 0);
  gtk_box_pack_end(GTK_BOX(session->private_data->gtk.bottom_box), GTK_WIDGET(session->gtk.notification_area), TRUE, TRUE, 0);
  gtk_box_pack_end(GTK_BOX(session->private_data->gtk.bottom_box), GTK_WIDGET(session->gtk.statusbar), TRUE, TRUE, 0);
430

Sebastian Ramacher's avatar
Sebastian Ramacher committed
431 432 433 434 435
  /* tabs */
  gtk_notebook_set_show_border(session->gtk.tabs, FALSE);
  gtk_notebook_set_show_tabs(session->gtk.tabs,   FALSE);

  /* packing */
436
  gtk_box_set_spacing(session->gtk.box, 0);
437 438
  gtk_box_pack_start(session->gtk.box, GTK_WIDGET(session->gtk.tabbar), FALSE, FALSE, 0);
  gtk_box_pack_start(session->gtk.box, GTK_WIDGET(session->gtk.view),   TRUE,  TRUE, 0);
439 440

  /* box */
441
  gtk_container_add(GTK_CONTAINER(session->private_data->gtk.overlay), GTK_WIDGET(session->gtk.box));
442
  /* overlay */
443 444
  g_object_set(session->private_data->gtk.bottom_box, "halign", GTK_ALIGN_FILL, NULL);
  g_object_set(session->private_data->gtk.bottom_box, "valign", GTK_ALIGN_END, NULL);
445

446 447
  gtk_overlay_add_overlay(GTK_OVERLAY(session->private_data->gtk.overlay), GTK_WIDGET(session->private_data->gtk.bottom_box));
  gtk_container_add(GTK_CONTAINER(session->gtk.window), GTK_WIDGET(session->private_data->gtk.overlay));
448

Sebastian Ramacher's avatar
Sebastian Ramacher committed
449
  /* statusbar */
450
  widget_add_class(GTK_WIDGET(session->gtk.statusbar), "statusbar");
Sebastian Ramacher's avatar
Sebastian Ramacher committed
451 452

  /* inputbar */
453
  widget_add_class(GTK_WIDGET(session->gtk.inputbar_box), "inputbar");
454 455 456
  widget_add_class(GTK_WIDGET(session->gtk.inputbar_entry), "inputbar");
  widget_add_class(GTK_WIDGET(session->gtk.inputbar), "inputbar");
  widget_add_class(GTK_WIDGET(session->gtk.inputbar_dialog), "inputbar");
Sebastian Ramacher's avatar
Sebastian Ramacher committed
457 458

  /* notification area */
459 460
  widget_add_class(session->gtk.notification_area, "notification");
  widget_add_class(session->gtk.notification_text, "notification");
461

Sebastian Ramacher's avatar
Sebastian Ramacher committed
462
  /* set window size */
463 464 465 466
  int window_width = 0;
  int window_height = 0;
  girara_setting_get(session, "window-width", &window_width);
  girara_setting_get(session, "window-height", &window_height);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
467

Sebastian Ramacher's avatar
CS  
Sebastian Ramacher committed
468
  if (window_width > 0 && window_height > 0) {
469
    gtk_window_set_default_size(GTK_WINDOW(session->gtk.window), window_width, window_height);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
470 471 472 473
  }

  gtk_widget_show_all(GTK_WIDGET(session->gtk.window));
  gtk_widget_hide(GTK_WIDGET(session->gtk.notification_area));
Moritz Lipp's avatar
Moritz Lipp committed
474
  gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar_dialog));
475

Moritz Lipp's avatar
Moritz Lipp committed
476 477 478
  if (session->global.autohide_inputbar == true) {
    gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar));
  }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
479

480 481 482 483
  if (session->global.hide_statusbar == true) {
    gtk_widget_hide(GTK_WIDGET(session->gtk.statusbar));
  }

484 485
  char* window_icon = NULL;
  girara_setting_get(session, "window-icon", &window_icon);
486 487 488 489
  if (window_icon != NULL) {
    if (strlen(window_icon) != 0) {
      girara_setting_set(session, "window-icon", window_icon);
    }
490 491
    g_free(window_icon);
  }
492

Moritz Lipp's avatar
Moritz Lipp committed
493 494
  gtk_widget_grab_focus(GTK_WIDGET(session->gtk.view));

495
  return true;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
496 497
}

498 499 500 501 502
static void
girara_session_private_free(girara_session_private_t* session)
{
  g_return_if_fail(session != NULL);

503 504 505 506
  if (session->session_name != NULL) {
    g_free(session->session_name);
  }

507
  /* clean up CSS style provider */
508
  if (session->gtk.cssprovider != NULL) {
509 510 511
    g_object_unref(session->gtk.cssprovider);
  }
  session->gtk.cssprovider = NULL;
512 513 514 515
  if (session->csstemplate != NULL) {
    g_object_unref(session->csstemplate);
  }
  session->csstemplate = NULL;
516

517 518 519 520 521 522 523
  /* clean up settings */
  girara_list_free(session->settings);
  session->settings = NULL;

  g_slice_free(girara_session_private_t, session);
}

Sebastian Ramacher's avatar
Sebastian Ramacher committed
524 525 526 527 528 529 530 531 532 533 534 535 536 537
bool
girara_session_destroy(girara_session_t* session)
{
  g_return_val_if_fail(session != NULL, FALSE);

  /* clean up shortcuts */
  girara_list_free(session->bindings.shortcuts);
  session->bindings.shortcuts = NULL;

  /* clean up inputbar shortcuts */
  girara_list_free(session->bindings.inputbar_shortcuts);
  session->bindings.inputbar_shortcuts = NULL;

  /* clean up commands */
538 539
  girara_list_free(session->bindings.commands);
  session->bindings.commands = NULL;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
540 541

  /* clean up special commands */
542 543
  girara_list_free(session->bindings.special_commands);
  session->bindings.special_commands = NULL;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
544 545

  /* clean up mouse events */
546 547
  girara_list_free(session->bindings.mouse_events);
  session->bindings.mouse_events = NULL;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
548

549
  /* clean up input histry */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
550 551
  g_object_unref(session->command_history);
  session->command_history = NULL;
552

Sebastian Ramacher's avatar
Sebastian Ramacher committed
553 554 555 556 557 558 559 560 561 562 563 564 565
  /* clean up statusbar items */
  girara_list_free(session->elements.statusbar_items);
  session->elements.statusbar_items = NULL;

  /* clean up config handles */
  girara_list_free(session->config.handles);
  session->config.handles = NULL;

  /* clean up shortcut mappings */
  girara_list_free(session->config.shortcut_mappings);
  session->config.shortcut_mappings = NULL;

  /* clean up argument mappings */
566 567
  girara_list_free(session->config.argument_mappings);
  session->config.argument_mappings = NULL;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584

  /* clean up modes */
  girara_list_free(session->modes.identifiers);
  session->modes.identifiers = NULL;

  /* clean up buffer */
  if (session->buffer.command) {
    g_string_free(session->buffer.command, TRUE);
  }

  if (session->global.buffer) {
    g_string_free(session->global.buffer,  TRUE);
  }

  session->buffer.command = NULL;
  session->global.buffer  = NULL;

585 586 587 588
  /* clean up private data */
  girara_session_private_free(session->private_data);
  session->private_data = NULL;

Sebastian Ramacher's avatar
Sebastian Ramacher committed
589 590 591 592 593
  /* clean up session */
  g_slice_free(girara_session_t, session);

  return TRUE;
}
594 595 596 597 598 599 600 601 602

char*
girara_buffer_get(girara_session_t* session)
{
  g_return_val_if_fail(session != NULL, NULL);

  return (session->global.buffer) ? g_strdup(session->global.buffer->str) : NULL;
}

603 604 605 606 607 608 609 610 611 612 613 614
void
girara_libnotify(girara_session_t* session, const char *summary,
    const char *body)
{
  if (session == NULL
      || summary == NULL
      || body == NULL) {
    return;
  }

#ifdef WITH_LIBNOTIFY

615 616 617 618 619 620
  bool was_initialized = notify_is_initted();

  if (!was_initialized) {
    notify_init(session->private_data->session_name);
  }

621 622 623 624 625 626 627 628 629 630
  NotifyNotification* libnotify_notification = NULL;
  char* icon_name = NULL;

  /* We use the NotifyNotification constructor at many branches because
   * libnotify does not have a notify_notification_set_image_from_name()
   * function, and accessing private fields is frowned upon and subject to API
   * changes.
   */
  icon_name = g_strdup(gtk_window_get_icon_name(GTK_WINDOW(session->gtk.window)));
  if (icon_name != NULL) {
631
    /* Icon can be loaded from theme with adequate quality for notification */
632 633 634
    libnotify_notification = notify_notification_new(summary, body, icon_name);
    g_free(icon_name);
  } else {
635
    /* Or extracted from the current window */
636 637 638 639 640 641
    GdkPixbuf* icon_pix = gtk_window_get_icon(GTK_WINDOW(session->gtk.window));
    if (icon_pix != NULL) {
      libnotify_notification = notify_notification_new(summary, body, NULL);
      notify_notification_set_image_from_pixbuf(libnotify_notification, icon_pix);
      g_object_unref(G_OBJECT(icon_pix));
    } else {
642
      /* Or from a default image as a last resort */
643 644 645 646
      libnotify_notification = notify_notification_new(summary, body, "info");
    }
  }

647
  g_return_if_fail(libnotify_notification != NULL);
Benoît Taine's avatar
Benoît Taine committed
648
  notify_notification_show(libnotify_notification, NULL);
649 650
  g_object_unref(G_OBJECT(libnotify_notification));

651 652 653 654
  if (!was_initialized) {
    notify_uninit();
  }

655 656 657 658 659 660 661
#else

  girara_notify(session, GIRARA_WARNING, "Girara was compiled without libnotify support.");

#endif
}

662 663 664
void
girara_notify(girara_session_t* session, int level, const char* format, ...)
{
Moritz Lipp's avatar
Moritz Lipp committed
665 666 667 668 669
  if (session == NULL
      || session->gtk.notification_text == NULL
      || session->gtk.notification_area == NULL
      || session->gtk.inputbar == NULL
      || session->gtk.view == NULL) {
670 671 672
    return;
  }

673 674
  bool error_class = false;
  bool warning_class = false;
675

676 677
  switch (level) {
    case GIRARA_ERROR:
678
      error_class = true;
679 680
      break;
    case GIRARA_WARNING:
681
      warning_class = true;
682 683 684 685 686 687 688
      break;
    case GIRARA_INFO:
      break;
    default:
      return;
  }

689 690 691 692 693 694 695 696 697 698 699 700 701
  if (error_class == true) {
    widget_add_class(session->gtk.notification_area, "notification-error");
    widget_add_class(session->gtk.notification_text, "notification-error");
  } else {
    widget_remove_class(session->gtk.notification_area, "notification-error");
    widget_remove_class(session->gtk.notification_text, "notification-error");
  }
  if (warning_class == true) {
    widget_add_class(session->gtk.notification_area, "notification-warning");
    widget_add_class(session->gtk.notification_text, "notification-warning");
  } else {
    widget_remove_class(session->gtk.notification_area, "notification-warning");
    widget_remove_class(session->gtk.notification_text, "notification-warning");
702
  }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
703

704 705 706 707 708 709 710 711 712 713 714
  /* prepare message */
  va_list ap;
  va_start(ap, format);
  char* message = g_strdup_vprintf(format, ap);
  va_end(ap);

  gtk_label_set_markup(GTK_LABEL(session->gtk.notification_text), message);
  g_free(message);

  /* update visibility */
  gtk_widget_show(GTK_WIDGET(session->gtk.notification_area));
Moritz Lipp's avatar
Moritz Lipp committed
715
  gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar));
Moritz Lipp's avatar
Moritz Lipp committed
716 717

  gtk_widget_grab_focus(GTK_WIDGET(session->gtk.view));
718 719
}

Sebastian Ramacher's avatar
Sebastian Ramacher committed
720 721
void
girara_dialog(girara_session_t* session, const char* dialog, bool
Moritz Lipp's avatar
Moritz Lipp committed
722
    invisible, girara_callback_inputbar_key_press_event_t key_press_event,
Moritz Lipp's avatar
Moritz Lipp committed
723
    girara_callback_inputbar_activate_t activate_event, void* data)
Moritz Lipp's avatar
Moritz Lipp committed
724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747
{
  if (session == NULL || session->gtk.inputbar == NULL
      || session->gtk.inputbar_dialog == NULL
      || session->gtk.inputbar_entry == NULL) {
    return;
  }

  gtk_widget_show(GTK_WIDGET(session->gtk.inputbar_dialog));

  /* set dialog message */
  if (dialog != NULL) {
    gtk_label_set_markup(session->gtk.inputbar_dialog, dialog);
  }

  /* set input visibility */
  if (invisible == true) {
    gtk_entry_set_visibility(session->gtk.inputbar_entry, FALSE);
  } else {
    gtk_entry_set_visibility(session->gtk.inputbar_entry, TRUE);
  }

  /* set handler */
  session->signals.inputbar_custom_activate        = activate_event;
  session->signals.inputbar_custom_key_press_event = key_press_event;
Moritz Lipp's avatar
Moritz Lipp committed
748
  session->signals.inputbar_custom_data            = data;
Moritz Lipp's avatar
Moritz Lipp committed
749 750

  /* focus inputbar */
751
  girara_sc_focus_inputbar(session, NULL, NULL, 0);
Moritz Lipp's avatar
Moritz Lipp committed
752 753
}

754 755 756 757 758 759 760
bool
girara_set_view(girara_session_t* session, GtkWidget* widget)
{
  g_return_val_if_fail(session != NULL, false);

  GtkWidget* child = gtk_bin_get_child(GTK_BIN(session->gtk.viewport));

Sebastian Ramacher's avatar
Sebastian Ramacher committed
761
  if (child != NULL) {
762 763 764 765 766 767
    g_object_ref(child);
    gtk_container_remove(GTK_CONTAINER(session->gtk.viewport), child);
  }

  gtk_container_add(GTK_CONTAINER(session->gtk.viewport), widget);
  gtk_widget_show_all(widget);
768
  gtk_widget_grab_focus(session->gtk.view);
769 770 771

  return true;
}
Sebastian Ramacher's avatar
Sebastian Ramacher committed
772 773 774 775 776 777 778 779 780 781 782 783 784

void
girara_mode_set(girara_session_t* session, girara_mode_t mode)
{
  g_return_if_fail(session != NULL);

  session->modes.current_mode = mode;
}

girara_mode_t
girara_mode_add(girara_session_t* session, const char* name)
{
  g_return_val_if_fail(session  != NULL, FALSE);
785
  g_return_val_if_fail(name != NULL && name[0] != '\0', FALSE);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
786 787 788 789 790 791 792 793 794 795 796 797 798 799

  girara_mode_t last_index = 0;
  GIRARA_LIST_FOREACH(session->modes.identifiers, girara_mode_string_t*, iter, mode)
    if (mode->index > last_index) {
      last_index = mode->index;
    }
  GIRARA_LIST_FOREACH_END(session->modes.identifiers, girara_mode_string_t*, iter, mode);

  /* create new mode identifier */
  girara_mode_string_t* mode = g_slice_new(girara_mode_string_t);
  mode->index = last_index + 1;
  mode->name = g_strdup(name);
  girara_list_append(session->modes.identifiers, mode);

Moritz Lipp's avatar
Moritz Lipp committed
800
  return mode->index;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
801 802 803 804 805
}

void
girara_mode_string_free(girara_mode_string_t* mode)
{
Sebastian Ramacher's avatar
CS  
Sebastian Ramacher committed
806
  if (mode == NULL) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
807 808 809 810 811 812 813 814 815 816 817 818 819 820
    return;
  }

  g_free(mode->name);
  g_slice_free(girara_mode_string_t, mode);
}

girara_mode_t
girara_mode_get(girara_session_t* session)
{
  g_return_val_if_fail(session != NULL, 0);

  return session->modes.current_mode;
}
821 822 823 824 825 826 827 828 829 830 831 832 833

bool
girara_set_window_title(girara_session_t* session, const char* name)
{
  if (session == NULL || session->gtk.window == NULL || name == NULL) {
    return false;
  }

  gtk_window_set_title(GTK_WINDOW(session->gtk.window), name);

  return true;
}

834 835 836 837 838 839 840 841 842 843 844 845
bool
girara_set_window_icon(girara_session_t* session, const char* name)
{
  if (session == NULL || session->gtk.window == NULL || name == NULL) {
    return false;
  }

  gtk_window_set_icon_name(GTK_WINDOW(session->gtk.window), name);

  return true;
}

Moritz Lipp's avatar
Moritz Lipp committed
846 847 848
girara_list_t*
girara_get_command_history(girara_session_t* session)
{
Sebastian Ramacher's avatar
CS  
Sebastian Ramacher committed
849
  g_return_val_if_fail(session != NULL, NULL);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
850
  return girara_input_history_list(session->command_history);
Moritz Lipp's avatar
Moritz Lipp committed
851
}
852 853 854 855 856 857 858 859 860

GiraraTemplate*
girara_session_get_template(girara_session_t* session)
{
  g_return_val_if_fail(session != NULL, NULL);

  return session->private_data->csstemplate;
}