completion.c 18.2 KB
Newer Older
1
/* SPDX-License-Identifier: Zlib */
2 3 4

#include <math.h>
#include <string.h>
Sebastian Ramacher's avatar
Sebastian Ramacher committed
5
#include <stdlib.h>
6

Sebastian Ramacher's avatar
Sebastian Ramacher committed
7 8 9 10 11
#include "completion.h"
#include "internal.h"
#include "session.h"
#include "settings.h"
#include "datastructures.h"
Sebastian Ramacher's avatar
Sebastian Ramacher committed
12
#include "utils.h"
Sebastian Ramacher's avatar
Sebastian Ramacher committed
13
#include "shortcuts.h"
14

Sebastian Ramacher's avatar
Sebastian Ramacher committed
15
static GtkEventBox* girara_completion_row_create(const char*, const char*, bool);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
16
static void girara_completion_row_set_color(girara_session_t*, GtkEventBox*, int);
17 18 19 20

/* completion */
struct girara_internal_completion_entry_s
{
Moritz Lipp's avatar
Moritz Lipp committed
21 22 23
  bool group; /**< The entry is a group */
  char* value; /**< Name of the entry */
  GtkEventBox* widget; /**< Eventbox widget */
24 25
};

Sebastian Ramacher's avatar
Sebastian Ramacher committed
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
/**
 * Structure of a completion element
 */
struct girara_completion_element_s
{
  char *value; /**> Name of the completion element */
  char *description; /**> Description of the completion element */
};

/**
 * Structure of a completion group
 */
struct girara_completion_group_s
{
  char *value; /**> Name of the completion element */
41
  girara_list_t *elements; /**> Elements of the completion group */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
42 43 44 45 46 47 48
};

/**
 * Structure of a completion object
 */
struct girara_completion_s
{
49
  girara_list_t *groups; /**> Containing completion groups */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
50 51
};

52 53
typedef struct girara_internal_completion_entry_s girara_internal_completion_entry_t;

54 55 56 57 58 59 60 61 62 63
static void
completion_element_free(girara_completion_element_t* element)
{
  if (element == NULL) {
    return;
  }

  /* free element */
  g_free(element->value);
  g_free(element->description);
64
  g_slice_free(girara_completion_element_t, element);
65
}
66 67

girara_completion_t*
Sebastian Ramacher's avatar
Sebastian Ramacher committed
68
girara_completion_init(void)
69 70
{
  girara_completion_t *completion = g_slice_new(girara_completion_t);
71 72
  completion->groups = girara_list_new2(
      (girara_free_function_t) girara_completion_group_free);
73 74 75 76 77

  return completion;
}

girara_completion_group_t*
Sebastian Ramacher's avatar
Sebastian Ramacher committed
78
girara_completion_group_create(girara_session_t* UNUSED(session), const char* name)
79 80 81 82
{
  girara_completion_group_t* group = g_slice_new(girara_completion_group_t);

  group->value    = name ? g_strdup(name) : NULL;
83 84
  group->elements = girara_list_new2(
      (girara_free_function_t) completion_element_free);
85

Moritz Lipp's avatar
Moritz Lipp committed
86 87 88 89 90
  if (group->elements == NULL) {
    g_slice_free(girara_completion_group_t, group);
    return NULL;
  }

91 92 93 94 95 96 97 98 99
  return group;
}

void
girara_completion_add_group(girara_completion_t* completion, girara_completion_group_t* group)
{
  g_return_if_fail(completion != NULL);
  g_return_if_fail(group      != NULL);

100
  girara_list_append(completion->groups, group);
101 102
}

103 104 105 106 107 108 109
void
girara_completion_group_free(girara_completion_group_t* group)
{
  if (group == NULL) {
    return;
  }

110 111
  g_free(group->value);
  girara_list_free(group->elements);
112 113 114
  g_slice_free(girara_completion_group_t, group);
}

115 116 117 118 119
void
girara_completion_free(girara_completion_t* completion)
{
  g_return_if_fail(completion != NULL);

120
  girara_list_free(completion->groups);
Moritz Lipp's avatar
Moritz Lipp committed
121
  /* free completion */
122 123 124 125
  g_slice_free(girara_completion_t, completion);
}

void
Sebastian Ramacher's avatar
Sebastian Ramacher committed
126
girara_completion_group_add_element(girara_completion_group_t* group, const char* name, const char* description)
127 128 129 130 131 132 133 134
{
  g_return_if_fail(group   != NULL);
  g_return_if_fail(name    != NULL);

  girara_completion_element_t* new_element = g_slice_new(girara_completion_element_t);

  new_element->value       = g_strdup(name);
  new_element->description = description ?  g_strdup(description) : NULL;
Moritz Lipp's avatar
Moritz Lipp committed
135

136
  girara_list_append(group->elements, new_element);
137 138 139
}

bool
140
girara_isc_completion(girara_session_t* session, girara_argument_t* argument, girara_event_t* UNUSED(event), unsigned int UNUSED(t))
141 142 143 144
{
  g_return_val_if_fail(session != NULL, false);

  /* get current text */
Moritz Lipp's avatar
Moritz Lipp committed
145 146 147 148 149
  gchar *input = gtk_editable_get_chars(GTK_EDITABLE(session->gtk.inputbar_entry), 0, -1);
  if (input == NULL) {
    return false;
  }

150
  const size_t input_length = strlen(input);
151

Moritz Lipp's avatar
Moritz Lipp committed
152
  if (input_length == 0 || input[0] != ':') {
153 154 155 156
    g_free(input);
    return false;
  }

157 158 159 160 161
  gchar** elements = NULL;
  gint    n_parameter = 0;
  if (input_length > 1) {
    if (g_shell_parse_argv(input + 1, &n_parameter, &elements, NULL) == FALSE) {
      g_free(input);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
162
      return false;
163
    }
Moritz Lipp's avatar
Moritz Lipp committed
164
  } else {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
165
    elements = g_try_malloc0(2 * sizeof(char*));
Sebastian Ramacher's avatar
Sebastian Ramacher committed
166
    if (elements == NULL) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
167 168 169
      g_free(input);
      return false;
    }
170 171
    elements[0] = g_strdup("");
  }
Moritz Lipp's avatar
Moritz Lipp committed
172

173 174 175
  if (n_parameter == 1 && input[input_length-1] == ' ') {
    n_parameter += 1;
  }
Moritz Lipp's avatar
Moritz Lipp committed
176

177 178 179 180 181 182
  g_free(input);

  /* get current values */
  gchar *current_command   = (elements[0] != NULL && elements[0][0] != '\0') ? g_strdup(elements[0]) : NULL;
  gchar *current_parameter = (elements[0] != NULL && elements[1] != NULL)    ? g_strdup(elements[1]) : NULL;

Moritz Lipp's avatar
Moritz Lipp committed
183
  size_t current_command_length = current_command ? strlen(current_command) : 0;
184 185 186 187 188 189

  static GList* entries           = NULL;
  static GList* entries_current   = NULL;
  static char *previous_command   = NULL;
  static char *previous_parameter = NULL;
  static bool command_mode        = true;
190
  static size_t previous_length   = 0;
191

192 193
  const bool is_single_entry = (1 == g_list_length(entries));

194 195 196 197 198
  /* delete old list iff
   *   the completion should be hidden
   *   the current command differs from the previous one
   *   the current parameter differs from the previous one
   *   no current command is given
199
   *   there is only one completion entry
200
   */
Moritz Lipp's avatar
Moritz Lipp committed
201
  if ( (argument->n == GIRARA_HIDE) ||
Sebastian Ramacher's avatar
Sebastian Ramacher committed
202 203
      (current_parameter && previous_parameter && g_strcmp0(current_parameter, previous_parameter)) ||
      (current_command && previous_command && g_strcmp0(current_command, previous_command)) ||
204 205
      (input_length != previous_length) ||
      is_single_entry
206 207
    )
  {
Moritz Lipp's avatar
Moritz Lipp committed
208
    if (session->gtk.results != NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
209 210
      /* destroy elements */
      for (GList* element = entries; element; element = g_list_next(element)) {
211
        girara_internal_completion_entry_t* entry = (girara_internal_completion_entry_t*) element->data;
Moritz Lipp's avatar
Moritz Lipp committed
212

Moritz Lipp's avatar
Moritz Lipp committed
213
        if (entry != NULL) {
214 215 216 217 218 219 220 221 222 223 224
          gtk_widget_destroy(GTK_WIDGET(entry->widget));
          g_free(entry->value);
          g_slice_free(girara_internal_completion_entry_t, entry);
        }
      }

      g_list_free(entries);
      entries         = NULL;
      entries_current = NULL;

      /* delete row box */
225 226
      gtk_widget_destroy(GTK_WIDGET(session->gtk.results));
      session->gtk.results = NULL;
227 228
    }

229
    command_mode = true;
230

Moritz Lipp's avatar
Moritz Lipp committed
231
    if (argument->n == GIRARA_HIDE) {
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
      g_free(previous_command);
      previous_command = NULL;

      g_free(previous_parameter);
      previous_parameter = NULL;

      g_strfreev(elements);

      g_free(current_command);
      g_free(current_parameter);

      return false;
    }
  }

  /* create new list iff
   *  there is no current list
   */
Moritz Lipp's avatar
Moritz Lipp committed
250
  if (session->gtk.results == NULL) {
251
    session->gtk.results = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));
252
    widget_add_class(GTK_WIDGET(session->gtk.results), "completion-box");
Moritz Lipp's avatar
Moritz Lipp committed
253

Moritz Lipp's avatar
Moritz Lipp committed
254
    if (session->gtk.results == NULL) {
255 256 257 258 259 260 261
      g_free(current_command);
      g_free(current_parameter);

      g_strfreev(elements);
      return false;
    }

Moritz Lipp's avatar
Moritz Lipp committed
262 263
    if (n_parameter <= 1) {
    /* based on commands */
264
      command_mode = true;
Moritz Lipp's avatar
Moritz Lipp committed
265 266

      /* create command rows */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
267
      GIRARA_LIST_FOREACH_BODY(session->bindings.commands, girara_command_t*, command,
Moritz Lipp's avatar
Moritz Lipp committed
268 269 270 271 272 273 274 275 276
        if (current_command == NULL ||
            (command->command != NULL && !strncmp(current_command, command->command, current_command_length)) ||
            (command->abbr != NULL && !strncmp(current_command, command->abbr,    current_command_length))
          )
        {
          /* create entry */
          girara_internal_completion_entry_t* entry = g_slice_new(girara_internal_completion_entry_t);
          entry->group  = FALSE;
          entry->value  = g_strdup(command->command);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
277
          entry->widget = girara_completion_row_create(command->command, command->description, FALSE);
Moritz Lipp's avatar
Moritz Lipp committed
278 279 280 281 282 283

          entries = g_list_append(entries, entry);

          /* show entry row */
          gtk_box_pack_start(session->gtk.results, GTK_WIDGET(entry->widget), FALSE, FALSE, 0);
        }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
284
      );
Moritz Lipp's avatar
Moritz Lipp committed
285 286
    }

287
    /* based on parameters */
Moritz Lipp's avatar
Moritz Lipp committed
288 289 290 291 292 293
    if (n_parameter > 1 || g_list_length(entries) == 1) {
      /* if only one command exists try to run parameter completion */
      if (g_list_length(entries) == 1) {
        girara_internal_completion_entry_t* entry = g_list_first(entries)->data;

        /* unset command mode */
294 295
        command_mode           = false;
        current_command        = entry->value;
Moritz Lipp's avatar
Moritz Lipp committed
296 297 298 299 300 301 302 303
        current_command_length = strlen(current_command);

        /* clear list */
        gtk_widget_destroy(GTK_WIDGET(entry->widget));

        entries = g_list_remove(entries, g_list_first(entries)->data);
        g_slice_free(girara_internal_completion_entry_t, entry);
      }
Moritz Lipp's avatar
Moritz Lipp committed
304

305 306
      /* search matching command */
      girara_command_t* command = NULL;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
307
      GIRARA_LIST_FOREACH_BODY(session->bindings.commands, girara_command_t*, command_it,
308 309
        if ( (current_command != NULL && command_it->command != NULL && !strncmp(current_command, command_it->command, current_command_length)) ||
             (current_command != NULL && command_it->abbr != NULL    && !strncmp(current_command, command_it->abbr,    current_command_length))
310 311
          )
        {
312 313 314 315
          g_free(previous_command);
          previous_command = g_strdup(command_it->command);
          command = command_it;
          break;
316
        }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
317
      );
318

Moritz Lipp's avatar
Moritz Lipp committed
319
      if (command == NULL) {
320 321 322 323 324 325 326
        g_free(current_command);
        g_free(current_parameter);

        g_strfreev(elements);
        return false;
      }

327
      if (command->completion == NULL) {
328
          girara_internal_completion_entry_t* entry = g_slice_new(girara_internal_completion_entry_t);
329 330
          entry->group  = FALSE;
          entry->value  = g_strdup(command->command);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
331
          entry->widget = girara_completion_row_create(command->command, command->description, FALSE);
332 333 334

          entries = g_list_append(entries, entry);

335
          gtk_box_pack_start(session->gtk.results, GTK_WIDGET(entry->widget), FALSE, FALSE, 0);
336 337 338 339 340 341 342 343 344 345 346 347 348 349
          command_mode = true;
      } else {
        /* generate completion result
         * XXX: the last argument should only be current_paramater ... but
         * therefore the completion functions would need to handle NULL correctly
         * (see cc_open in zathura). */
        girara_completion_t *result = command->completion(session, current_parameter ? current_parameter : "");

        if (result == NULL || result->groups == NULL) {
          g_free(current_command);
          g_free(current_parameter);

          g_strfreev(elements);
          return false;
350 351
        }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
352
        GIRARA_LIST_FOREACH_BODY_WITH_ITER(result->groups, girara_completion_group_t*, iter, group,
Sebastian Ramacher's avatar
Sebastian Ramacher committed
353 354 355 356 357
          if (group->elements == NULL || girara_list_size(group->elements) == 0) {
            girara_list_iterator_next(iter);
            continue;
          }

358 359 360 361 362
          /* create group entry */
          if (group->value != NULL) {
            girara_internal_completion_entry_t* entry = g_slice_new(girara_internal_completion_entry_t);
            entry->group  = TRUE;
            entry->value  = g_strdup(group->value);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
363
            entry->widget = girara_completion_row_create(group->value, NULL, TRUE);
364

365
            entries = g_list_append(entries, entry);
366

367 368 369
            gtk_box_pack_start(session->gtk.results, GTK_WIDGET(entry->widget), FALSE, FALSE, 0);
          }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
370
          GIRARA_LIST_FOREACH_BODY_WITH_ITER(group->elements, girara_completion_element_t*, iter2, element,
371 372 373
            girara_internal_completion_entry_t* entry = g_slice_new(girara_internal_completion_entry_t);
            entry->group  = FALSE;
            entry->value  = g_strdup(element->value);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
374
            entry->widget = girara_completion_row_create(element->value, element->description, FALSE);
375 376

            entries = g_list_append(entries, entry);
377

378
            gtk_box_pack_start(session->gtk.results, GTK_WIDGET(entry->widget), FALSE, FALSE, 0);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
379 380
          );
        );
381 382 383 384
        girara_completion_free(result);

        command_mode = false;
      }
385 386
    }

Moritz Lipp's avatar
Moritz Lipp committed
387
    if (entries != NULL) {
388
      entries_current = (argument->n == GIRARA_NEXT) ? g_list_last(entries) : entries;
389
      gtk_box_pack_start(session->private_data->gtk.bottom_box, GTK_WIDGET(session->gtk.results), FALSE, FALSE, 0);
390
      gtk_widget_show(GTK_WIDGET(session->gtk.results));
391 392 393 394
    }
  }

  /* update entries */
395
  unsigned int n_elements = g_list_length(entries);
Moritz Lipp's avatar
Moritz Lipp committed
396
  if (entries != NULL && n_elements > 0) {
397 398 399 400 401 402 403 404
    if (n_elements > 1) {
      girara_completion_row_set_color(session, ((girara_internal_completion_entry_t *) entries_current->data)->widget, GIRARA_NORMAL);

      bool next_group = FALSE;

      for (unsigned int i = 0; i < n_elements; i++) {
        if (argument->n == GIRARA_NEXT || argument->n == GIRARA_NEXT_GROUP) {
          GList* entry = g_list_next(entries_current);
405
          if (entry == NULL) {
406 407
            entry = g_list_first(entries);
          }
408

409 410 411
          entries_current = entry;
        } else if (argument->n == GIRARA_PREVIOUS || argument->n == GIRARA_PREVIOUS_GROUP) {
          GList* entry = g_list_previous(entries_current);
Moritz Lipp's avatar
Moritz Lipp committed
412
          if (entry == NULL) {
413 414
            entry = g_list_last(entries);
          }
415

416
          entries_current = entry;
Moritz Lipp's avatar
Moritz Lipp committed
417
        }
418

419
        if (((girara_internal_completion_entry_t*) entries_current->data)->group) {
Moritz Lipp's avatar
Moritz Lipp committed
420
          if (command_mode == false && (argument->n == GIRARA_NEXT_GROUP || argument->n == GIRARA_PREVIOUS_GROUP)) {
421 422
            next_group = TRUE;
          }
423
          continue;
424
        } else {
Moritz Lipp's avatar
Moritz Lipp committed
425
          if (command_mode == false && (next_group == 0) && (argument->n == GIRARA_NEXT_GROUP || argument->n == GIRARA_PREVIOUS_GROUP)) {
426 427 428
            continue;
          }
          break;
Moritz Lipp's avatar
Moritz Lipp committed
429
        }
430 431
      }

432 433 434
      girara_completion_row_set_color(session, ((girara_internal_completion_entry_t *) entries_current->data)->widget, GIRARA_HIGHLIGHT);

      /* hide other items */
435 436
      unsigned int n_completion_items = 15;
      girara_setting_get(session, "n-completion-items", &n_completion_items);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
437 438
      unsigned int uh = ceil(n_completion_items / 2.0);
      unsigned int lh = floor(n_completion_items / 2.0);
439 440 441

      unsigned int current_item = g_list_position(entries, entries_current);

Sebastian Ramacher's avatar
Sebastian Ramacher committed
442
      GList* tmpentry = entries;
443 444 445 446 447 448 449
      for (unsigned int i = 0; i < n_elements; i++) {
        if (
            (i >= (current_item - lh) && (i <= current_item + uh)) ||
            (i < n_completion_items && current_item < lh) ||
            (i >= (n_elements - n_completion_items) && (current_item >= (n_elements - uh)))
          )
        {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
450
          gtk_widget_show(GTK_WIDGET(((girara_internal_completion_entry_t*) tmpentry->data)->widget));
451
        } else {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
452
          gtk_widget_hide(GTK_WIDGET(((girara_internal_completion_entry_t*) tmpentry->data)->widget));
453
        }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
454 455

        tmpentry = g_list_next(tmpentry);
Moritz Lipp's avatar
Moritz Lipp committed
456
      }
457 458
    } else {
      gtk_widget_hide(GTK_WIDGET(((girara_internal_completion_entry_t*) (g_list_nth(entries, 0))->data)->widget));
459 460 461 462
    }

    /* update text */
    char* temp;
463
    char* escaped_value = girara_escape_string(((girara_internal_completion_entry_t *) entries_current->data)->value);
Moritz Lipp's avatar
Moritz Lipp committed
464
    if (command_mode == true) {
465
      char* space = (n_elements == 1) ? " " : "";
466
      temp = g_strconcat(":", escaped_value, space, NULL);
Moritz Lipp's avatar
Moritz Lipp committed
467
    } else {
468
      temp = g_strconcat(":", previous_command, " ", escaped_value, NULL);
Moritz Lipp's avatar
Moritz Lipp committed
469
    }
470

Moritz Lipp's avatar
Moritz Lipp committed
471 472
    gtk_entry_set_text(session->gtk.inputbar_entry, temp);
    gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), -1);
473
    g_free(escaped_value);
474 475 476 477 478 479

    /* update previous */
    g_free(previous_command);
    g_free(previous_parameter);
    previous_command   = g_strdup((command_mode) ? ((girara_internal_completion_entry_t*) entries_current->data)->value : current_command);
    previous_parameter = g_strdup((command_mode) ? current_parameter : ((girara_internal_completion_entry_t*) entries_current->data)->value);
480 481
    previous_length    = strlen(temp);
    g_free(temp);
482 483 484 485 486 487 488 489 490 491
  }

  g_free(current_command);
  g_free(current_parameter);

  g_strfreev(elements);

  return false;
}

Sebastian Ramacher's avatar
Sebastian Ramacher committed
492
static GtkEventBox*
Sebastian Ramacher's avatar
Sebastian Ramacher committed
493
girara_completion_row_create(const char* command, const char* description, bool group)
494
{
495 496
  GtkBox *col = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0));

497 498 499 500 501
  GtkEventBox *row = GTK_EVENT_BOX(gtk_event_box_new());

  GtkLabel *show_command     = GTK_LABEL(gtk_label_new(NULL));
  GtkLabel *show_description = GTK_LABEL(gtk_label_new(NULL));

Sebastian Ramacher's avatar
Sebastian Ramacher committed
502 503 504 505
  gtk_widget_set_halign(GTK_WIDGET(show_command), GTK_ALIGN_START);
  gtk_widget_set_valign(GTK_WIDGET(show_command), GTK_ALIGN_START);
  gtk_widget_set_halign(GTK_WIDGET(show_description), GTK_ALIGN_END);
  gtk_widget_set_valign(GTK_WIDGET(show_description), GTK_ALIGN_START);
506 507 508 509

  gtk_label_set_use_markup(show_command,     TRUE);
  gtk_label_set_use_markup(show_description, TRUE);

510 511
  gtk_label_set_ellipsize(show_command, PANGO_ELLIPSIZE_END);
  gtk_label_set_ellipsize(show_description, PANGO_ELLIPSIZE_END);
512

513 514 515 516 517 518 519
  gchar* c = g_markup_printf_escaped(FORMAT_COMMAND,     command ? command : "");
  gchar* d = g_markup_printf_escaped(FORMAT_DESCRIPTION, description ? description : "");
  gtk_label_set_markup(show_command,     c);
  gtk_label_set_markup(show_description, d);
  g_free(c);
  g_free(d);

520
  const char* class = group == true ? "completion-group" : "completion";
521 522 523
  widget_add_class(GTK_WIDGET(show_command), class);
  widget_add_class(GTK_WIDGET(show_description), class);
  widget_add_class(GTK_WIDGET(row), class);
524
  widget_add_class(GTK_WIDGET(col), class);
525

Sebastian Ramacher's avatar
Sebastian Ramacher committed
526 527
  gtk_box_pack_start(GTK_BOX(col), GTK_WIDGET(show_command),     TRUE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(col), GTK_WIDGET(show_description), TRUE, TRUE, 0);
528 529 530 531 532 533 534

  gtk_container_add(GTK_CONTAINER(row), GTK_WIDGET(col));
  gtk_widget_show_all(GTK_WIDGET(row));

  return row;
}

Sebastian Ramacher's avatar
Sebastian Ramacher committed
535
static void
536 537 538 539 540
girara_completion_row_set_color(girara_session_t* session, GtkEventBox* row, int mode)
{
  g_return_if_fail(session != NULL);
  g_return_if_fail(row     != NULL);

541 542
  GtkBox* col     = GTK_BOX(gtk_bin_get_child(GTK_BIN(row)));
  GList* items    = gtk_container_get_children(GTK_CONTAINER(col));
Sebastian Ramacher's avatar
Sebastian Ramacher committed
543 544
  GtkWidget* cmd  = GTK_WIDGET(g_list_nth_data(items, 0));
  GtkWidget* desc = GTK_WIDGET(g_list_nth_data(items, 1));
545

546
  if (mode == GIRARA_HIGHLIGHT) {
547 548 549
    gtk_widget_set_state_flags(cmd, GTK_STATE_FLAG_SELECTED, false);
    gtk_widget_set_state_flags(desc, GTK_STATE_FLAG_SELECTED, false);
    gtk_widget_set_state_flags(GTK_WIDGET(row), GTK_STATE_FLAG_SELECTED, false);
550
  } else {
551 552 553
    gtk_widget_unset_state_flags(cmd, GTK_STATE_FLAG_SELECTED);
    gtk_widget_unset_state_flags(desc, GTK_STATE_FLAG_SELECTED);
    gtk_widget_unset_state_flags(GTK_WIDGET(row), GTK_STATE_FLAG_SELECTED);
554
  }
555 556 557

  g_list_free(items);
}