rofi  1.7.3
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_strings(
180  WIDGET(wid2),
181  "children"); // rofi_theme_get_list(WIDGET(wid2), "children", "");
182  for (GList *iter = g_list_first(list); iter != NULL;
183  iter = g_list_next(iter)) {
184  listview_add_widget(lv, row, wid2, (const char *)iter->data);
185  }
186  }
187 }
188 
190  row->box = box_create(WIDGET(lv), "element", ROFI_ORIENTATION_HORIZONTAL);
192  GList *list = NULL;
193  list = rofi_theme_get_list_strings(WIDGET(row->box), "children");
194  if (list == NULL) {
195  if (config.show_icons) {
196  list = g_list_append(list, g_strdup("element-icon"));
197  list = g_list_append(list, g_strdup("element-text"));
198  } else {
199  list = g_list_append(list, g_strdup("element-text"));
200  }
201  }
202 
203  row->textbox = NULL;
204  row->icon = NULL;
205  row->index = NULL;
206 
207  for (GList *iter = g_list_first(list); iter != NULL;
208  iter = g_list_next(iter)) {
209  listview_add_widget(lv, row, WIDGET(row->box), (const char *)iter->data);
210  }
211  g_list_free_full(list, g_free);
212 }
213 
214 static int listview_get_desired_height(widget *wid, const int width);
215 
216 static void listview_free(widget *wid) {
217  listview *lv = (listview *)wid;
218  for (unsigned int i = 0; i < lv->cur_elements; i++) {
219  widget_free(WIDGET(lv->boxes[i].box));
220  }
221  g_free(lv->boxes);
222 
223  g_free(lv->listview_name);
225  g_free(lv);
226 }
227 static unsigned int scroll_per_page_barview(listview *lv) {
228  unsigned int offset = lv->last_offset;
229 
230  // selected row is always visible.
231  // If selected is visible do not scroll.
232  if (lv->selected < lv->last_offset) {
233  offset = lv->selected;
234  lv->rchanged = TRUE;
235  } else if (lv->selected >= (lv->last_offset + lv->barview.cur_visible)) {
236  offset = lv->selected;
237  lv->rchanged = TRUE;
238  }
239  return offset;
240 }
241 static unsigned int scroll_per_page(listview *lv) {
242  int offset = 0;
243 
244  // selected row is always visible.
245  // If selected is visible do not scroll.
246  if (((lv->selected - (lv->last_offset)) < (lv->max_elements)) &&
247  (lv->selected >= (lv->last_offset))) {
248  offset = lv->last_offset;
249  } else {
250  // Do paginating
251  unsigned int page =
252  (lv->max_elements > 0) ? (lv->selected / lv->max_elements) : 0;
253  offset = page * lv->max_elements;
254  if (page != lv->cur_page) {
255  lv->cur_page = page;
256  lv->rchanged = TRUE;
257  }
258  // Set the position
259  // scrollbar_set_handle ( lv->scrollbar, page * lv->max_elements );
260  }
261  return offset;
262 }
263 
264 static unsigned int scroll_continious(listview *lv) {
265  unsigned int middle = (lv->max_rows - ((lv->max_rows & 1) == 0)) / 2;
266  unsigned int offset = 0;
267  if (lv->selected > middle) {
268  if (lv->selected < (lv->req_elements - (lv->max_rows - middle))) {
269  offset = lv->selected - middle;
270  }
271  // Don't go below zero.
272  else if (lv->req_elements > lv->max_rows) {
273  offset = lv->req_elements - lv->max_rows;
274  }
275  }
276  if (offset != lv->cur_page) {
277  // scrollbar_set_handle ( lv->scrollbar, offset );
278  lv->cur_page = offset;
279  lv->rchanged = TRUE;
280  }
281  return offset;
282 }
283 
284 static void update_element(listview *lv, unsigned int tb, unsigned int index,
285  gboolean full) {
286  // Select drawing mode
287  TextBoxFontType type = (index & 1) == 0 ? NORMAL : ALT;
288  type = (index) == lv->selected ? HIGHLIGHT : type;
289 
290  if (lv->boxes[tb].index) {
291  if (index < 10) {
292  char str[2] = {((index + 1) % 10) + '0', '\0'};
293  textbox_text(lv->boxes[tb].index, str);
294  } else {
295  textbox_text(lv->boxes[tb].index, " ");
296  }
297  }
298  if (lv->callback) {
299  lv->callback(lv->boxes[tb].textbox, lv->boxes[tb].icon, index, lv->udata,
300  &type, full);
301  listview_set_state(lv->boxes[tb], type);
302  }
303 }
304 
305 static void barview_draw(widget *wid, cairo_t *draw) {
306  unsigned int offset = 0;
307  listview *lv = (listview *)wid;
308  offset = scroll_per_page_barview(lv);
309  lv->last_offset = offset;
310  int spacing_hori =
312 
313  int left_offset = widget_padding_get_left(wid);
314  int right_offset = lv->widget.w - widget_padding_get_right(wid);
315  int top_offset = widget_padding_get_top(wid);
316  if (lv->cur_elements > 0) {
317  // Set new x/y position.
318  unsigned int max = MIN(lv->cur_elements, lv->req_elements - offset);
319  if (lv->rchanged) {
320  int first = TRUE;
321  int width = lv->widget.w;
322  lv->barview.cur_visible = 0;
323  width -= widget_padding_get_padding_width(wid);
324  if (lv->barview.direction == LEFT_TO_RIGHT) {
325  for (unsigned int i = 0; i < max && width > 0; i++) {
326  update_element(lv, i, i + offset, TRUE);
327  int twidth = widget_get_desired_width(WIDGET(lv->boxes[i].box),
328  lv->element_height);
329  if (twidth >= width) {
330  if (!first) {
331  break;
332  }
333  twidth = width;
334  }
335  widget_move(WIDGET(lv->boxes[i].box), left_offset, top_offset);
336  widget_resize(WIDGET(lv->boxes[i].box), twidth, lv->element_height);
337 
338  widget_draw(WIDGET(lv->boxes[i].box), draw);
339  width -= twidth + spacing_hori;
340  left_offset += twidth + spacing_hori;
341  first = FALSE;
342  lv->barview.cur_visible++;
343  }
344  } else {
345  for (unsigned int i = 0;
346  i < lv->cur_elements && width > 0 && i <= offset; i++) {
347  update_element(lv, i, offset - i, TRUE);
348  int twidth = widget_get_desired_width(WIDGET(lv->boxes[i].box),
349  lv->element_height);
350  if (twidth >= width) {
351  if (!first) {
352  break;
353  }
354  twidth = width;
355  }
356  right_offset -= twidth;
357  widget_move(WIDGET(lv->boxes[i].box), right_offset, top_offset);
358  widget_resize(WIDGET(lv->boxes[i].box), twidth, lv->element_height);
359 
360  widget_draw(WIDGET(lv->boxes[i].box), draw);
361  width -= twidth + spacing_hori;
362  right_offset -= spacing_hori;
363  first = FALSE;
364  lv->barview.cur_visible++;
365  }
366  offset -= lv->barview.cur_visible - 1;
367  lv->last_offset = offset;
368  for (unsigned int i = 0; i < (lv->barview.cur_visible / 2); i++) {
369  _listview_row temp = lv->boxes[i];
370  int sw = lv->barview.cur_visible - i - 1;
371  lv->boxes[i] = lv->boxes[sw];
372  lv->boxes[sw] = temp;
373  }
374  }
375  lv->rchanged = FALSE;
376  } else {
377  for (unsigned int i = 0; i < lv->barview.cur_visible; i++) {
378  update_element(lv, i, i + offset, TRUE);
379  widget_draw(WIDGET(lv->boxes[i].box), draw);
380  }
381  }
382  }
383 }
384 
385 static void listview_draw(widget *wid, cairo_t *draw) {
386  unsigned int offset = 0;
387  listview *lv = (listview *)wid;
389  offset = scroll_continious(lv);
390  } else {
391  offset = scroll_per_page(lv);
392  }
393  // Set these all together to make sure they update consistently.
396  if (lv->reverse) {
398  } else {
400  }
401  lv->last_offset = offset;
402  int spacing_vert = distance_get_pixel(lv->spacing, ROFI_ORIENTATION_VERTICAL);
403  int spacing_hori =
405 
406  int left_offset = widget_padding_get_left(wid);
407  int top_offset = widget_padding_get_top(wid);
408  /*
409  if ( lv->scrollbar->widget.index == 0 ) {
410  left_offset += spacing_hori + lv->scrollbar->widget.w;
411  }
412  */
413  if (lv->cur_elements > 0 && lv->max_rows > 0) {
414  // Set new x/y position.
415  unsigned int max = MIN(lv->cur_elements, lv->req_elements - offset);
416  if (lv->rchanged) {
417  unsigned int width = lv->widget.w;
418  width -= widget_padding_get_padding_width(wid);
419  if (widget_enabled(WIDGET(lv->scrollbar))) {
420  width -= spacing_hori;
421  width -= widget_get_width(WIDGET(lv->scrollbar));
422  }
423  unsigned int element_width =
424  (width - spacing_hori * (lv->cur_columns - 1)) / lv->cur_columns;
425 
426  int d = width - (element_width + spacing_hori) * (lv->cur_columns - 1) -
427  element_width;
428  if (lv->cur_columns > 1) {
429  int diff = d / (lv->cur_columns - 1);
430  if (diff >= 1) {
431  spacing_hori += 1;
432  d -= lv->cur_columns - 1;
433  }
434  }
435  for (unsigned int i = 0; i < max; i++) {
436  unsigned int ex =
437  left_offset + ((i) / lv->max_rows) * (element_width + spacing_hori);
438 
439  if ((i) / lv->max_rows == (lv->cur_columns - 1)) {
440  ex += d;
441  }
442  if (lv->reverse) {
443  unsigned int ey =
444  wid->h -
446  ((i) % lv->max_rows) * (lv->element_height + spacing_vert)) -
447  lv->element_height;
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  } else {
452  unsigned int ey =
453  top_offset +
454  ((i) % lv->max_rows) * (lv->element_height + spacing_vert);
455  widget_move(WIDGET(lv->boxes[i].box), ex, ey);
456  widget_resize(WIDGET(lv->boxes[i].box), element_width,
457  lv->element_height);
458  }
459 
460  update_element(lv, i, i + offset, TRUE);
461  widget_draw(WIDGET(lv->boxes[i].box), draw);
462  }
463  lv->rchanged = FALSE;
464  } else {
465  for (unsigned int i = 0; i < max; i++) {
466  update_element(lv, i, i + offset, TRUE);
467  widget_draw(WIDGET(lv->boxes[i].box), draw);
468  }
469  }
470  }
471  widget_draw(WIDGET(lv->scrollbar), draw);
472 }
476  gint x, gint y, void *user_data);
477 static gboolean listview_element_motion_notify(widget *wid, gint x, gint y);
478 
479 static void _listview_draw(widget *wid, cairo_t *draw) {
480  listview *lv = (listview *)wid;
481  if (lv->type == LISTVIEW) {
482  listview_draw(wid, draw);
483  } else {
484  barview_draw(wid, draw);
485  }
486 }
491  unsigned int newne = 0;
492  if (lv->max_rows == 0) {
493  return;
494  }
495  if (!(lv->fixed_columns) && lv->req_elements < lv->max_elements) {
496  newne = lv->req_elements;
497  lv->cur_columns = (lv->req_elements + (lv->max_rows - 1)) / lv->max_rows;
498  } else {
499  newne = MIN(lv->req_elements, lv->max_elements);
500  lv->cur_columns = lv->menu_columns;
501  }
502  for (unsigned int i = newne; i < lv->cur_elements; i++) {
503  widget_free(WIDGET(lv->boxes[i].box));
504  }
505  lv->boxes = g_realloc(lv->boxes, newne * sizeof(_listview_row));
506  if (newne > 0) {
507  for (unsigned int i = lv->cur_elements; i < newne; i++) {
508  listview_create_row(lv, &(lv->boxes[i]));
509  widget *wid = WIDGET(lv->boxes[i].box);
511  lv);
512  if (wid != NULL) {
514  }
515 
517  }
518  }
519  lv->rchanged = TRUE;
520  lv->cur_elements = newne;
521 }
522 
523 void listview_set_num_elements(listview *lv, unsigned int rows) {
524  if (lv == NULL) {
525  return;
526  }
527  TICK_N("listview_set_num_elements");
528  lv->req_elements = rows;
530  TICK_N("Set selected");
532  TICK_N("recompute elements");
534  TICK_N("queue redraw");
535 }
536 
537 unsigned int listview_get_selected(listview *lv) {
538  if (lv != NULL) {
539  return lv->selected;
540  }
541  return 0;
542 }
543 
544 void listview_set_selected(listview *lv, unsigned int selected) {
545  if (lv && lv->req_elements > 0) {
546  lv->selected = MIN(selected, lv->req_elements - 1);
549  }
550 }
551 
552 static void listview_resize(widget *wid, short w, short h) {
553  listview *lv = (listview *)wid;
554  lv->widget.w = MAX(0, w);
555  lv->widget.h = MAX(0, h);
556  int height = lv->widget.h - widget_padding_get_padding_height(WIDGET(lv));
557  int spacing_vert = distance_get_pixel(lv->spacing, ROFI_ORIENTATION_VERTICAL);
558  lv->max_rows = (spacing_vert + height) / (lv->element_height + spacing_vert);
559  lv->max_elements = lv->max_rows * lv->menu_columns;
560 
565 
567  height);
568 
569  if (lv->type == BARVIEW) {
570  lv->max_elements = lv->menu_lines;
571  }
572 
574  widget_queue_redraw(wid);
575 }
576 
578  gint y) {
579  widget *target = NULL;
580  gint rx, ry;
581  listview *lv = (listview *)wid;
582  if (widget_enabled(WIDGET(lv->scrollbar)) &&
583  widget_intersect(WIDGET(lv->scrollbar), x, y)) {
584  rx = x - widget_get_x_pos(WIDGET(lv->scrollbar));
585  ry = y - widget_get_y_pos(WIDGET(lv->scrollbar));
586  target = widget_find_mouse_target(WIDGET(lv->scrollbar), type, rx, ry);
587  }
588 
589  unsigned int max = MIN(lv->cur_elements, lv->req_elements - lv->last_offset);
590  unsigned int i;
591  for (i = 0; i < max && target == NULL; i++) {
592  widget *w = WIDGET(lv->boxes[i].box);
593  if (widget_intersect(w, x, y)) {
594  rx = x - widget_get_x_pos(w);
595  ry = y - widget_get_y_pos(w);
596  target = widget_find_mouse_target(w, type, rx, ry);
597  }
598  }
599 
600  return target;
601 }
602 
605  G_GNUC_UNUSED gint x, G_GNUC_UNUSED gint y,
606  G_GNUC_UNUSED void *user_data) {
607  listview *lv = (listview *)wid;
608  switch (action) {
609  case SCROLL_LEFT:
610  listview_nav_left(lv);
611  break;
612  case SCROLL_RIGHT:
613  listview_nav_right(lv);
614  break;
615  case SCROLL_DOWN:
616  listview_nav_down(lv);
617  break;
618  case SCROLL_UP:
619  listview_nav_up(lv);
620  break;
621  }
623 }
624 
626  widget *wid, MouseBindingListviewElementAction action, G_GNUC_UNUSED gint x,
627  G_GNUC_UNUSED gint y, void *user_data) {
628  listview *lv = (listview *)user_data;
629  unsigned int max = MIN(lv->cur_elements, lv->req_elements - lv->last_offset);
630  unsigned int i;
631  for (i = 0; i < max && WIDGET(lv->boxes[i].box) != wid; i++) {
632  }
633  if (i == max) {
635  }
636 
637  gboolean custom = FALSE;
638  switch (action) {
640  listview_set_selected(lv, lv->last_offset + i);
641  break;
643  custom = TRUE;
644  /* FALLTHRU */
646  listview_set_selected(lv, lv->last_offset + i);
647  lv->mouse_activated(lv, custom, lv->mouse_activated_data);
648  break;
649  }
651 }
652 
654  G_GNUC_UNUSED gint x,
655  G_GNUC_UNUSED gint y) {
656  listview *lv = (listview *)wid->parent;
657  unsigned int max = MIN(lv->cur_elements, lv->req_elements - lv->last_offset);
658  unsigned int i;
659  for (i = 0; i < max && WIDGET(lv->boxes[i].box) != wid; i++) {
660  }
661  if (i < max && (lv->last_offset + i) != listview_get_selected(lv)) {
662  listview_set_selected(lv, lv->last_offset + i);
663  }
664  return TRUE;
665 }
666 
667 listview *listview_create(widget *parent, const char *name,
668  listview_update_callback cb, void *udata,
669  unsigned int eh, gboolean reverse) {
670  listview *lv = g_malloc0(sizeof(listview));
671  widget_init(WIDGET(lv), parent, WIDGET_TYPE_LISTVIEW, name);
672  lv->listview_name = g_strdup(name);
673  lv->widget.free = listview_free;
675  lv->widget.draw = _listview_draw;
679  lv->eh = eh;
680 
681  lv->emode = PANGO_ELLIPSIZE_END;
682  lv->scrollbar = scrollbar_create(WIDGET(lv), "scrollbar");
683  // Calculate height of an element.
684  //
685  _listview_row row;
686  listview_create_row(lv, &row);
687  // FIXME: hack to scale hight correctly.
688  if (lv->eh > 1 && row.textbox) {
689  char buff[lv->eh * 2 + 1];
690  memset(buff, '\0', lv->eh * 2 + 1);
691  for (unsigned int i = 0; i < (lv->eh - 1); i++) {
692  buff[i * 2] = 'a';
693  buff[i * 2 + 1] = '\n';
694  };
695  textbox_text(row.textbox, buff);
696  }
697  // Make textbox very wide.
699  widget_free(WIDGET(row.box));
700 
701  lv->callback = cb;
702  lv->udata = udata;
703 
704  // Some settings.
705  lv->spacing = rofi_theme_get_distance(WIDGET(lv), "spacing", DEFAULT_SPACING);
706  lv->menu_columns =
708  lv->fixed_num_lines = rofi_theme_get_boolean(WIDGET(lv), "fixed-height",
710  lv->dynamic = rofi_theme_get_boolean(WIDGET(lv), "dynamic", TRUE);
711  lv->reverse = rofi_theme_get_boolean(WIDGET(lv), "reverse", reverse);
712  lv->cycle = rofi_theme_get_boolean(WIDGET(lv), "cycle", config.cycle);
713  lv->fixed_columns =
714  rofi_theme_get_boolean(WIDGET(lv), "fixed-columns", FALSE);
715 
716  lv->type = rofi_theme_get_orientation(WIDGET(lv), "layout",
718  if (lv->type == LISTVIEW) {
720  lv, rofi_theme_get_boolean(WIDGET(lv), "scrollbar", FALSE));
721  } else {
722  listview_set_show_scrollbar(lv, FALSE);
723  }
724  return lv;
725 }
726 
731 static void listview_nav_up_int(listview *lv) {
732  if (lv == NULL) {
733  return;
734  }
735  if (lv->req_elements == 0 || (lv->selected == 0 && !lv->cycle)) {
736  return;
737  }
738  if (lv->selected == 0) {
739  lv->selected = lv->req_elements;
740  }
741  lv->selected--;
744 }
745 static void listview_nav_down_int(listview *lv) {
746  if (lv == NULL) {
747  return;
748  }
749  if (lv->req_elements == 0 ||
750  (lv->selected == (lv->req_elements - 1) && !lv->cycle)) {
751  return;
752  }
753  lv->selected = lv->selected < lv->req_elements - 1
754  ? MIN(lv->req_elements - 1, lv->selected + 1)
755  : 0;
758 }
759 
761  if (lv == NULL) {
762  return;
763  }
764  if (lv->reverse) {
766  } else {
768  }
769 }
771  if (lv == NULL) {
772  return;
773  }
774  if (lv->reverse) {
776  } else {
778  }
779 }
780 
782  if (lv == NULL) {
783  return;
784  }
785  if (lv->type == BARVIEW) {
787  return;
788  }
789  if (lv->selected >= lv->max_rows) {
790  lv->selected -= lv->max_rows;
792  }
793 }
795  if (lv == NULL) {
796  return;
797  }
798  if (lv->max_rows == 0) {
799  return;
800  }
801  if (lv->type == BARVIEW) {
803  return;
804  }
805  if ((lv->selected + lv->max_rows) < lv->req_elements) {
806  lv->selected += lv->max_rows;
808  } else if (lv->selected < (lv->req_elements - 1)) {
809  // We do not want to move to last item, UNLESS the last column is only
810  // partially filled, then we still want to move column and select last
811  // entry. First check the column we are currently in.
812  int col = lv->selected / lv->max_rows;
813  // Check total number of columns.
814  int ncol = lv->req_elements / lv->max_rows;
815  // If there is an extra column, move.
816  if (col != ncol) {
817  lv->selected = lv->req_elements - 1;
819  }
820  }
821 }
822 
824  if (lv == NULL) {
825  return;
826  }
827  if (lv->type == BARVIEW) {
828  if (lv->last_offset == 0) {
829  lv->selected = 0;
830  } else {
831  lv->selected = lv->last_offset - 1;
832  }
835  return;
836  }
837 
838  if (lv->selected < lv->max_elements) {
839  lv->selected = 0;
840  } else {
841  lv->selected -= (lv->max_elements);
842  }
844 }
846  if (lv == NULL) {
847  return;
848  }
849  if (lv->req_elements == 0) {
850  return;
851  }
852  if (lv->type == BARVIEW) {
853  unsigned int new = lv->last_offset + lv->barview.cur_visible;
854  lv->selected = MIN(new, lv->req_elements - 1);
856 
858  return;
859  }
860  lv->selected += (lv->max_elements);
861  if (lv->selected >= lv->req_elements) {
862  lv->selected = lv->req_elements - 1;
863  }
865 }
866 
868  if (lv == NULL) {
869  return;
870  }
871  if (lv->reverse) {
873  } else {
875  }
876 }
878  if (lv == NULL) {
879  return;
880  }
881  if (lv->reverse) {
883  } else {
885  }
886 }
887 
889  G_GNUC_UNUSED const int width) {
890  listview *lv = (listview *)wid;
891  if (lv == NULL || lv->widget.enabled == FALSE) {
892  return 0;
893  }
895  int h = lv->menu_lines;
896  if (!(lv->fixed_num_lines)) {
897  if (lv->dynamic) {
898  h = MIN(lv->menu_lines, lv->req_elements);
899  } else {
900  h = MIN(lv->menu_lines, lv->max_displayed_lines);
901  }
902  }
903  if (lv->type == BARVIEW) {
904  h = MIN(h, 1);
905  }
906  if (h == 0) {
907  if (lv->dynamic && !lv->fixed_num_lines) {
908  // Hide widget fully.
909  return 0;
910  }
912  }
913  int height = widget_padding_get_padding_height(WIDGET(lv));
914  height += h * (lv->element_height + spacing) - spacing;
915  return height;
916 }
917 
918 void listview_set_show_scrollbar(listview *lv, gboolean enabled) {
919  if (lv) {
920  if (enabled) {
922  } else {
924  }
926  }
927 }
928 
930  if (lv) {
931  lv->scroll_type = type;
932  }
933 }
934 
937  void *udata) {
938  if (lv) {
939  lv->mouse_activated = cb;
940  lv->mouse_activated_data = udata;
941  }
942 }
943 void listview_set_multi_select(listview *lv, gboolean enable) {
944  if (lv) {
945  lv->multi_select = enable;
946  }
947 }
948 void listview_set_num_lines(listview *lv, unsigned int num_lines) {
949  if (lv) {
950  lv->menu_lines = num_lines;
951  }
952 }
953 
954 void listview_set_max_lines(listview *lv, unsigned int max_lines) {
955  if (lv) {
956  lv->max_displayed_lines = max_lines;
957  }
958 }
959 
961  if (lv) {
962  return lv->fixed_num_lines;
963  }
964  return FALSE;
965 }
967  if (lv) {
968  lv->fixed_num_lines = TRUE;
969  }
970 }
971 
973  if (lv) {
974  lv->emode = PANGO_ELLIPSIZE_START;
975  for (unsigned int i = 0; i < lv->cur_elements; i++) {
977  }
978  }
979 }
980 
982  if (lv) {
983  PangoEllipsizeMode mode = lv->emode;
984  if (mode == PANGO_ELLIPSIZE_START) {
985  mode = PANGO_ELLIPSIZE_MIDDLE;
986  } else if (mode == PANGO_ELLIPSIZE_MIDDLE) {
987  mode = PANGO_ELLIPSIZE_END;
988  } else if (mode == PANGO_ELLIPSIZE_END) {
989  mode = PANGO_ELLIPSIZE_START;
990  }
991  lv->emode = mode;
992  for (unsigned int i = 0; i < lv->cur_elements; i++) {
993  textbox_set_ellipsize(lv->boxes[i].textbox, mode);
994  }
995  }
996 }
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: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:101
void textbox_set_ellipsize(textbox *tb, PangoEllipsizeMode mode)
Definition: textbox.c:965
textbox * textbox_create(widget *parent, WidgetType type, const char *name, TextboxFlags flags, TextBoxFontType tbft, const char *text, double xalign, double yalign)
Definition: textbox.c:191
void textbox_text(textbox *tb, const char *text)
Definition: textbox.c:345
@ 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: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:877
void listview_set_fixed_num_lines(listview *lv)
Definition: listview.c:966
void listview_set_num_lines(listview *lv, unsigned int num_lines)
Definition: listview.c:948
void listview_set_show_scrollbar(listview *lv, gboolean enabled)
Definition: listview.c:918
void listview_set_num_elements(listview *lv, unsigned int rows)
Definition: listview.c:523
listview * listview_create(widget *parent, const char *name, listview_update_callback cb, void *udata, unsigned int eh, gboolean reverse)
Definition: listview.c:667
void listview_nav_right(listview *lv)
Definition: listview.c:794
void listview_set_mouse_activated_cb(listview *lv, listview_mouse_activated_cb cb, void *udata)
Definition: listview.c:935
void listview_toggle_ellipsizing(listview *lv)
Definition: listview.c:981
void listview_set_selected(listview *lv, unsigned int selected)
Definition: listview.c:544
void listview_set_max_lines(listview *lv, unsigned int max_lines)
Definition: listview.c:954
void listview_nav_left(listview *lv)
Definition: listview.c:781
void listview_set_scroll_type(listview *lv, ScrollType type)
Definition: listview.c:929
gboolean listview_get_fixed_num_lines(listview *lv)
Definition: listview.c:960
void listview_set_ellipsize_start(listview *lv)
Definition: listview.c:972
unsigned int listview_get_selected(listview *lv)
Definition: listview.c:537
ScrollType
Definition: listview.h:49
void listview_set_multi_select(listview *lv, gboolean enable)
Definition: listview.c:943
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:760
void listview_nav_page_prev(listview *lv)
Definition: listview.c:867
void listview_nav_down(listview *lv)
Definition: listview.c:770
@ 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:651
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:555
int widget_get_desired_height(widget *wid, const int width)
Definition: widget.c:642
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 unsigned int scroll_per_page(listview *lv)
Definition: listview.c:241
static void listview_nav_up_int(listview *lv)
Definition: listview.c:731
static widget * listview_find_mouse_target(widget *wid, WidgetType type, gint x, gint y)
Definition: listview.c:577
static unsigned int scroll_per_page_barview(listview *lv)
Definition: listview.c:227
#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:845
static void listview_resize(widget *wid, short w, short h)
Definition: listview.c:552
static void _listview_draw(widget *wid, cairo_t *draw)
Definition: listview.c:479
static void listview_nav_page_prev_int(listview *lv)
Definition: listview.c:823
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:604
static void listview_recompute_elements(listview *lv)
Definition: listview.c:490
#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 void barview_draw(widget *wid, cairo_t *draw)
Definition: listview.c:305
static void listview_free(widget *wid)
Definition: listview.c:216
static void listview_nav_down_int(listview *lv)
Definition: listview.c:745
#define LISTVIEW
Definition: listview.c:49
static void listview_create_row(listview *lv, _listview_row *row)
Definition: listview.c:189
static void listview_draw(widget *wid, cairo_t *draw)
Definition: listview.c:385
static int listview_get_desired_height(widget *wid, const int width)
static unsigned int scroll_continious(listview *lv)
Definition: listview.c:264
static void update_element(listview *lv, unsigned int tb, unsigned int index, gboolean full)
Definition: listview.c:284
static void listview_set_state(_listview_row r, TextBoxFontType tbft)
Definition: listview.c:138
RofiOrientation
Definition: rofi-types.h:137
@ ROFI_ORIENTATION_HORIZONTAL
Definition: rofi-types.h:139
@ ROFI_ORIENTATION_VERTICAL
Definition: rofi-types.h:138
Settings config
#define DEFAULT_MENU_COLUMNS
Definition: settings.h:185
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 *, 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:1255
RofiDistance rofi_theme_get_distance(const widget *widget, const char *property, int def)
Definition: theme.c:865
int rofi_theme_get_boolean(const widget *widget, const char *property, int def)
Definition: theme.c:891
int distance_get_pixel(RofiDistance d, RofiOrientation ori)
Definition: theme.c:1403
RofiOrientation rofi_theme_get_orientation(const widget *widget, const char *property, RofiOrientation def)
Definition: theme.c:920
int rofi_theme_get_integer(const widget *widget, const char *property, int def)
Definition: theme.c:828
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:635
int widget_padding_get_left(const widget *wid)
Definition: widget.c:574
int widget_padding_get_right(const widget *wid)
Definition: widget.c:584
int widget_padding_get_padding_height(const widget *wid)
Definition: widget.c:629
int widget_padding_get_top(const widget *wid)
Definition: widget.c:596
int widget_padding_get_bottom(const widget *wid)
Definition: widget.c:606