rofi  1.7.5
listview.c
Go to the documentation of this file.
1 /*
2  * rofi
3  *
4  * MIT/X11 License
5  * Copyright © 2013-2022 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 
75  // The direction we pack the widgets.
77  // Administration
78 
79  unsigned int cur_page;
80  unsigned int last_offset;
81  unsigned int selected;
82 
83  unsigned int element_height;
84  unsigned int max_rows;
85  unsigned int max_elements;
86 
87  //
88  gboolean fixed_columns;
89  unsigned int cur_columns;
90  unsigned int req_elements;
91  unsigned int cur_elements;
92 
94  unsigned int menu_lines;
95  unsigned int max_displayed_lines;
96  unsigned int menu_columns;
97  unsigned int fixed_num_lines;
98  unsigned int dynamic;
99  unsigned int eh;
100  unsigned int reverse;
101  gboolean require_input;
102  gboolean filtered;
103 
104  gboolean cycle;
105 
107 
110 
112  void *udata;
113 
115  void *sc_udata;
116 
118 
119  xcb_timestamp_t last_click;
122 
124 
125  PangoEllipsizeMode emode;
127  struct {
129  unsigned int cur_visible;
131 };
137 const char *const listview_theme_prop_names[][3] = {
139  {"normal.normal", "selected.normal", "alternate.normal"},
141  {"normal.urgent", "selected.urgent", "alternate.urgent"},
143  {"normal.active", "selected.active", "alternate.active"},
144 };
145 
147  widget *w = WIDGET(r.box);
148  TextBoxFontType t = tbft & STATE_MASK;
149  if (w == NULL) {
150  return;
151  }
152  // ACTIVE has priority over URGENT if both set.
153  if (t == (URGENT | ACTIVE)) {
154  t = ACTIVE;
155  }
156  switch ((tbft & FMOD_MASK)) {
157  case HIGHLIGHT:
159  break;
160  case ALT:
162  break;
163  default:
165  break;
166  }
167 }
168 static void listview_add_widget(listview *lv, _listview_row *row, widget *wid,
169  const char *label) {
170  TextboxFlags flags = 0;
171  if (strcasecmp(label, "element-icon") == 0) {
172  row->icon = icon_create(WIDGET(wid), "element-icon");
173  box_add((box *)wid, WIDGET(row->icon), FALSE);
174  } else if (strcasecmp(label, "element-text") == 0) {
175  row->textbox =
176  textbox_create(WIDGET(wid), WIDGET_TYPE_TEXTBOX_TEXT, "element-text",
177  TB_AUTOHEIGHT | flags, NORMAL, "DDD", 0, 0);
178  box_add((box *)wid, WIDGET(row->textbox), TRUE);
179  } else if (strcasecmp(label, "element-index") == 0) {
180  row->index =
181  textbox_create(WIDGET(wid), WIDGET_TYPE_TEXTBOX_TEXT, "element-index",
182  TB_AUTOHEIGHT, NORMAL, " ", 0, 0);
183  box_add((box *)wid, WIDGET(row->index), FALSE);
184  } else {
185  widget *wid2 = (widget *)box_create(wid, label, ROFI_ORIENTATION_VERTICAL);
186  box_add((box *)wid, WIDGET(wid2), TRUE);
187  GList *list = rofi_theme_get_list_strings(
188  WIDGET(wid2),
189  "children"); // rofi_theme_get_list(WIDGET(wid2), "children", "");
190  for (GList *iter = g_list_first(list); iter != NULL;
191  iter = g_list_next(iter)) {
192  listview_add_widget(lv, row, wid2, (const char *)iter->data);
193  }
194  }
195 }
196 
198  row->box = box_create(WIDGET(lv), "element", ROFI_ORIENTATION_HORIZONTAL);
200  GList *list = NULL;
201  list = rofi_theme_get_list_strings(WIDGET(row->box), "children");
202  if (list == NULL) {
203  if (config.show_icons) {
204  list = g_list_append(list, g_strdup("element-icon"));
205  list = g_list_append(list, g_strdup("element-text"));
206  } else {
207  list = g_list_append(list, g_strdup("element-text"));
208  }
209  }
210 
211  row->textbox = NULL;
212  row->icon = NULL;
213  row->index = NULL;
214 
215  for (GList *iter = g_list_first(list); iter != NULL;
216  iter = g_list_next(iter)) {
217  listview_add_widget(lv, row, WIDGET(row->box), (const char *)iter->data);
218  }
219  g_list_free_full(list, g_free);
220 }
221 
222 static int listview_get_desired_height(widget *wid, const int width);
223 
224 static void listview_free(widget *wid) {
225  listview *lv = (listview *)wid;
226  for (unsigned int i = 0; i < lv->cur_elements; i++) {
227  widget_free(WIDGET(lv->boxes[i].box));
228  }
229  g_free(lv->boxes);
230 
231  g_free(lv->listview_name);
233  g_free(lv);
234 }
235 static unsigned int scroll_per_page_barview(listview *lv) {
236  unsigned int offset = lv->last_offset;
237 
238  // selected row is always visible.
239  // If selected is visible do not scroll.
240  if (lv->selected < lv->last_offset) {
241  offset = lv->selected;
242  lv->rchanged = TRUE;
243  } else if (lv->selected >= (lv->last_offset + lv->barview.cur_visible)) {
244  offset = lv->selected;
245  lv->rchanged = TRUE;
246  }
247  return offset;
248 }
249 static unsigned int scroll_per_page(listview *lv) {
250  int offset = 0;
251 
252  // selected row is always visible.
253  // If selected is visible do not scroll.
254  if (((lv->selected - (lv->last_offset)) < (lv->max_elements)) &&
255  (lv->selected >= (lv->last_offset))) {
256  offset = lv->last_offset;
257  } else {
258  // Do paginating
259  unsigned int page =
260  (lv->max_elements > 0) ? (lv->selected / lv->max_elements) : 0;
261  offset = page * lv->max_elements;
262  if (page != lv->cur_page) {
263  lv->cur_page = page;
264  lv->rchanged = TRUE;
265  }
266  // Set the position
267  // scrollbar_set_handle ( lv->scrollbar, page * lv->max_elements );
268  }
269  return offset;
270 }
271 
272 static unsigned int scroll_continious(listview *lv) {
273  unsigned int middle = (lv->max_rows - ((lv->max_rows & 1) == 0)) / 2;
274  unsigned int offset = 0;
275  if (lv->selected > middle) {
276  if (lv->selected < (lv->req_elements - (lv->max_rows - middle))) {
277  offset = lv->selected - middle;
278  }
279  // Don't go below zero.
280  else if (lv->req_elements > lv->max_rows) {
281  offset = lv->req_elements - lv->max_rows;
282  }
283  }
284  if (offset != lv->cur_page) {
285  // scrollbar_set_handle ( lv->scrollbar, offset );
286  lv->cur_page = offset;
287  lv->rchanged = TRUE;
288  }
289  return offset;
290 }
291 
292 static void update_element(listview *lv, unsigned int tb, unsigned int index,
293  gboolean full) {
294  // Select drawing mode
295  TextBoxFontType type = (index & 1) == 0 ? NORMAL : ALT;
296  type = (index) == lv->selected ? HIGHLIGHT : type;
297 
298  if (lv->boxes[tb].index) {
299  if (index < 10) {
300  char str[2] = {((index + 1) % 10) + '0', '\0'};
301  textbox_text(lv->boxes[tb].index, str);
302  } else {
303  textbox_text(lv->boxes[tb].index, " ");
304  }
305  }
306  if (lv->callback) {
307  lv->callback(lv->boxes[tb].textbox, lv->boxes[tb].icon, index, lv->udata,
308  &type, full);
309  listview_set_state(lv->boxes[tb], type);
310  }
311 }
312 
313 static void barview_draw(widget *wid, cairo_t *draw) {
314  unsigned int offset = 0;
315  listview *lv = (listview *)wid;
316  offset = scroll_per_page_barview(lv);
317  lv->last_offset = offset;
318  int spacing_hori =
320 
321  int left_offset = widget_padding_get_left(wid);
322  int right_offset = lv->widget.w - widget_padding_get_right(wid);
323  int top_offset = widget_padding_get_top(wid);
324  if (lv->cur_elements > 0) {
325  // Set new x/y position.
326  unsigned int max = MIN(lv->cur_elements, lv->req_elements - offset);
327  if (lv->rchanged) {
328  int first = TRUE;
329  int width = lv->widget.w;
330  lv->barview.cur_visible = 0;
331  width -= widget_padding_get_padding_width(wid);
332  if (lv->barview.direction == LEFT_TO_RIGHT) {
333  for (unsigned int i = 0; i < max && width > 0; i++) {
334  update_element(lv, i, i + offset, TRUE);
335  int twidth = widget_get_desired_width(WIDGET(lv->boxes[i].box),
336  lv->element_height);
337  if (twidth >= width) {
338  if (!first) {
339  break;
340  }
341  twidth = width;
342  }
343  widget_move(WIDGET(lv->boxes[i].box), left_offset, top_offset);
344  widget_resize(WIDGET(lv->boxes[i].box), twidth, lv->element_height);
345 
346  widget_draw(WIDGET(lv->boxes[i].box), draw);
347  width -= twidth + spacing_hori;
348  left_offset += twidth + spacing_hori;
349  first = FALSE;
350  lv->barview.cur_visible++;
351  }
352  } else {
353  for (unsigned int i = 0;
354  i < lv->cur_elements && width > 0 && i <= offset; i++) {
355  update_element(lv, i, offset - i, TRUE);
356  int twidth = widget_get_desired_width(WIDGET(lv->boxes[i].box),
357  lv->element_height);
358  if (twidth >= width) {
359  if (!first) {
360  break;
361  }
362  twidth = width;
363  }
364  right_offset -= twidth;
365  widget_move(WIDGET(lv->boxes[i].box), right_offset, top_offset);
366  widget_resize(WIDGET(lv->boxes[i].box), twidth, lv->element_height);
367 
368  widget_draw(WIDGET(lv->boxes[i].box), draw);
369  width -= twidth + spacing_hori;
370  right_offset -= spacing_hori;
371  first = FALSE;
372  lv->barview.cur_visible++;
373  }
374  offset -= lv->barview.cur_visible - 1;
375  lv->last_offset = offset;
376  for (unsigned int i = 0; i < (lv->barview.cur_visible / 2); i++) {
377  _listview_row temp = lv->boxes[i];
378  int sw = lv->barview.cur_visible - i - 1;
379  lv->boxes[i] = lv->boxes[sw];
380  lv->boxes[sw] = temp;
381  }
382  }
383  lv->rchanged = FALSE;
384  } else {
385  for (unsigned int i = 0; i < lv->barview.cur_visible; i++) {
386  update_element(lv, i, i + offset, TRUE);
387  widget_draw(WIDGET(lv->boxes[i].box), draw);
388  }
389  }
390  }
391 }
392 
393 static void listview_draw(widget *wid, cairo_t *draw) {
394  unsigned int offset = 0;
395  listview *lv = (listview *)wid;
397  offset = scroll_continious(lv);
398  } else {
399  offset = scroll_per_page(lv);
400  }
401  // Set these all together to make sure they update consistently.
404  if (lv->reverse) {
406  } else {
408  }
409  lv->last_offset = offset;
410  int spacing_vert = distance_get_pixel(lv->spacing, ROFI_ORIENTATION_VERTICAL);
411  int spacing_hori =
413 
414  int left_offset = widget_padding_get_left(wid);
415  int top_offset = widget_padding_get_top(wid);
416  /*
417  if ( lv->scrollbar->widget.index == 0 ) {
418  left_offset += spacing_hori + lv->scrollbar->widget.w;
419  }
420  */
421  if (lv->cur_elements > 0 && lv->max_rows > 0) {
422  // Set new x/y position.
423  unsigned int max = MIN(lv->cur_elements, lv->req_elements - offset);
424  if (lv->rchanged) {
425  unsigned int width = lv->widget.w;
426  width -= widget_padding_get_padding_width(wid);
427  if (widget_enabled(WIDGET(lv->scrollbar))) {
428  width -= spacing_hori;
429  width -= widget_get_width(WIDGET(lv->scrollbar));
430  }
431  unsigned int element_width =
432  (width - spacing_hori * (lv->cur_columns - 1)) / lv->cur_columns;
433 
434  int d = width - (element_width + spacing_hori) * (lv->cur_columns - 1) -
435  element_width;
436  if (lv->cur_columns > 1) {
437  int diff = d / (lv->cur_columns - 1);
438  if (diff >= 1) {
439  spacing_hori += 1;
440  d -= lv->cur_columns - 1;
441  }
442  }
443  for (unsigned int i = 0; i < max; i++) {
445  unsigned int ex = left_offset + ((i) % lv->cur_columns) *
446  (element_width + spacing_hori);
447  unsigned int ey = 0;
448  if (lv->reverse) {
449  ey = wid->h -
451  ((i) / lv->cur_columns) *
452  (lv->element_height + spacing_vert)) -
453  lv->element_height;
454 
455  if ((i) / lv->cur_columns == (lv->cur_columns - 1)) {
456  ex += d;
457  }
458  } else {
459  ey = top_offset +
460  ((i) / lv->cur_columns) * (lv->element_height + spacing_vert);
461 
462  if ((i) / lv->cur_columns == (lv->cur_columns - 1)) {
463  ex += d;
464  }
465  }
466  widget_move(WIDGET(lv->boxes[i].box), ex, ey);
467  widget_resize(WIDGET(lv->boxes[i].box), element_width,
468  lv->element_height);
469 
470  } else {
471  unsigned int ex = left_offset + ((i) / lv->max_rows) *
472  (element_width + spacing_hori);
473 
474  if ((i) / lv->max_rows == (lv->cur_columns - 1)) {
475  ex += d;
476  }
477  unsigned int ey = 0;
478  if (lv->reverse) {
479  ey = wid->h -
481  ((i) % lv->max_rows) * (lv->element_height + spacing_vert)) -
482  lv->element_height;
483  } else {
484  ey = top_offset +
485  ((i) % lv->max_rows) * (lv->element_height + spacing_vert);
486  }
487  widget_move(WIDGET(lv->boxes[i].box), ex, ey);
488  widget_resize(WIDGET(lv->boxes[i].box), element_width,
489  lv->element_height);
490  }
491  update_element(lv, i, i + offset, TRUE);
492  widget_draw(WIDGET(lv->boxes[i].box), draw);
493  }
494  lv->rchanged = FALSE;
495  } else {
496  for (unsigned int i = 0; i < max; i++) {
497  update_element(lv, i, i + offset, TRUE);
498  widget_draw(WIDGET(lv->boxes[i].box), draw);
499  }
500  }
501  }
502  widget_draw(WIDGET(lv->scrollbar), draw);
503 }
507  gint x, gint y, void *user_data);
508 static gboolean listview_element_motion_notify(widget *wid, gint x, gint y);
509 
510 static void _listview_draw(widget *wid, cairo_t *draw) {
511  listview *lv = (listview *)wid;
512  if (lv->type == LISTVIEW) {
513  listview_draw(wid, draw);
514  } else {
515  barview_draw(wid, draw);
516  }
517 }
522  unsigned int newne = 0;
523  if (lv->max_rows == 0) {
524  return;
525  }
526  if (!(lv->fixed_columns) && lv->req_elements < lv->max_elements) {
527  newne = lv->req_elements;
529  lv->cur_columns = (lv->req_elements + (lv->max_rows - 1)) / lv->max_rows;
530  } else {
531  lv->cur_columns = lv->menu_columns;
532  if (lv->req_elements < lv->menu_columns) {
533  lv->cur_columns = lv->req_elements;
534  }
535  }
536  } else {
537  newne = MIN(lv->req_elements, lv->max_elements);
538  lv->cur_columns = lv->menu_columns;
539  }
540  for (unsigned int i = newne; i < lv->cur_elements; i++) {
541  widget_free(WIDGET(lv->boxes[i].box));
542  }
543  lv->boxes = g_realloc(lv->boxes, newne * sizeof(_listview_row));
544  if (newne > 0) {
545  for (unsigned int i = lv->cur_elements; i < newne; i++) {
546  listview_create_row(lv, &(lv->boxes[i]));
547  widget *wid = WIDGET(lv->boxes[i].box);
549  lv);
550  if (wid != NULL) {
552  }
553 
555  }
556  }
557  lv->rchanged = TRUE;
558  lv->cur_elements = newne;
559 }
560 
561 void listview_set_num_elements(listview *lv, unsigned int rows) {
562  if (lv == NULL) {
563  return;
564  }
565  TICK_N("listview_set_num_elements");
566  lv->req_elements = rows;
567  if (lv->require_input && !lv->filtered) {
568  lv->req_elements = 0;
569  }
571  TICK_N("Set selected");
573  TICK_N("recompute elements");
575  TICK_N("queue redraw");
576 }
577 
578 unsigned int listview_get_selected(listview *lv) {
579  if (lv != NULL) {
580  return lv->selected;
581  }
582  return 0;
583 }
584 
585 void listview_set_selected(listview *lv, unsigned int selected) {
586  if (lv && lv->req_elements > 0) {
587  lv->selected = MIN(selected, lv->req_elements - 1);
590  lv->sc_callback(lv, lv->selected, lv->sc_udata);
591  } else if (lv->req_elements == 0) {
592  lv->sc_callback(lv, UINT32_MAX, lv->sc_udata);
593  }
594 }
595 
596 static void listview_resize(widget *wid, short w, short h) {
597  listview *lv = (listview *)wid;
598  lv->widget.w = MAX(0, w);
599  lv->widget.h = MAX(0, h);
600  int height = lv->widget.h - widget_padding_get_padding_height(WIDGET(lv));
601  int spacing_vert = distance_get_pixel(lv->spacing, ROFI_ORIENTATION_VERTICAL);
602  lv->max_rows = (spacing_vert + height) / (lv->element_height + spacing_vert);
603  lv->max_elements = lv->max_rows * lv->menu_columns;
604 
609 
611  height);
612 
613  if (lv->type == BARVIEW) {
614  lv->max_elements = lv->menu_lines;
615  }
616 
618  widget_queue_redraw(wid);
619 }
620 
622  gint y) {
623  widget *target = NULL;
624  gint rx, ry;
625  listview *lv = (listview *)wid;
626  if (widget_enabled(WIDGET(lv->scrollbar)) &&
627  widget_intersect(WIDGET(lv->scrollbar), x, y)) {
628  rx = x - widget_get_x_pos(WIDGET(lv->scrollbar));
629  ry = y - widget_get_y_pos(WIDGET(lv->scrollbar));
630  target = widget_find_mouse_target(WIDGET(lv->scrollbar), type, rx, ry);
631  }
632 
633  unsigned int max = MIN(lv->cur_elements, lv->req_elements - lv->last_offset);
634  unsigned int i;
635  for (i = 0; i < max && target == NULL; i++) {
636  widget *w = WIDGET(lv->boxes[i].box);
637  if (widget_intersect(w, x, y)) {
638  rx = x - widget_get_x_pos(w);
639  ry = y - widget_get_y_pos(w);
640  target = widget_find_mouse_target(w, type, rx, ry);
641  }
642  }
643 
644  return target;
645 }
646 
649  G_GNUC_UNUSED gint x, G_GNUC_UNUSED gint y,
650  G_GNUC_UNUSED void *user_data) {
651  listview *lv = (listview *)wid;
652  switch (action) {
653  case SCROLL_LEFT:
654  listview_nav_left(lv);
655  break;
656  case SCROLL_RIGHT:
657  listview_nav_right(lv);
658  break;
659  case SCROLL_DOWN:
660  listview_nav_down(lv);
661  break;
662  case SCROLL_UP:
663  listview_nav_up(lv);
664  break;
665  }
667 }
668 
670  widget *wid, MouseBindingListviewElementAction action, G_GNUC_UNUSED gint x,
671  G_GNUC_UNUSED gint y, void *user_data) {
672  listview *lv = (listview *)user_data;
673  unsigned int max = MIN(lv->cur_elements, lv->req_elements - lv->last_offset);
674  unsigned int i;
675  for (i = 0; i < max && WIDGET(lv->boxes[i].box) != wid; i++) {
676  }
677  if (i == max) {
679  }
680 
681  gboolean custom = FALSE;
682  switch (action) {
684  listview_set_selected(lv, lv->last_offset + i);
685  break;
687  custom = TRUE;
688  /* FALLTHRU */
690  listview_set_selected(lv, lv->last_offset + i);
691  lv->mouse_activated(lv, custom, lv->mouse_activated_data);
692  break;
693  }
695 }
696 
698  G_GNUC_UNUSED gint x,
699  G_GNUC_UNUSED gint y) {
700  listview *lv = (listview *)wid->parent;
701  unsigned int max = MIN(lv->cur_elements, lv->req_elements - lv->last_offset);
702  unsigned int i;
703  for (i = 0; i < max && WIDGET(lv->boxes[i].box) != wid; i++) {
704  }
705  if (i < max && (lv->last_offset + i) != listview_get_selected(lv)) {
706  listview_set_selected(lv, lv->last_offset + i);
707  }
708  return TRUE;
709 }
710 
711 listview *listview_create(widget *parent, const char *name,
712  listview_update_callback cb, void *udata,
713  unsigned int eh, gboolean reverse) {
714  listview *lv = g_malloc0(sizeof(listview));
715  widget_init(WIDGET(lv), parent, WIDGET_TYPE_LISTVIEW, name);
716  lv->listview_name = g_strdup(name);
717  lv->widget.free = listview_free;
719  lv->widget.draw = _listview_draw;
723  lv->eh = eh;
724 
725  lv->emode = PANGO_ELLIPSIZE_END;
726  lv->scrollbar = scrollbar_create(WIDGET(lv), "scrollbar");
727  // Calculate height of an element.
728  //
729  _listview_row row;
730  listview_create_row(lv, &row);
731  // FIXME: hack to scale hight correctly.
732  if (lv->eh > 1 && row.textbox) {
733  char buff[lv->eh * 2 + 1];
734  memset(buff, '\0', lv->eh * 2 + 1);
735  for (unsigned int i = 0; i < (lv->eh - 1); i++) {
736  buff[i * 2] = 'a';
737  buff[i * 2 + 1] = '\n';
738  };
739  textbox_moveresize(row.textbox, 0, 0, 100000000, -1);
740  textbox_text(row.textbox, buff);
741  }
742  // Make textbox very wide.
744  widget_free(WIDGET(row.box));
745 
746  lv->callback = cb;
747  lv->udata = udata;
748 
749  // Some settings.
750  lv->spacing = rofi_theme_get_distance(WIDGET(lv), "spacing", DEFAULT_SPACING);
751  lv->menu_columns =
753  lv->fixed_num_lines = rofi_theme_get_boolean(WIDGET(lv), "fixed-height",
755  lv->dynamic = rofi_theme_get_boolean(WIDGET(lv), "dynamic", TRUE);
756  lv->reverse = rofi_theme_get_boolean(WIDGET(lv), "reverse", reverse);
757  lv->pack_direction =
759  lv->cycle = rofi_theme_get_boolean(WIDGET(lv), "cycle", config.cycle);
760  lv->fixed_columns =
761  rofi_theme_get_boolean(WIDGET(lv), "fixed-columns", FALSE);
762 
763  lv->require_input =
764  rofi_theme_get_boolean(WIDGET(lv), "require-input", FALSE);
765  lv->type = rofi_theme_get_orientation(WIDGET(lv), "layout",
767  if (lv->type == LISTVIEW) {
769  lv, rofi_theme_get_boolean(WIDGET(lv), "scrollbar", FALSE));
770  } else {
771  listview_set_show_scrollbar(lv, FALSE);
772  }
773  return lv;
774 }
775 
780 static void listview_nav_up_int(listview *lv) {
781  if (lv == NULL) {
782  return;
783  }
784  if (lv->req_elements == 0 || (lv->selected == 0 && !lv->cycle)) {
785  return;
786  }
787  if (lv->selected == 0) {
788  lv->selected = lv->req_elements;
789  }
790  lv->selected--;
792 
793  lv->sc_callback(lv, lv->selected, lv->sc_udata);
795 }
796 static void listview_nav_down_int(listview *lv) {
797  if (lv == NULL) {
798  return;
799  }
800  if (lv->req_elements == 0 ||
801  (lv->selected == (lv->req_elements - 1) && !lv->cycle)) {
802  return;
803  }
804  lv->selected = lv->selected < lv->req_elements - 1
805  ? MIN(lv->req_elements - 1, lv->selected + 1)
806  : 0;
808  lv->sc_callback(lv, lv->selected, lv->sc_udata);
810 }
812  if (lv == NULL) {
813  return;
814  }
816 }
818  if (lv == NULL) {
819  return;
820  }
822 }
823 
825  if (lv->selected >= lv->cur_columns) {
826  lv->selected -= lv->cur_columns;
827  lv->sc_callback(lv, lv->selected, lv->sc_udata);
829  }
830 }
832  if ((lv->selected + lv->cur_columns) < lv->req_elements) {
833  lv->selected += lv->cur_columns;
834  lv->sc_callback(lv, lv->selected, lv->sc_udata);
836  }
837 }
838 
840  if (lv == NULL) {
841  return;
842  }
844  if (lv->reverse) {
846  } else {
848  }
849  return;
850  }
851  if (lv->reverse) {
853  } else {
855  }
856 }
858  if (lv == NULL) {
859  return;
860  }
862  if (lv->reverse) {
864  } else {
866  }
867  return;
868  }
869  if (lv->reverse) {
871  } else {
873  }
874 }
875 
877  if (lv == NULL) {
878  return;
879  }
880  if (lv->max_rows == 0) {
881  return;
882  }
885  return;
886  }
887  if (lv->type == BARVIEW) {
889  return;
890  }
891  if (lv->selected >= lv->max_rows) {
892  lv->selected -= lv->max_rows;
893  lv->sc_callback(lv, lv->selected, lv->sc_udata);
895  }
896 }
898  if (lv == NULL) {
899  return;
900  }
901  if (lv->max_rows == 0) {
902  return;
903  }
906  return;
907  }
908  if (lv->type == BARVIEW) {
910  return;
911  }
912  if ((lv->selected + lv->max_rows) < lv->req_elements) {
913  lv->selected += lv->max_rows;
914  lv->sc_callback(lv, lv->selected, lv->sc_udata);
916  } else if (lv->selected < (lv->req_elements - 1)) {
917  // We do not want to move to last item, UNLESS the last column is only
918  // partially filled, then we still want to move column and select last
919  // entry. First check the column we are currently in.
920  int col = lv->selected / lv->max_rows;
921  // Check total number of columns.
922  int ncol = lv->req_elements / lv->max_rows;
923  // If there is an extra column, move.
924  if (col != ncol) {
925  lv->selected = lv->req_elements - 1;
926  lv->sc_callback(lv, lv->selected, lv->sc_udata);
928  }
929  }
930 }
931 
933  if (lv == NULL) {
934  return;
935  }
936  if (lv->type == BARVIEW) {
937  if (lv->last_offset == 0) {
938  lv->selected = 0;
939  } else {
940  lv->selected = lv->last_offset - 1;
941  }
944  return;
945  }
946 
947  if (lv->selected < lv->max_elements) {
948  lv->selected = 0;
949  } else {
950  lv->selected -= (lv->max_elements);
951  }
952  lv->sc_callback(lv, lv->selected, lv->sc_udata);
954 }
956  if (lv == NULL) {
957  return;
958  }
959  if (lv->req_elements == 0) {
960  return;
961  }
962  if (lv->type == BARVIEW) {
963  unsigned int new = lv->last_offset + lv->barview.cur_visible;
964  lv->selected = MIN(new, lv->req_elements - 1);
966 
967  lv->sc_callback(lv, lv->selected, lv->sc_udata);
969  return;
970  }
971  lv->selected += (lv->max_elements);
972  if (lv->selected >= lv->req_elements) {
973  lv->selected = lv->req_elements - 1;
974  }
975  lv->sc_callback(lv, lv->selected, lv->sc_udata);
977 }
978 
980  if (lv == NULL) {
981  return;
982  }
983  if (lv->reverse) {
985  } else {
987  }
988 }
990  if (lv == NULL) {
991  return;
992  }
993  if (lv->reverse) {
995  } else {
997  }
998 }
999 
1001  G_GNUC_UNUSED const int width) {
1002  listview *lv = (listview *)wid;
1003  if (lv == NULL || lv->widget.enabled == FALSE) {
1004  return 0;
1005  }
1007  int h = lv->menu_lines;
1008  if (!(lv->fixed_num_lines)) {
1009  if (lv->dynamic) {
1010  h = MIN(lv->menu_lines, lv->req_elements);
1011  } else {
1012  h = MIN(lv->menu_lines, lv->max_displayed_lines);
1013  }
1014  }
1015  if (lv->type == BARVIEW) {
1016  h = MIN(h, 1);
1017  }
1018  if (h == 0) {
1019  if (lv->dynamic && !lv->fixed_num_lines) {
1020  // Hide widget fully.
1021  return 0;
1022  }
1024  }
1025  int height = widget_padding_get_padding_height(WIDGET(lv));
1026  height += h * (lv->element_height + spacing) - spacing;
1027  return height;
1028 }
1029 
1030 void listview_set_show_scrollbar(listview *lv, gboolean enabled) {
1031  if (lv) {
1032  if (enabled) {
1034  } else {
1036  }
1038  }
1039 }
1040 
1042  if (lv) {
1043  lv->scroll_type = type;
1044  }
1045 }
1046 
1049  void *udata) {
1050  if (lv) {
1051  lv->mouse_activated = cb;
1052  lv->mouse_activated_data = udata;
1053  }
1054 }
1055 void listview_set_num_lines(listview *lv, unsigned int num_lines) {
1056  if (lv) {
1057  lv->menu_lines = num_lines;
1058  }
1059 }
1060 
1061 void listview_set_max_lines(listview *lv, unsigned int max_lines) {
1062  if (lv) {
1063  lv->max_displayed_lines = max_lines;
1064  }
1065 }
1066 
1068  if (lv) {
1069  return lv->fixed_num_lines;
1070  }
1071  return FALSE;
1072 }
1074  if (lv) {
1075  lv->fixed_num_lines = TRUE;
1076  }
1077 }
1078 
1080  if (lv) {
1081  lv->emode = PANGO_ELLIPSIZE_START;
1082  for (unsigned int i = 0; i < lv->cur_elements; i++) {
1084  }
1085  }
1086 }
1087 
1089  if (lv) {
1090  PangoEllipsizeMode mode = lv->emode;
1091  if (mode == PANGO_ELLIPSIZE_START) {
1092  mode = PANGO_ELLIPSIZE_MIDDLE;
1093  } else if (mode == PANGO_ELLIPSIZE_MIDDLE) {
1094  mode = PANGO_ELLIPSIZE_END;
1095  } else if (mode == PANGO_ELLIPSIZE_END) {
1096  mode = PANGO_ELLIPSIZE_START;
1097  }
1098  lv->emode = mode;
1099  for (unsigned int i = 0; i < lv->cur_elements; i++) {
1100  textbox_set_ellipsize(lv->boxes[i].textbox, mode);
1101  }
1102  }
1103 }
1104 
1105 void listview_set_filtered(listview *lv, gboolean filtered) {
1106  if (lv) {
1107  lv->filtered = filtered;
1108  }
1109 }
1110 
1112  listview *lv, listview_selection_changed_callback cb, void *udata) {
1113  lv->sc_callback = cb;
1114  lv->sc_udata = udata;
1115 }
MouseBindingListviewElementAction
Definition: keyb.h:159
MouseBindingListviewAction
Definition: keyb.h:149
@ ACCEPT_HOVERED_ENTRY
Definition: keyb.h:161
@ ACCEPT_HOVERED_CUSTOM
Definition: keyb.h:162
@ SELECT_HOVERED_ENTRY
Definition: keyb.h:160
@ SCROLL_LEFT
Definition: keyb.h:150
@ SCROLL_DOWN
Definition: keyb.h:152
@ SCROLL_RIGHT
Definition: keyb.h:151
@ SCROLL_UP
Definition: keyb.h:153
void scrollbar_set_max_value(scrollbar *sb, unsigned int max)
Definition: scrollbar.c:132
scrollbar * scrollbar_create(widget *parent, const char *name)
Definition: scrollbar.c:103
void scrollbar_set_handle(scrollbar *sb, unsigned int pos)
Definition: scrollbar.c:138
void scrollbar_set_handle_length(scrollbar *sb, unsigned int pos_length)
Definition: scrollbar.c:144
#define TICK_N(a)
Definition: timings.h:69
TextboxFlags
Definition: textbox.h:89
TextBoxFontType
Definition: textbox.h:100
void textbox_set_ellipsize(textbox *tb, PangoEllipsizeMode mode)
Definition: textbox.c:981
textbox * textbox_create(widget *parent, WidgetType type, const char *name, TextboxFlags flags, TextBoxFontType tbft, const char *text, double xalign, double yalign)
Definition: textbox.c:186
void textbox_moveresize(textbox *tb, int x, int y, int w, int h)
Definition: textbox.c:393
void textbox_text(textbox *tb, const char *text)
Definition: textbox.c:358
@ TB_AUTOHEIGHT
Definition: textbox.h:90
@ URGENT
Definition: textbox.h:104
@ ACTIVE
Definition: textbox.h:106
@ HIGHLIGHT
Definition: textbox.h:115
@ NORMAL
Definition: textbox.h:102
@ STATE_MASK
Definition: textbox.h:119
@ ALT
Definition: textbox.h:113
@ FMOD_MASK
Definition: textbox.h:117
void box_add(box *box, widget *child, gboolean expand)
Definition: box.c:286
box * box_create(widget *parent, const char *name, RofiOrientation type)
Definition: box.c:346
icon * icon_create(widget *parent, const char *name)
Definition: icon.c:153
void listview_nav_page_next(listview *lv)
Definition: listview.c:989
void listview_set_fixed_num_lines(listview *lv)
Definition: listview.c:1073
void listview_set_num_lines(listview *lv, unsigned int num_lines)
Definition: listview.c:1055
void listview_set_show_scrollbar(listview *lv, gboolean enabled)
Definition: listview.c:1030
void listview_set_num_elements(listview *lv, unsigned int rows)
Definition: listview.c:561
listview * listview_create(widget *parent, const char *name, listview_update_callback cb, void *udata, unsigned int eh, gboolean reverse)
Definition: listview.c:711
void listview_nav_right(listview *lv)
Definition: listview.c:897
void listview_set_mouse_activated_cb(listview *lv, listview_mouse_activated_cb cb, void *udata)
Definition: listview.c:1047
void listview_toggle_ellipsizing(listview *lv)
Definition: listview.c:1088
void listview_set_selected(listview *lv, unsigned int selected)
Definition: listview.c:585
void listview_set_max_lines(listview *lv, unsigned int max_lines)
Definition: listview.c:1061
void listview_nav_left(listview *lv)
Definition: listview.c:876
void listview_set_scroll_type(listview *lv, ScrollType type)
Definition: listview.c:1041
void(* listview_selection_changed_callback)(listview *lv, unsigned int index, void *udata)
Definition: listview.h:78
gboolean listview_get_fixed_num_lines(listview *lv)
Definition: listview.c:1067
void listview_set_ellipsize_start(listview *lv)
Definition: listview.c:1079
void listview_nav_prev(listview *lv)
Definition: listview.c:817
unsigned int listview_get_selected(listview *lv)
Definition: listview.c:578
ScrollType
Definition: listview.h:49
void(* listview_mouse_activated_cb)(listview *, gboolean, void *)
Definition: listview.h:84
void(* listview_update_callback)(textbox *tb, icon *ico, unsigned int entry, void *udata, TextBoxFontType *type, gboolean full)
Definition: listview.h:67
void listview_set_filtered(listview *lv, gboolean filtered)
Definition: listview.c:1105
void listview_nav_up(listview *lv)
Definition: listview.c:839
void listview_nav_next(listview *lv)
Definition: listview.c:811
void listview_nav_page_prev(listview *lv)
Definition: listview.c:979
void listview_set_selection_changed_callback(listview *lv, listview_selection_changed_callback cb, void *udata)
Definition: listview.c:1111
void listview_nav_down(listview *lv)
Definition: listview.c:857
@ LISTVIEW_SCROLL_CONTINIOUS
Definition: listview.h:53
void widget_queue_redraw(widget *wid)
Definition: widget.c:487
gboolean widget_enabled(widget *widget)
Definition: widget.c:116
int widget_get_width(widget *widget)
Definition: widget.c:446
void widget_free(widget *wid)
Definition: widget.c:425
static void widget_disable(widget *widget)
Definition: widget.h:170
int widget_intersect(const widget *widget, int x, int y)
Definition: widget.c:75
void widget_resize(widget *widget, short w, short h)
Definition: widget.c:87
WidgetType
Definition: widget.h:56
static void widget_enable(widget *widget)
Definition: widget.h:178
int widget_get_desired_width(widget *wid, const int height)
Definition: widget.c:653
void widget_move(widget *widget, short x, short y)
Definition: widget.c:102
int widget_get_y_pos(widget *widget)
Definition: widget.c:461
void widget_draw(widget *widget, cairo_t *d)
Definition: widget.c:135
#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:510
void widget_set_type(widget *widget, WidgetType type)
Definition: widget.c:109
void widget_set_trigger_action_handler(widget *wid, widget_trigger_action_cb cb, void *cb_data)
Definition: widget.c:557
int widget_get_desired_height(widget *wid, const int width)
Definition: widget.c:644
int widget_get_x_pos(widget *widget)
Definition: widget.c:455
@ 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 void listview_nav_column_right_int(listview *lv)
Definition: listview.c:831
static unsigned int scroll_per_page(listview *lv)
Definition: listview.c:249
static void listview_nav_up_int(listview *lv)
Definition: listview.c:780
static widget * listview_find_mouse_target(widget *wid, WidgetType type, gint x, gint y)
Definition: listview.c:621
static unsigned int scroll_per_page_barview(listview *lv)
Definition: listview.c:235
#define BARVIEW
Definition: listview.c:51
const char *const listview_theme_prop_names[][3]
Definition: listview.c:137
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:955
static void listview_resize(widget *wid, short w, short h)
Definition: listview.c:596
static void _listview_draw(widget *wid, cairo_t *draw)
Definition: listview.c:510
static void listview_nav_page_prev_int(listview *lv)
Definition: listview.c:932
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:648
static void listview_recompute_elements(listview *lv)
Definition: listview.c:521
#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:168
static void barview_draw(widget *wid, cairo_t *draw)
Definition: listview.c:313
static void listview_free(widget *wid)
Definition: listview.c:224
static void listview_nav_column_left_int(listview *lv)
Definition: listview.c:824
static void listview_nav_down_int(listview *lv)
Definition: listview.c:796
#define LISTVIEW
Definition: listview.c:49
static void listview_create_row(listview *lv, _listview_row *row)
Definition: listview.c:197
static void listview_draw(widget *wid, cairo_t *draw)
Definition: listview.c:393
static int listview_get_desired_height(widget *wid, const int width)
static unsigned int scroll_continious(listview *lv)
Definition: listview.c:272
static void update_element(listview *lv, unsigned int tb, unsigned int index, gboolean full)
Definition: listview.c:292
static void listview_set_state(_listview_row r, TextBoxFontType tbft)
Definition: listview.c:146
RofiOrientation
Definition: rofi-types.h:141
@ ROFI_ORIENTATION_HORIZONTAL
Definition: rofi-types.h:143
@ ROFI_ORIENTATION_VERTICAL
Definition: rofi-types.h:142
Settings config
#define DEFAULT_MENU_COLUMNS
Definition: settings.h:192
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:89
void * mouse_activated_data
Definition: listview.c:121
gboolean cycle
Definition: listview.c:104
unsigned int rchanged
Definition: listview.c:73
unsigned int max_rows
Definition: listview.c:84
unsigned int cur_page
Definition: listview.c:79
unsigned int menu_columns
Definition: listview.c:96
unsigned int max_displayed_lines
Definition: listview.c:95
widget widget
Definition: listview.c:67
void * udata
Definition: listview.c:112
PangoEllipsizeMode emode
Definition: listview.c:125
unsigned int cur_visible
Definition: listview.c:129
listview_update_callback callback
Definition: listview.c:111
listview_mouse_activated_cb mouse_activated
Definition: listview.c:120
void * sc_udata
Definition: listview.c:115
unsigned int req_elements
Definition: listview.c:90
xcb_timestamp_t last_click
Definition: listview.c:119
unsigned int element_height
Definition: listview.c:83
unsigned int last_offset
Definition: listview.c:80
unsigned int cur_elements
Definition: listview.c:91
unsigned int dynamic
Definition: listview.c:98
MoveDirection direction
Definition: listview.c:128
char * listview_name
Definition: listview.c:123
gboolean filtered
Definition: listview.c:102
scrollbar * scrollbar
Definition: listview.c:109
unsigned int max_elements
Definition: listview.c:85
gboolean require_input
Definition: listview.c:101
unsigned int fixed_num_lines
Definition: listview.c:97
unsigned int reverse
Definition: listview.c:100
listview_selection_changed_callback sc_callback
Definition: listview.c:114
RofiDistance spacing
Definition: listview.c:93
gboolean fixed_columns
Definition: listview.c:88
RofiOrientation type
Definition: listview.c:69
unsigned int selected
Definition: listview.c:81
unsigned int eh
Definition: listview.c:99
ScrollType scroll_type
Definition: listview.c:106
struct _listview::@3 barview
gboolean scrollbar_scroll
Definition: listview.c:117
RofiOrientation pack_direction
Definition: listview.c:76
_listview_row * boxes
Definition: listview.c:108
unsigned int menu_lines
Definition: listview.c:94
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 *, const int width)
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)
GList * rofi_theme_get_list_strings(const widget *widget, const char *property)
Definition: theme.c:1267
RofiDistance rofi_theme_get_distance(const widget *widget, const char *property, int def)
Definition: theme.c:877
int rofi_theme_get_boolean(const widget *widget, const char *property, int def)
Definition: theme.c:903
int distance_get_pixel(RofiDistance d, RofiOrientation ori)
Definition: theme.c:1415
RofiOrientation rofi_theme_get_orientation(const widget *widget, const char *property, RofiOrientation def)
Definition: theme.c:932
int rofi_theme_get_integer(const widget *widget, const char *property, int def)
Definition: theme.c:840
MenuFlags flags
Definition: view.c:107
void widget_init(widget *wid, widget *parent, WidgetType type, const char *name)
Definition: widget.c:34
void widget_set_state(widget *widget, const char *state)
Definition: widget.c:57
int widget_padding_get_padding_width(const widget *wid)
Definition: widget.c:637
int widget_padding_get_left(const widget *wid)
Definition: widget.c:576
int widget_padding_get_right(const widget *wid)
Definition: widget.c:586
int widget_padding_get_padding_height(const widget *wid)
Definition: widget.c:631
int widget_padding_get_top(const widget *wid)
Definition: widget.c:598
int widget_padding_get_bottom(const widget *wid)
Definition: widget.c:608