rofi  1.7.0
listview.c
Go to the documentation of this file.
1 /*
2  * rofi
3  *
4  * MIT/X11 License
5  * Copyright © 2013-2021 Qball Cow <qball@gmpclient.org>
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining
8  * a copy of this software and associated documentation files (the
9  * "Software"), to deal in the Software without restriction, including
10  * without limitation the rights to use, copy, modify, merge, publish,
11  * distribute, sublicense, and/or sell copies of the Software, and to
12  * permit persons to whom the Software is furnished to do so, subject to
13  * the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be
16  * included in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25  *
26  */
27 
28 #include <config.h>
29 #include <glib.h>
30 #include <widgets/box.h>
31 #include <widgets/icon.h>
32 #include <widgets/listview.h>
33 #include <widgets/scrollbar.h>
34 #include <widgets/textbox.h>
35 #include <widgets/widget.h>
36 
37 #include "settings.h"
38 #include "theme.h"
39 
40 #include "timings.h"
41 
43 #define DEFAULT_SPACING 2
44 
49 #define LISTVIEW ROFI_ORIENTATION_VERTICAL
51 #define BARVIEW ROFI_ORIENTATION_HORIZONTAL
52 
57 typedef enum { LEFT_TO_RIGHT = 0, RIGHT_TO_LEFT = 1 } MoveDirection;
58 
59 typedef struct {
60  box *box;
65 
66 struct _listview {
68 
70 
71  // RChanged
72  // Text needs to be repainted.
73  unsigned int rchanged;
74  // Administration
75 
76  unsigned int cur_page;
77  unsigned int last_offset;
78  unsigned int selected;
79 
80  unsigned int element_height;
81  unsigned int max_rows;
82  unsigned int max_elements;
83 
84  //
85  gboolean fixed_columns;
86  unsigned int cur_columns;
87  unsigned int req_elements;
88  unsigned int cur_elements;
89 
91  unsigned int menu_lines;
92  unsigned int max_displayed_lines;
93  unsigned int menu_columns;
94  unsigned int fixed_num_lines;
95  unsigned int dynamic;
96  unsigned int eh;
97  unsigned int reverse;
98  gboolean cycle;
99  gboolean multi_select;
100 
102 
105 
107  void *udata;
108 
110 
111  xcb_timestamp_t last_click;
114 
116 
117  PangoEllipsizeMode emode;
119  struct {
121  unsigned int cur_visible;
123 };
129 const char *const listview_theme_prop_names[][3] = {
131  {"normal.normal", "selected.normal", "alternate.normal"},
133  {"normal.urgent", "selected.urgent", "alternate.urgent"},
135  {"normal.active", "selected.active", "alternate.active"},
136 };
137 
139  widget *w = WIDGET(r.box);
140  TextBoxFontType t = tbft & STATE_MASK;
141  if (w == NULL) {
142  return;
143  }
144  // ACTIVE has priority over URGENT if both set.
145  if (t == (URGENT | ACTIVE)) {
146  t = ACTIVE;
147  }
148  switch ((tbft & FMOD_MASK)) {
149  case HIGHLIGHT:
151  break;
152  case ALT:
154  break;
155  default:
157  break;
158  }
159 }
160 static void listview_add_widget(listview *lv, _listview_row *row, widget *wid,
161  const char *label) {
163  if (strcasecmp(label, "element-icon") == 0) {
164  row->icon = icon_create(WIDGET(wid), "element-icon");
165  box_add((box *)wid, WIDGET(row->icon), FALSE);
166  } else if (strcasecmp(label, "element-text") == 0) {
167  row->textbox =
168  textbox_create(WIDGET(wid), WIDGET_TYPE_TEXTBOX_TEXT, "element-text",
169  TB_AUTOHEIGHT | flags, NORMAL, "DDD", 0, 0);
170  box_add((box *)wid, WIDGET(row->textbox), TRUE);
171  } else if (strcasecmp(label, "element-index") == 0) {
172  row->index =
173  textbox_create(WIDGET(wid), WIDGET_TYPE_TEXTBOX_TEXT, "element-index",
174  TB_AUTOHEIGHT, NORMAL, " ", 0, 0);
175  box_add((box *)wid, WIDGET(row->index), FALSE);
176  } else {
177  widget *wid2 = (widget *)box_create(wid, label, ROFI_ORIENTATION_VERTICAL);
178  box_add((box *)wid, WIDGET(wid2), TRUE);
179  GList *list = rofi_theme_get_list(WIDGET(wid2), "children", "");
180  for (GList *iter = g_list_first(list); iter != NULL;
181  iter = g_list_next(iter)) {
182  listview_add_widget(lv, row, wid2, (const char *)iter->data);
183  }
184  }
185 }
186 
188  row->box = box_create(WIDGET(lv), "element", ROFI_ORIENTATION_HORIZONTAL);
190  GList *list = NULL;
191  if (config.show_icons) {
192  list = rofi_theme_get_list(WIDGET(row->box), "children",
193  "element-icon,element-text");
194  } else {
195  list = rofi_theme_get_list(WIDGET(row->box), "children", "element-text");
196  }
197 
198  row->textbox = NULL;
199  row->icon = NULL;
200  row->index = NULL;
201 
202  for (GList *iter = g_list_first(list); iter != NULL;
203  iter = g_list_next(iter)) {
204  listview_add_widget(lv, row, WIDGET(row->box), (const char *)iter->data);
205  }
206  g_list_free_full(list, g_free);
207 }
208 
209 static int listview_get_desired_height(widget *wid);
210 
211 static void listview_free(widget *wid) {
212  listview *lv = (listview *)wid;
213  for (unsigned int i = 0; i < lv->cur_elements; i++) {
214  widget_free(WIDGET(lv->boxes[i].box));
215  }
216  g_free(lv->boxes);
217 
218  g_free(lv->listview_name);
220  g_free(lv);
221 }
222 static unsigned int scroll_per_page_barview(listview *lv) {
223  unsigned int offset = lv->last_offset;
224 
225  // selected row is always visible.
226  // If selected is visible do not scroll.
227  if (lv->selected < lv->last_offset) {
228  offset = lv->selected;
229  lv->rchanged = TRUE;
230  } else if (lv->selected >= (lv->last_offset + lv->barview.cur_visible)) {
231  offset = lv->selected;
232  lv->rchanged = TRUE;
233  }
234  return offset;
235 }
236 static unsigned int scroll_per_page(listview *lv) {
237  int offset = 0;
238 
239  // selected row is always visible.
240  // If selected is visible do not scroll.
241  if (((lv->selected - (lv->last_offset)) < (lv->max_elements)) &&
242  (lv->selected >= (lv->last_offset))) {
243  offset = lv->last_offset;
244  } else {
245  // Do paginating
246  unsigned int page =
247  (lv->max_elements > 0) ? (lv->selected / lv->max_elements) : 0;
248  offset = page * lv->max_elements;
249  if (page != lv->cur_page) {
250  lv->cur_page = page;
251  lv->rchanged = TRUE;
252  }
253  // Set the position
254  // scrollbar_set_handle ( lv->scrollbar, page * lv->max_elements );
255  }
256  return offset;
257 }
258 
259 static unsigned int scroll_continious(listview *lv) {
260  unsigned int middle = (lv->max_rows - ((lv->max_rows & 1) == 0)) / 2;
261  unsigned int offset = 0;
262  if (lv->selected > middle) {
263  if (lv->selected < (lv->req_elements - (lv->max_rows - middle))) {
264  offset = lv->selected - middle;
265  }
266  // Don't go below zero.
267  else if (lv->req_elements > lv->max_rows) {
268  offset = lv->req_elements - lv->max_rows;
269  }
270  }
271  if (offset != lv->cur_page) {
272  // scrollbar_set_handle ( lv->scrollbar, offset );
273  lv->cur_page = offset;
274  lv->rchanged = TRUE;
275  }
276  return offset;
277 }
278 
279 static void update_element(listview *lv, unsigned int tb, unsigned int index,
280  gboolean full) {
281  // Select drawing mode
282  TextBoxFontType type = (index & 1) == 0 ? NORMAL : ALT;
283  type = (index) == lv->selected ? HIGHLIGHT : type;
284 
285  if (lv->boxes[tb].index) {
286  if (index < 10) {
287  char str[2] = {((index + 1) % 10) + '0', '\0'};
288  textbox_text(lv->boxes[tb].index, str);
289  } else {
290  textbox_text(lv->boxes[tb].index, " ");
291  }
292  }
293  if (lv->callback) {
294  lv->callback(lv->boxes[tb].textbox, lv->boxes[tb].icon, index, lv->udata,
295  &type, full);
296  listview_set_state(lv->boxes[tb], type);
297  }
298 }
299 
300 static void barview_draw(widget *wid, cairo_t *draw) {
301  unsigned int offset = 0;
302  listview *lv = (listview *)wid;
303  offset = scroll_per_page_barview(lv);
304  lv->last_offset = offset;
305  int spacing_hori =
307 
308  int left_offset = widget_padding_get_left(wid);
309  int right_offset = lv->widget.w - widget_padding_get_right(wid);
310  int top_offset = widget_padding_get_top(wid);
311  if (lv->cur_elements > 0) {
312  // Set new x/y position.
313  unsigned int max = MIN(lv->cur_elements, lv->req_elements - offset);
314  if (lv->rchanged) {
315  int first = TRUE;
316  int width = lv->widget.w;
317  lv->barview.cur_visible = 0;
318  width -= widget_padding_get_padding_width(wid);
319  if (lv->barview.direction == LEFT_TO_RIGHT) {
320  for (unsigned int i = 0; i < max && width > 0; i++) {
321  update_element(lv, i, i + offset, TRUE);
322  int twidth = widget_get_desired_width(WIDGET(lv->boxes[i].box));
323  if (twidth >= width) {
324  if (!first) {
325  break;
326  }
327  twidth = width;
328  }
329  widget_move(WIDGET(lv->boxes[i].box), left_offset, top_offset);
330  widget_resize(WIDGET(lv->boxes[i].box), twidth, lv->element_height);
331 
332  widget_draw(WIDGET(lv->boxes[i].box), draw);
333  width -= twidth + spacing_hori;
334  left_offset += twidth + spacing_hori;
335  first = FALSE;
336  lv->barview.cur_visible++;
337  }
338  } else {
339  for (unsigned int i = 0;
340  i < lv->cur_elements && width > 0 && i <= offset; i++) {
341  update_element(lv, i, offset - i, TRUE);
342  int twidth = widget_get_desired_width(WIDGET(lv->boxes[i].box));
343  if (twidth >= width) {
344  if (!first) {
345  break;
346  }
347  twidth = width;
348  }
349  right_offset -= twidth;
350  widget_move(WIDGET(lv->boxes[i].box), right_offset, top_offset);
351  widget_resize(WIDGET(lv->boxes[i].box), twidth, lv->element_height);
352 
353  widget_draw(WIDGET(lv->boxes[i].box), draw);
354  width -= twidth + spacing_hori;
355  right_offset -= spacing_hori;
356  first = FALSE;
357  lv->barview.cur_visible++;
358  }
359  offset -= lv->barview.cur_visible - 1;
360  lv->last_offset = offset;
361  for (unsigned int i = 0; i < (lv->barview.cur_visible / 2); i++) {
362  _listview_row temp = lv->boxes[i];
363  int sw = lv->barview.cur_visible - i - 1;
364  lv->boxes[i] = lv->boxes[sw];
365  lv->boxes[sw] = temp;
366  }
367  }
368  lv->rchanged = FALSE;
369  } else {
370  for (unsigned int i = 0; i < lv->barview.cur_visible; i++) {
371  update_element(lv, i, i + offset, TRUE);
372  widget_draw(WIDGET(lv->boxes[i].box), draw);
373  }
374  }
375  }
376 }
377 
378 static void listview_draw(widget *wid, cairo_t *draw) {
379  unsigned int offset = 0;
380  listview *lv = (listview *)wid;
382  offset = scroll_continious(lv);
383  } else {
384  offset = scroll_per_page(lv);
385  }
386  // Set these all together to make sure they update consistently.
389  if (lv->reverse) {
391  } else {
393  }
394  lv->last_offset = offset;
395  int spacing_vert = distance_get_pixel(lv->spacing, ROFI_ORIENTATION_VERTICAL);
396  int spacing_hori =
398 
399  int left_offset = widget_padding_get_left(wid);
400  int top_offset = widget_padding_get_top(wid);
401  /*
402  if ( lv->scrollbar->widget.index == 0 ) {
403  left_offset += spacing_hori + lv->scrollbar->widget.w;
404  }
405  */
406  if (lv->cur_elements > 0 && lv->max_rows > 0) {
407  // Set new x/y position.
408  unsigned int max = MIN(lv->cur_elements, lv->req_elements - offset);
409  if (lv->rchanged) {
410  unsigned int width = lv->widget.w;
411  width -= widget_padding_get_padding_width(wid);
412  if (widget_enabled(WIDGET(lv->scrollbar))) {
413  width -= spacing_hori;
414  width -= widget_get_width(WIDGET(lv->scrollbar));
415  }
416  unsigned int element_width =
417  (width - spacing_hori * (lv->cur_columns - 1)) / lv->cur_columns;
418 
419  int d = width - (element_width + spacing_hori) * (lv->cur_columns - 1) -
420  element_width;
421  if (lv->cur_columns > 1) {
422  int diff = d / (lv->cur_columns - 1);
423  if (diff >= 1) {
424  spacing_hori += 1;
425  d -= lv->cur_columns - 1;
426  }
427  }
428  for (unsigned int i = 0; i < max; i++) {
429  unsigned int ex =
430  left_offset + ((i) / lv->max_rows) * (element_width + spacing_hori);
431 
432  if ((i) / lv->max_rows == (lv->cur_columns - 1)) {
433  ex += d;
434  }
435  if (lv->reverse) {
436  unsigned int ey =
437  wid->h -
439  ((i) % lv->max_rows) * (lv->element_height + spacing_vert)) -
440  lv->element_height;
441  widget_move(WIDGET(lv->boxes[i].box), ex, ey);
442  widget_resize(WIDGET(lv->boxes[i].box), element_width,
443  lv->element_height);
444  } else {
445  unsigned int ey =
446  top_offset +
447  ((i) % lv->max_rows) * (lv->element_height + spacing_vert);
448  widget_move(WIDGET(lv->boxes[i].box), ex, ey);
449  widget_resize(WIDGET(lv->boxes[i].box), element_width,
450  lv->element_height);
451  }
452 
453  update_element(lv, i, i + offset, TRUE);
454  widget_draw(WIDGET(lv->boxes[i].box), draw);
455  }
456  lv->rchanged = FALSE;
457  } else {
458  for (unsigned int i = 0; i < max; i++) {
459  update_element(lv, i, i + offset, TRUE);
460  widget_draw(WIDGET(lv->boxes[i].box), draw);
461  }
462  }
463  }
464  widget_draw(WIDGET(lv->scrollbar), draw);
465 }
469  gint x, gint y, void *user_data);
470 static gboolean listview_element_motion_notify(widget *wid, gint x, gint y);
471 
472 static void _listview_draw(widget *wid, cairo_t *draw) {
473  listview *lv = (listview *)wid;
474  if (lv->type == LISTVIEW) {
475  listview_draw(wid, draw);
476  } else {
477  barview_draw(wid, draw);
478  }
479 }
484  unsigned int newne = 0;
485  if (lv->max_rows == 0) {
486  return;
487  }
488  if (!(lv->fixed_columns) && lv->req_elements < lv->max_elements) {
489  newne = lv->req_elements;
490  lv->cur_columns = (lv->req_elements + (lv->max_rows - 1)) / lv->max_rows;
491  } else {
492  newne = MIN(lv->req_elements, lv->max_elements);
493  lv->cur_columns = lv->menu_columns;
494  }
495  for (unsigned int i = newne; i < lv->cur_elements; i++) {
496  widget_free(WIDGET(lv->boxes[i].box));
497  }
498  lv->boxes = g_realloc(lv->boxes, newne * sizeof(_listview_row));
499  if (newne > 0) {
500  for (unsigned int i = lv->cur_elements; i < newne; i++) {
501  listview_create_row(lv, &(lv->boxes[i]));
502  widget *wid = WIDGET(lv->boxes[i].box);
504  lv);
505  if (wid != NULL) {
507  }
508 
510  }
511  }
512  lv->rchanged = TRUE;
513  lv->cur_elements = newne;
514 }
515 
516 void listview_set_num_elements(listview *lv, unsigned int rows) {
517  if (lv == NULL) {
518  return;
519  }
520  TICK_N("listview_set_num_elements");
521  lv->req_elements = rows;
523  TICK_N("Set selected");
525  TICK_N("recompute elements");
527  TICK_N("queue redraw");
528 }
529 
530 unsigned int listview_get_selected(listview *lv) {
531  if (lv != NULL) {
532  return lv->selected;
533  }
534  return 0;
535 }
536 
537 void listview_set_selected(listview *lv, unsigned int selected) {
538  if (lv && lv->req_elements > 0) {
539  lv->selected = MIN(selected, lv->req_elements - 1);
542  }
543 }
544 
545 static void listview_resize(widget *wid, short w, short h) {
546  listview *lv = (listview *)wid;
547  lv->widget.w = MAX(0, w);
548  lv->widget.h = MAX(0, h);
549  int height = lv->widget.h - widget_padding_get_padding_height(WIDGET(lv));
550  int spacing_vert = distance_get_pixel(lv->spacing, ROFI_ORIENTATION_VERTICAL);
551  lv->max_rows = (spacing_vert + height) / (lv->element_height + spacing_vert);
552  lv->max_elements = lv->max_rows * lv->menu_columns;
553 
558 
560  height);
561 
562  if (lv->type == BARVIEW) {
563  lv->max_elements = lv->menu_lines;
564  }
565 
567  widget_queue_redraw(wid);
568 }
569 
571  gint y) {
572  widget *target = NULL;
573  gint rx, ry;
574  listview *lv = (listview *)wid;
575  if (widget_enabled(WIDGET(lv->scrollbar)) &&
576  widget_intersect(WIDGET(lv->scrollbar), x, y)) {
577  rx = x - widget_get_x_pos(WIDGET(lv->scrollbar));
578  ry = y - widget_get_y_pos(WIDGET(lv->scrollbar));
579  target = widget_find_mouse_target(WIDGET(lv->scrollbar), type, rx, ry);
580  }
581 
582  unsigned int max = MIN(lv->cur_elements, lv->req_elements - lv->last_offset);
583  unsigned int i;
584  for (i = 0; i < max && target == NULL; i++) {
585  widget *w = WIDGET(lv->boxes[i].box);
586  if (widget_intersect(w, x, y)) {
587  rx = x - widget_get_x_pos(w);
588  ry = y - widget_get_y_pos(w);
589  target = widget_find_mouse_target(w, type, rx, ry);
590  }
591  }
592 
593  return target;
594 }
595 
598  G_GNUC_UNUSED gint x, G_GNUC_UNUSED gint y,
599  G_GNUC_UNUSED void *user_data) {
600  listview *lv = (listview *)wid;
601  switch (action) {
602  case SCROLL_LEFT:
603  listview_nav_left(lv);
604  break;
605  case SCROLL_RIGHT:
606  listview_nav_right(lv);
607  break;
608  case SCROLL_DOWN:
609  listview_nav_down(lv);
610  break;
611  case SCROLL_UP:
612  listview_nav_up(lv);
613  break;
614  }
616 }
617 
619  widget *wid, MouseBindingListviewElementAction action, G_GNUC_UNUSED gint x,
620  G_GNUC_UNUSED gint y, void *user_data) {
621  listview *lv = (listview *)user_data;
622  unsigned int max = MIN(lv->cur_elements, lv->req_elements - lv->last_offset);
623  unsigned int i;
624  for (i = 0; i < max && WIDGET(lv->boxes[i].box) != wid; i++) {
625  }
626  if (i == max) {
628  }
629 
630  gboolean custom = FALSE;
631  switch (action) {
633  listview_set_selected(lv, lv->last_offset + i);
634  break;
636  custom = TRUE;
637  /* FALLTHRU */
639  listview_set_selected(lv, lv->last_offset + i);
640  lv->mouse_activated(lv, custom, lv->mouse_activated_data);
641  break;
642  }
644 }
645 
647  G_GNUC_UNUSED gint x,
648  G_GNUC_UNUSED gint y) {
649  listview *lv = (listview *)wid->parent;
650  unsigned int max = MIN(lv->cur_elements, lv->req_elements - lv->last_offset);
651  unsigned int i;
652  for (i = 0; i < max && WIDGET(lv->boxes[i].box) != wid; i++) {
653  }
654  if (i < max && i != listview_get_selected(lv)) {
655  listview_set_selected(lv, lv->last_offset + i);
656  }
657  return TRUE;
658 }
659 
660 listview *listview_create(widget *parent, const char *name,
661  listview_update_callback cb, void *udata,
662  unsigned int eh, gboolean reverse) {
663  listview *lv = g_malloc0(sizeof(listview));
664  widget_init(WIDGET(lv), parent, WIDGET_TYPE_LISTVIEW, name);
665  lv->listview_name = g_strdup(name);
666  lv->widget.free = listview_free;
668  lv->widget.draw = _listview_draw;
672  lv->eh = eh;
673 
674  lv->emode = PANGO_ELLIPSIZE_END;
675  lv->scrollbar = scrollbar_create(WIDGET(lv), "scrollbar");
676  // Calculate height of an element.
677  //
678  _listview_row row;
679  listview_create_row(lv, &row);
680  // FIXME: hack to scale hight correctly.
681  if (lv->eh > 1 && row.textbox) {
682  char buff[lv->eh * 2 + 1];
683  memset(buff, '\0', lv->eh * 2 + 1);
684  for (unsigned int i = 0; i < (lv->eh - 1); i++) {
685  buff[i * 2] = 'a';
686  buff[i * 2 + 1] = '\n';
687  };
688  textbox_text(row.textbox, buff);
689  }
691  widget_free(WIDGET(row.box));
692 
693  lv->callback = cb;
694  lv->udata = udata;
695 
696  // Some settings.
697  lv->spacing = rofi_theme_get_distance(WIDGET(lv), "spacing", DEFAULT_SPACING);
698  lv->menu_columns =
700  lv->fixed_num_lines = rofi_theme_get_boolean(WIDGET(lv), "fixed-height",
702  lv->dynamic = rofi_theme_get_boolean(WIDGET(lv), "dynamic", TRUE);
703  lv->reverse = rofi_theme_get_boolean(WIDGET(lv), "reverse", reverse);
704  lv->cycle = rofi_theme_get_boolean(WIDGET(lv), "cycle", config.cycle);
705  lv->fixed_columns =
706  rofi_theme_get_boolean(WIDGET(lv), "fixed-columns", FALSE);
707 
708  lv->type = rofi_theme_get_orientation(WIDGET(lv), "layout",
710  if (lv->type == LISTVIEW) {
712  lv, rofi_theme_get_boolean(WIDGET(lv), "scrollbar", FALSE));
713  } else {
714  listview_set_show_scrollbar(lv, FALSE);
715  }
716  return lv;
717 }
718 
723 static void listview_nav_up_int(listview *lv) {
724  if (lv == NULL) {
725  return;
726  }
727  if (lv->req_elements == 0 || (lv->selected == 0 && !lv->cycle)) {
728  return;
729  }
730  if (lv->selected == 0) {
731  lv->selected = lv->req_elements;
732  }
733  lv->selected--;
736 }
737 static void listview_nav_down_int(listview *lv) {
738  if (lv == NULL) {
739  return;
740  }
741  if (lv->req_elements == 0 ||
742  (lv->selected == (lv->req_elements - 1) && !lv->cycle)) {
743  return;
744  }
745  lv->selected = lv->selected < lv->req_elements - 1
746  ? MIN(lv->req_elements - 1, lv->selected + 1)
747  : 0;
750 }
751 
753  if (lv == NULL) {
754  return;
755  }
756  if (lv->reverse) {
758  } else {
760  }
761 }
763  if (lv == NULL) {
764  return;
765  }
766  if (lv->reverse) {
768  } else {
770  }
771 }
772 
774  if (lv == NULL) {
775  return;
776  }
777  if (lv->type == BARVIEW) {
779  return;
780  }
781  if (lv->selected >= lv->max_rows) {
782  lv->selected -= lv->max_rows;
784  }
785 }
787  if (lv == NULL) {
788  return;
789  }
790  if (lv->max_rows == 0) {
791  return;
792  }
793  if (lv->type == BARVIEW) {
795  return;
796  }
797  if ((lv->selected + lv->max_rows) < lv->req_elements) {
798  lv->selected += lv->max_rows;
800  } else if (lv->selected < (lv->req_elements - 1)) {
801  // We do not want to move to last item, UNLESS the last column is only
802  // partially filled, then we still want to move column and select last
803  // entry. First check the column we are currently in.
804  int col = lv->selected / lv->max_rows;
805  // Check total number of columns.
806  int ncol = lv->req_elements / lv->max_rows;
807  // If there is an extra column, move.
808  if (col != ncol) {
809  lv->selected = lv->req_elements - 1;
811  }
812  }
813 }
814 
816  if (lv == NULL) {
817  return;
818  }
819  if (lv->type == BARVIEW) {
820  if (lv->last_offset == 0) {
821  lv->selected = 0;
822  } else {
823  lv->selected = lv->last_offset - 1;
824  }
827  return;
828  }
829 
830  if (lv->selected < lv->max_elements) {
831  lv->selected = 0;
832  } else {
833  lv->selected -= (lv->max_elements);
834  }
836 }
838  if (lv == NULL) {
839  return;
840  }
841  if (lv->req_elements == 0) {
842  return;
843  }
844  if (lv->type == BARVIEW) {
845  unsigned int new = lv->last_offset + lv->barview.cur_visible;
846  lv->selected = MIN(new, lv->req_elements - 1);
848 
850  return;
851  }
852  lv->selected += (lv->max_elements);
853  if (lv->selected >= lv->req_elements) {
854  lv->selected = lv->req_elements - 1;
855  }
857 }
858 
860  if (lv == NULL) {
861  return;
862  }
863  if (lv->reverse) {
865  } else {
867  }
868 }
870  if (lv == NULL) {
871  return;
872  }
873  if (lv->reverse) {
875  } else {
877  }
878 }
879 
881  listview *lv = (listview *)wid;
882  if (lv == NULL || lv->widget.enabled == FALSE) {
883  return 0;
884  }
886  int h = lv->menu_lines;
887  if (!(lv->fixed_num_lines)) {
888  if (lv->dynamic) {
889  h = MIN(lv->menu_lines, lv->req_elements);
890  } else {
891  h = MIN(lv->menu_lines, lv->max_displayed_lines);
892  }
893  }
894  if (lv->type == BARVIEW) {
895  h = MIN(h, 1);
896  }
897  if (h == 0) {
898  if (lv->dynamic && !lv->fixed_num_lines) {
899  // Hide widget fully.
900  return 0;
901  }
903  }
904  int height = widget_padding_get_padding_height(WIDGET(lv));
905  height += h * (lv->element_height + spacing) - spacing;
906  return height;
907 }
908 
909 void listview_set_show_scrollbar(listview *lv, gboolean enabled) {
910  if (lv) {
911  if (enabled) {
913  } else {
915  }
917  }
918 }
919 
921  if (lv) {
922  lv->scroll_type = type;
923  }
924 }
925 
928  void *udata) {
929  if (lv) {
930  lv->mouse_activated = cb;
931  lv->mouse_activated_data = udata;
932  }
933 }
934 void listview_set_multi_select(listview *lv, gboolean enable) {
935  if (lv) {
936  lv->multi_select = enable;
937  }
938 }
939 void listview_set_num_lines(listview *lv, unsigned int num_lines) {
940  if (lv) {
941  lv->menu_lines = num_lines;
942  }
943 }
944 
945 unsigned int listview_get_num_lines(listview *lv) {
946  if (lv) {
947  return lv->menu_lines;
948  }
949  return 0;
950 }
951 void listview_set_max_lines(listview *lv, unsigned int max_lines) {
952  if (lv) {
953  lv->max_displayed_lines = max_lines;
954  }
955 }
956 
958  if (lv) {
959  return lv->fixed_num_lines;
960  }
961  return FALSE;
962 }
964  if (lv) {
965  lv->fixed_num_lines = TRUE;
966  }
967 }
968 
970  if (lv) {
971  lv->emode = PANGO_ELLIPSIZE_START;
972  for (unsigned int i = 0; i < lv->cur_elements; i++) {
974  }
975  }
976 }
977 
979  if (lv) {
980  PangoEllipsizeMode mode = lv->emode;
981  if (mode == PANGO_ELLIPSIZE_START) {
982  mode = PANGO_ELLIPSIZE_MIDDLE;
983  } else if (mode == PANGO_ELLIPSIZE_MIDDLE) {
984  mode = PANGO_ELLIPSIZE_END;
985  } else if (mode == PANGO_ELLIPSIZE_END) {
986  mode = PANGO_ELLIPSIZE_START;
987  }
988  lv->emode = mode;
989  for (unsigned int i = 0; i < lv->cur_elements; i++) {
990  textbox_set_ellipsize(lv->boxes[i].textbox, mode);
991  }
992  }
993 }
MouseBindingListviewElementAction
Definition: keyb.h:157
MouseBindingListviewAction
Definition: keyb.h:147
@ ACCEPT_HOVERED_ENTRY
Definition: keyb.h:159
@ ACCEPT_HOVERED_CUSTOM
Definition: keyb.h:160
@ SELECT_HOVERED_ENTRY
Definition: keyb.h:158
@ SCROLL_LEFT
Definition: keyb.h:148
@ SCROLL_DOWN
Definition: keyb.h:150
@ SCROLL_RIGHT
Definition: keyb.h:149
@ SCROLL_UP
Definition: keyb.h:151
void scrollbar_set_max_value(scrollbar *sb, unsigned int max)
Definition: scrollbar.c:131
scrollbar * scrollbar_create(widget *parent, const char *name)
Definition: scrollbar.c:102
void scrollbar_set_handle(scrollbar *sb, unsigned int pos)
Definition: scrollbar.c:137
void scrollbar_set_handle_length(scrollbar *sb, unsigned int pos_length)
Definition: scrollbar.c:143
#define TICK_N(a)
Definition: timings.h:69
TextboxFlags
Definition: textbox.h:89
TextBoxFontType
Definition: textbox.h:101
void textbox_set_ellipsize(textbox *tb, PangoEllipsizeMode mode)
Definition: textbox.c:927
textbox * textbox_create(widget *parent, WidgetType type, const char *name, TextboxFlags flags, TextBoxFontType tbft, const char *text, double xalign, double yalign)
Definition: textbox.c:158
void textbox_text(textbox *tb, const char *text)
Definition: textbox.c:311
@ TB_INDICATOR
Definition: textbox.h:96
@ TB_AUTOHEIGHT
Definition: textbox.h:90
@ URGENT
Definition: textbox.h:105
@ ACTIVE
Definition: textbox.h:107
@ HIGHLIGHT
Definition: textbox.h:116
@ NORMAL
Definition: textbox.h:103
@ STATE_MASK
Definition: textbox.h:120
@ ALT
Definition: textbox.h:114
@ FMOD_MASK
Definition: textbox.h:118
void box_add(box *box, widget *child, gboolean expand)
Definition: box.c:285
box * box_create(widget *parent, const char *name, RofiOrientation type)
Definition: box.c:345
icon * icon_create(widget *parent, const char *name)
Definition: icon.c:151
void listview_nav_page_next(listview *lv)
Definition: listview.c:869
void listview_set_fixed_num_lines(listview *lv)
Definition: listview.c:963
void listview_set_num_lines(listview *lv, unsigned int num_lines)
Definition: listview.c:939
void listview_set_show_scrollbar(listview *lv, gboolean enabled)
Definition: listview.c:909
void listview_set_num_elements(listview *lv, unsigned int rows)
Definition: listview.c:516
listview * listview_create(widget *parent, const char *name, listview_update_callback cb, void *udata, unsigned int eh, gboolean reverse)
Definition: listview.c:660
void listview_nav_right(listview *lv)
Definition: listview.c:786
void listview_set_mouse_activated_cb(listview *lv, listview_mouse_activated_cb cb, void *udata)
Definition: listview.c:926
void listview_toggle_ellipsizing(listview *lv)
Definition: listview.c:978
void listview_set_selected(listview *lv, unsigned int selected)
Definition: listview.c:537
void listview_set_max_lines(listview *lv, unsigned int max_lines)
Definition: listview.c:951
void listview_nav_left(listview *lv)
Definition: listview.c:773
void listview_set_scroll_type(listview *lv, ScrollType type)
Definition: listview.c:920
unsigned int listview_get_num_lines(listview *lv)
Definition: listview.c:945
gboolean listview_get_fixed_num_lines(listview *lv)
Definition: listview.c:957
void listview_set_ellipsize_start(listview *lv)
Definition: listview.c:969
unsigned int listview_get_selected(listview *lv)
Definition: listview.c:530
ScrollType
Definition: listview.h:49
void listview_set_multi_select(listview *lv, gboolean enable)
Definition: listview.c:934
void(* listview_mouse_activated_cb)(listview *, gboolean, void *)
Definition: listview.h:73
void(* listview_update_callback)(textbox *tb, icon *ico, unsigned int entry, void *udata, TextBoxFontType *type, gboolean full)
Definition: listview.h:66
void listview_nav_up(listview *lv)
Definition: listview.c:752
void listview_nav_page_prev(listview *lv)
Definition: listview.c:859
void listview_nav_down(listview *lv)
Definition: listview.c:762
@ LISTVIEW_SCROLL_CONTINIOUS
Definition: listview.h:53
void widget_queue_redraw(widget *wid)
Definition: widget.c:509
gboolean widget_enabled(widget *widget)
Definition: widget.c:138
int widget_get_width(widget *widget)
Definition: widget.c:468
int widget_get_desired_height(widget *wid)
Definition: widget.c:649
void widget_free(widget *wid)
Definition: widget.c:447
static void widget_disable(widget *widget)
Definition: widget.h:178
int widget_intersect(const widget *widget, int x, int y)
Definition: widget.c:90
void widget_resize(widget *widget, short w, short h)
Definition: widget.c:102
int widget_get_desired_width(widget *wid)
Definition: widget.c:658
WidgetType
Definition: widget.h:56
static void widget_enable(widget *widget)
Definition: widget.h:186
void widget_move(widget *widget, short x, short y)
Definition: widget.c:117
int widget_get_y_pos(widget *widget)
Definition: widget.c:483
void widget_draw(widget *widget, cairo_t *d)
Definition: widget.c:157
#define WIDGET(a)
Definition: widget.h:119
WidgetTriggerActionResult
Definition: widget.h:76
widget * widget_find_mouse_target(widget *wid, WidgetType type, gint x, gint y)
Definition: widget.c:532
void widget_set_type(widget *widget, WidgetType type)
Definition: widget.c:124
void widget_set_trigger_action_handler(widget *wid, widget_trigger_action_cb cb, void *cb_data)
Definition: widget.c:562
int widget_get_x_pos(widget *widget)
Definition: widget.c:477
@ WIDGET_TYPE_LISTVIEW_ELEMENT
Definition: widget.h:62
@ WIDGET_TYPE_TEXTBOX_TEXT
Definition: widget.h:70
@ WIDGET_TYPE_LISTVIEW
Definition: widget.h:60
@ WIDGET_TRIGGER_ACTION_RESULT_HANDLED
Definition: widget.h:80
@ WIDGET_TRIGGER_ACTION_RESULT_IGNORED
Definition: widget.h:78
MoveDirection
Definition: listview.c:57
@ RIGHT_TO_LEFT
Definition: listview.c:57
@ LEFT_TO_RIGHT
Definition: listview.c:57
static unsigned int scroll_per_page(listview *lv)
Definition: listview.c:236
static void listview_nav_up_int(listview *lv)
Definition: listview.c:723
static widget * listview_find_mouse_target(widget *wid, WidgetType type, gint x, gint y)
Definition: listview.c:570
static unsigned int scroll_per_page_barview(listview *lv)
Definition: listview.c:222
#define BARVIEW
Definition: listview.c:51
const char *const listview_theme_prop_names[][3]
Definition: listview.c:129
static WidgetTriggerActionResult listview_element_trigger_action(widget *wid, MouseBindingListviewElementAction action, gint x, gint y, void *user_data)
static gboolean listview_element_motion_notify(widget *wid, gint x, gint y)
static void listview_nav_page_next_int(listview *lv)
Definition: listview.c:837
static void listview_resize(widget *wid, short w, short h)
Definition: listview.c:545
static void _listview_draw(widget *wid, cairo_t *draw)
Definition: listview.c:472
static void listview_nav_page_prev_int(listview *lv)
Definition: listview.c:815
static WidgetTriggerActionResult listview_trigger_action(widget *wid, MouseBindingListviewAction action, G_GNUC_UNUSED gint x, G_GNUC_UNUSED gint y, G_GNUC_UNUSED void *user_data)
Definition: listview.c:597
static void listview_recompute_elements(listview *lv)
Definition: listview.c:483
#define DEFAULT_SPACING
Definition: listview.c:43
static void listview_add_widget(listview *lv, _listview_row *row, widget *wid, const char *label)
Definition: listview.c:160
static int listview_get_desired_height(widget *wid)
Definition: listview.c:880
static void barview_draw(widget *wid, cairo_t *draw)
Definition: listview.c:300
static void listview_free(widget *wid)
Definition: listview.c:211
static void listview_nav_down_int(listview *lv)
Definition: listview.c:737
#define LISTVIEW
Definition: listview.c:49
static void listview_create_row(listview *lv, _listview_row *row)
Definition: listview.c:187
static void listview_draw(widget *wid, cairo_t *draw)
Definition: listview.c:378
static unsigned int scroll_continious(listview *lv)
Definition: listview.c:259
static void update_element(listview *lv, unsigned int tb, unsigned int index, gboolean full)
Definition: listview.c:279
static void listview_set_state(_listview_row r, TextBoxFontType tbft)
Definition: listview.c:138
RofiOrientation
Definition: rofi-types.h:134
@ ROFI_ORIENTATION_HORIZONTAL
Definition: rofi-types.h:136
@ ROFI_ORIENTATION_VERTICAL
Definition: rofi-types.h:135
Settings config
#define DEFAULT_MENU_COLUMNS
Definition: settings.h:183
unsigned int fixed_num_lines
Definition: settings.h:90
gboolean show_icons
Definition: settings.h:62
unsigned int cycle
Definition: settings.h:116
Definition: box.c:40
Definition: icon.c:39
box * box
Definition: listview.c:60
textbox * index
Definition: listview.c:62
icon * icon
Definition: listview.c:63
textbox * textbox
Definition: listview.c:61
unsigned int cur_columns
Definition: listview.c:86
void * mouse_activated_data
Definition: listview.c:113
gboolean cycle
Definition: listview.c:98
unsigned int rchanged
Definition: listview.c:73
unsigned int max_rows
Definition: listview.c:81
unsigned int cur_page
Definition: listview.c:76
unsigned int menu_columns
Definition: listview.c:93
unsigned int max_displayed_lines
Definition: listview.c:92
widget widget
Definition: listview.c:67
void * udata
Definition: listview.c:107
PangoEllipsizeMode emode
Definition: listview.c:117
unsigned int cur_visible
Definition: listview.c:121
listview_update_callback callback
Definition: listview.c:106
listview_mouse_activated_cb mouse_activated
Definition: listview.c:112
unsigned int req_elements
Definition: listview.c:87
gboolean multi_select
Definition: listview.c:99
xcb_timestamp_t last_click
Definition: listview.c:111
unsigned int element_height
Definition: listview.c:80
unsigned int last_offset
Definition: listview.c:77
unsigned int cur_elements
Definition: listview.c:88
unsigned int dynamic
Definition: listview.c:95
MoveDirection direction
Definition: listview.c:120
char * listview_name
Definition: listview.c:115
scrollbar * scrollbar
Definition: listview.c:104
unsigned int max_elements
Definition: listview.c:82
unsigned int fixed_num_lines
Definition: listview.c:94
unsigned int reverse
Definition: listview.c:97
RofiDistance spacing
Definition: listview.c:90
gboolean fixed_columns
Definition: listview.c:85
RofiOrientation type
Definition: listview.c:69
unsigned int selected
Definition: listview.c:78
unsigned int eh
Definition: listview.c:96
ScrollType scroll_type
Definition: listview.c:101
struct _listview::@3 barview
gboolean scrollbar_scroll
Definition: listview.c:109
_listview_row * boxes
Definition: listview.c:103
unsigned int menu_lines
Definition: listview.c:91
void(* free)(struct _widget *widget)
widget_find_mouse_target_cb find_mouse_target
gboolean enabled
widget_trigger_action_cb trigger_action
int(* get_desired_height)(struct _widget *)
struct _widget * parent
void(* draw)(struct _widget *widget, cairo_t *draw)
gboolean(* motion_notify)(struct _widget *, gint x, gint y)
void(* resize)(struct _widget *, short, short)
RofiDistance rofi_theme_get_distance(const widget *widget, const char *property, int def)
Definition: theme.c:830
int rofi_theme_get_boolean(const widget *widget, const char *property, int def)
Definition: theme.c:856
int distance_get_pixel(RofiDistance d, RofiOrientation ori)
Definition: theme.c:1282
RofiOrientation rofi_theme_get_orientation(const widget *widget, const char *property, RofiOrientation def)
Definition: theme.c:884
int rofi_theme_get_integer(const widget *widget, const char *property, int def)
Definition: theme.c:793
GList * rofi_theme_get_list(const widget *widget, const char *property, const char *defaults)
Definition: theme.c:1176
MenuFlags flags
Definition: view.c:107
void widget_init(widget *wid, widget *parent, WidgetType type, const char *name)
Definition: widget.c:44
void widget_set_state(widget *widget, const char *state)
Definition: widget.c:72
int widget_padding_get_padding_width(const widget *wid)
Definition: widget.c:642
int widget_padding_get_left(const widget *wid)
Definition: widget.c:581
int widget_padding_get_right(const widget *wid)
Definition: widget.c:591
int widget_padding_get_padding_height(const widget *wid)
Definition: widget.c:636
int widget_padding_get_top(const widget *wid)
Definition: widget.c:603
int widget_padding_get_bottom(const widget *wid)
Definition: widget.c:613