rofi 2.1.0
listview.c
Go to the documentation of this file.
1/*
2 * rofi
3 *
4 * MIT/X11 License
5 * Copyright © 2013-2023 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 "helper.h"
38#include "settings.h"
39#include "theme.h"
40#include "view.h"
41
42#include "timings.h"
43
45#define DEFAULT_SPACING 2
46
51#define LISTVIEW ROFI_ORIENTATION_VERTICAL
53#define BARVIEW ROFI_ORIENTATION_HORIZONTAL
54
59typedef enum { LEFT_TO_RIGHT = 0, RIGHT_TO_LEFT = 1 } MoveDirection;
60
67
68struct _listview {
70
72
73 // RChanged
74 // Text needs to be repainted.
75 unsigned int rchanged;
76
77 // The direction we pack the widgets.
79 // Administration
80
81 unsigned int cur_page;
82 unsigned int last_offset;
83 unsigned int selected;
84
85 unsigned int element_height;
86 unsigned int max_rows;
87 unsigned int max_elements;
88
89 //
90 gboolean fixed_columns;
91 unsigned int cur_columns;
92 unsigned int req_elements;
93 unsigned int cur_elements;
94
96 unsigned int menu_lines;
97 unsigned int max_displayed_lines;
98 unsigned int menu_columns;
99 unsigned int fixed_num_lines;
100 unsigned int dynamic;
101 unsigned int eh;
102 unsigned int reverse;
104 gboolean filtered;
105
106 gboolean cycle;
107
109
112
114 void *udata;
115
117 void *sc_udata;
118
120
121 guint32 last_click;
124
126
128
129 PangoEllipsizeMode emode;
131 struct {
133 unsigned int cur_visible;
135};
136
141const char *const listview_theme_prop_names[][3] = {
143 {"normal.normal", "selected.normal", "alternate.normal"},
145 {"normal.urgent", "selected.urgent", "alternate.urgent"},
147 {"normal.active", "selected.active", "alternate.active"},
148};
149
151 widget *w = WIDGET(r.box);
152 TextBoxFontType t = tbft & STATE_MASK;
153 if (w == NULL) {
154 return;
155 }
156 // ACTIVE has priority over URGENT if both set.
157 if (t == (URGENT | ACTIVE)) {
158 t = ACTIVE;
159 }
160 switch ((tbft & FMOD_MASK)) {
161 case HIGHLIGHT:
163 break;
164 case ALT:
166 break;
167 default:
169 break;
170 }
171}
173 const char *label) {
174 if (strcasecmp(label, "element-icon") == 0) {
175 row->icon = icon_create(WIDGET(wid), "element-icon");
176 box_add((box *)wid, WIDGET(row->icon), FALSE);
177 } else if (strcasecmp(label, "element-text") == 0) {
178 row->textbox =
179 textbox_create(WIDGET(wid), WIDGET_TYPE_TEXTBOX_TEXT, "element-text",
180 TB_AUTOHEIGHT, NORMAL, "DDD", 0, 0);
182 box_add((box *)wid, WIDGET(row->textbox), TRUE);
183 } else if (strcasecmp(label, "element-index") == 0) {
184 row->index =
185 textbox_create(WIDGET(wid), WIDGET_TYPE_TEXTBOX_TEXT, "element-index",
186 TB_AUTOHEIGHT, NORMAL, " ", 0, 0);
187 box_add((box *)wid, WIDGET(row->index), FALSE);
188 } else if (strncasecmp(label, "textbox", 7) == 0) {
189 textbox *textbox_custom =
191 TB_AUTOHEIGHT | TB_WRAP, NORMAL, "", 0, 0);
192 box_add((box *)wid, WIDGET(textbox_custom), TRUE);
193 } else if (strncasecmp(label, "button", 6) == 0) {
194 textbox *button_custom =
196 NORMAL, "", 0, 0);
197 box_add((box *)wid, WIDGET(button_custom), TRUE);
200 } else if (strncasecmp(label, "icon", 4) == 0) {
201 icon *icon_custom = icon_create(wid, label);
202 /* small hack to make it clickable */
203 const char *type =
204 rofi_theme_get_string(WIDGET(icon_custom), "action", NULL);
205 if (type) {
206 WIDGET(icon_custom)->type = WIDGET_TYPE_EDITBOX;
207 }
208 box_add((box *)wid, WIDGET(icon_custom), TRUE);
211 } else {
212 widget *wid2 = (widget *)box_create(wid, label, ROFI_ORIENTATION_VERTICAL);
213 box_add((box *)wid, WIDGET(wid2), TRUE);
214 GList *list = rofi_theme_get_list_strings(
215 WIDGET(wid2),
216 "children"); // rofi_theme_get_list(WIDGET(wid2), "children", "");
217 for (GList *iter = g_list_first(list); iter != NULL;
218 iter = g_list_next(iter)) {
219 listview_add_widget(lv, row, wid2, (const char *)iter->data);
220 }
221 }
222}
223
225 row->box = box_create(WIDGET(lv), "element", ROFI_ORIENTATION_HORIZONTAL);
227 GList *list = NULL;
228 list = rofi_theme_get_list_strings(WIDGET(row->box), "children");
229 if (list == NULL) {
230 if (config.show_icons) {
231 list = g_list_append(list, g_strdup("element-icon"));
232 list = g_list_append(list, g_strdup("element-text"));
233 } else {
234 list = g_list_append(list, g_strdup("element-text"));
235 }
236 }
237
238 row->textbox = NULL;
239 row->icon = NULL;
240 row->index = NULL;
241
242 for (GList *iter = g_list_first(list); iter != NULL;
243 iter = g_list_next(iter)) {
244 listview_add_widget(lv, row, WIDGET(row->box), (const char *)iter->data);
245 }
246 g_list_free_full(list, g_free);
247}
248
249static int listview_get_desired_height(widget *wid, const int width);
250
251static void listview_free(widget *wid) {
252 listview *lv = (listview *)wid;
253 for (unsigned int i = 0; i < lv->cur_elements; i++) {
254 widget_free(WIDGET(lv->boxes[i].box));
255 }
256 g_free(lv->boxes);
257
258 g_free(lv->listview_name);
260 g_free(lv);
261}
262static unsigned int scroll_per_page_barview(listview *lv) {
263 unsigned int offset = lv->last_offset;
264
265 // selected row is always visible.
266 // If selected is visible do not scroll.
267 if (lv->selected < lv->last_offset) {
268 offset = lv->selected;
269 lv->rchanged = TRUE;
270 } else if (lv->selected >= (lv->last_offset + lv->barview.cur_visible)) {
271 offset = lv->selected;
272 lv->rchanged = TRUE;
273 }
274 return offset;
275}
276static unsigned int scroll_per_page(listview *lv) {
277 int offset = 0;
278
279 // selected row is always visible.
280 // If selected is visible do not scroll.
281 if (((lv->selected - (lv->last_offset)) < (lv->max_elements)) &&
282 (lv->selected >= (lv->last_offset))) {
283 offset = lv->last_offset;
284 } else {
285 // Do paginating
286 unsigned int page =
287 (lv->max_elements > 0) ? (lv->selected / lv->max_elements) : 0;
288 offset = page * lv->max_elements;
289 if (page != lv->cur_page) {
290
291 if (lv->page_callback)
292 lv->page_callback();
293
294 lv->cur_page = page;
295 lv->rchanged = TRUE;
296 }
297 // Set the position
298 // scrollbar_set_handle ( lv->scrollbar, page * lv->max_elements );
299 }
300 return offset;
301}
302
303// For vertical packing flow
304static unsigned int scroll_continious_elements(listview *lv) {
305 unsigned int vmid = (lv->max_rows - 1) / 2;
306 unsigned int hmid = (lv->menu_columns - 1) / 2;
307 unsigned int middle = (lv->max_rows * hmid) + vmid;
308 unsigned int offset = 0;
309 if (lv->selected > middle) {
310 if (lv->selected < (lv->req_elements - (lv->max_elements - middle))) {
311 offset = lv->selected - middle;
312 }
313 // Don't go below zero.
314 else if (lv->req_elements > lv->max_elements) {
315 offset = lv->req_elements - lv->max_elements;
316 }
317 }
318 if (offset != lv->cur_page) {
319 // scrollbar_set_handle ( lv->scrollbar, offset );
320 lv->cur_page = offset;
321 lv->rchanged = TRUE;
322 }
323 return offset;
324}
325
326// For horizontal packing flow
327static unsigned int scroll_continious_rows(listview *lv) {
328 unsigned int middle, selected, req_rows, offset;
329 middle = (lv->max_rows - 1) / 2;
330 selected = lv->selected / lv->menu_columns;
331 req_rows = (lv->req_elements + lv->menu_columns - 1) / lv->menu_columns;
332 offset = 0;
333 if (selected > middle) {
334 if (selected < (req_rows - (lv->max_rows - middle))) {
335 offset = selected - middle;
336 }
337 // Don't go below zero.
338 else if (req_rows > lv->max_rows) {
339 offset = req_rows - lv->max_rows;
340 }
341 }
342 offset *= lv->menu_columns;
343 if (offset != lv->cur_page) {
344 // scrollbar_set_handle ( lv->scrollbar, offset );
345 lv->cur_page = offset;
346 lv->rchanged = TRUE;
347 }
348 return offset;
349}
350
351static void update_element(listview *lv, unsigned int tb, unsigned int index,
352 gboolean full) {
353 // Select drawing mode
354 TextBoxFontType type = (index & 1) == 0 ? NORMAL : ALT;
355 type = (index) == lv->selected ? HIGHLIGHT : type;
356
357 if (lv->boxes[tb].index) {
358 if (index < 10) {
359 char str[2] = {((index + 1) % 10) + '0', '\0'};
360 textbox_text(lv->boxes[tb].index, str);
361 } else {
362 textbox_text(lv->boxes[tb].index, " ");
363 }
364 }
365 if (lv->callback) {
366 lv->callback(lv->boxes[tb].textbox, lv->boxes[tb].icon, index, lv->udata,
367 &type, full);
368 listview_set_state(lv->boxes[tb], type);
369 }
370}
371
372static void barview_draw(widget *wid, cairo_t *draw) {
373 unsigned int offset = 0;
374 listview *lv = (listview *)wid;
375 offset = scroll_per_page_barview(lv);
376 lv->last_offset = offset;
377 int spacing_hori =
379
380 int left_offset = widget_padding_get_left(wid);
381 int right_offset = lv->widget.w - widget_padding_get_right(wid);
382 int top_offset = widget_padding_get_top(wid);
383 if (lv->cur_elements > 0) {
384 // Set new x/y position.
385 unsigned int max = MIN(lv->cur_elements, lv->req_elements - offset);
386 if (lv->rchanged) {
387 int first = TRUE;
388 int width = lv->widget.w;
389 lv->barview.cur_visible = 0;
391 if (lv->barview.direction == LEFT_TO_RIGHT) {
392 for (unsigned int i = 0; i < max && width > 0; i++) {
393 update_element(lv, i, i + offset, TRUE);
394 int twidth = widget_get_desired_width(WIDGET(lv->boxes[i].box),
395 lv->element_height);
396 if (twidth >= width) {
397 if (!first) {
398 break;
399 }
400 twidth = width;
401 }
402 widget_move(WIDGET(lv->boxes[i].box), left_offset, top_offset);
403 widget_resize(WIDGET(lv->boxes[i].box), twidth, lv->element_height);
404
405 widget_draw(WIDGET(lv->boxes[i].box), draw);
406 width -= twidth + spacing_hori;
407 left_offset += twidth + spacing_hori;
408 first = FALSE;
409 lv->barview.cur_visible++;
410 }
411 } else {
412 for (unsigned int i = 0;
413 i < lv->cur_elements && width > 0 && i <= offset; i++) {
414 update_element(lv, i, offset - i, TRUE);
415 int twidth = widget_get_desired_width(WIDGET(lv->boxes[i].box),
416 lv->element_height);
417 if (twidth >= width) {
418 if (!first) {
419 break;
420 }
421 twidth = width;
422 }
423 right_offset -= twidth;
424 widget_move(WIDGET(lv->boxes[i].box), right_offset, top_offset);
425 widget_resize(WIDGET(lv->boxes[i].box), twidth, lv->element_height);
426
427 widget_draw(WIDGET(lv->boxes[i].box), draw);
428 width -= twidth + spacing_hori;
429 right_offset -= spacing_hori;
430 first = FALSE;
431 lv->barview.cur_visible++;
432 }
433 offset -= lv->barview.cur_visible - 1;
434 lv->last_offset = offset;
435 for (unsigned int i = 0; i < (lv->barview.cur_visible / 2); i++) {
436 _listview_row temp = lv->boxes[i];
437 int sw = lv->barview.cur_visible - i - 1;
438 lv->boxes[i] = lv->boxes[sw];
439 lv->boxes[sw] = temp;
440 }
441 }
442 lv->rchanged = FALSE;
443 } else {
444 for (unsigned int i = 0; i < lv->barview.cur_visible; i++) {
445 update_element(lv, i, i + offset, TRUE);
446 widget_draw(WIDGET(lv->boxes[i].box), draw);
447 }
448 }
449 }
450}
451
452static void listview_draw(widget *wid, cairo_t *draw) {
453 unsigned int offset = 0;
454 listview *lv = (listview *)wid;
456 offset = scroll_per_page(lv);
457 } else if (lv->pack_direction == ROFI_ORIENTATION_VERTICAL) {
458 offset = scroll_continious_elements(lv);
459 } else {
460 offset = scroll_continious_rows(lv);
461 }
462 // Set these all together to make sure they update consistently.
465 if (lv->reverse) {
467 } else {
469 }
470 lv->last_offset = offset;
472 int spacing_hori =
474
475 int left_offset = widget_padding_get_left(wid);
476 int top_offset = widget_padding_get_top(wid);
477 /*
478 if ( lv->scrollbar->widget.index == 0 ) {
479 left_offset += spacing_hori + lv->scrollbar->widget.w;
480 }
481 */
482 if (lv->cur_elements > 0 && lv->max_rows > 0) {
483 // Set new x/y position.
484 unsigned int max = MIN(lv->cur_elements, lv->req_elements - offset);
485 if (lv->rchanged) {
486 unsigned int width = lv->widget.w;
488 if (widget_enabled(WIDGET(lv->scrollbar))) {
489 width -= spacing_hori;
490 width -= widget_get_width(WIDGET(lv->scrollbar));
491 }
492 unsigned int element_width =
493 (width - spacing_hori * (lv->cur_columns - 1)) / lv->cur_columns;
494
495 int d = width - (element_width + spacing_hori) * (lv->cur_columns - 1) -
496 element_width;
497 if (lv->cur_columns > 1) {
498 int diff = d / (lv->cur_columns - 1);
499 if (diff >= 1) {
500 spacing_hori += 1;
501 }
502 }
503 for (unsigned int i = 0; i < max; i++) {
505 unsigned int ex = left_offset + ((i) % lv->cur_columns) *
506 (element_width + spacing_hori);
507 unsigned int ey = 0;
508 if (lv->reverse) {
509 ey = wid->h -
511 ((i) / lv->cur_columns) *
512 (lv->element_height + spacing_vert)) -
513 lv->element_height;
514
515 } else {
516 ey = top_offset +
517 ((i) / lv->cur_columns) * (lv->element_height + spacing_vert);
518 }
519 widget_move(WIDGET(lv->boxes[i].box), ex, ey);
520 widget_resize(WIDGET(lv->boxes[i].box), element_width,
521 lv->element_height);
522
523 } else {
524 unsigned int ex = left_offset + ((i) / lv->max_rows) *
525 (element_width + spacing_hori);
526
527 if ((i) / lv->max_rows == (lv->cur_columns - 1)) {
528 ex += d;
529 }
530 unsigned int ey = 0;
531 if (lv->reverse) {
532 ey = wid->h -
534 ((i) % lv->max_rows) * (lv->element_height + spacing_vert)) -
535 lv->element_height;
536 } else {
537 ey = top_offset +
538 ((i) % lv->max_rows) * (lv->element_height + spacing_vert);
539 }
540 widget_move(WIDGET(lv->boxes[i].box), ex, ey);
541 widget_resize(WIDGET(lv->boxes[i].box), element_width,
542 lv->element_height);
543 }
544 update_element(lv, i, i + offset, TRUE);
545 widget_draw(WIDGET(lv->boxes[i].box), draw);
546 }
547 lv->rchanged = FALSE;
548 } else {
549 for (unsigned int i = 0; i < max; i++) {
550 update_element(lv, i, i + offset, TRUE);
551 widget_draw(WIDGET(lv->boxes[i].box), draw);
552 }
553 }
554 }
555 widget_draw(WIDGET(lv->scrollbar), draw);
556}
560 gint x, gint y, void *user_data);
561static gboolean listview_element_motion_notify(widget *wid, gint x, gint y);
562
563static void _listview_draw(widget *wid, cairo_t *draw) {
564 listview *lv = (listview *)wid;
565 if (lv->type == LISTVIEW) {
566 listview_draw(wid, draw);
567 } else {
568 barview_draw(wid, draw);
569 }
570}
571
575 unsigned int newne = 0;
576 if (lv->max_rows == 0) {
577 return;
578 }
579 if (!(lv->fixed_columns) && lv->req_elements < lv->max_elements) {
580 newne = lv->req_elements;
582 lv->cur_columns = (lv->req_elements + (lv->max_rows - 1)) / lv->max_rows;
583 } else {
584 lv->cur_columns = lv->menu_columns;
585 if (lv->req_elements < lv->menu_columns) {
586 lv->cur_columns = lv->req_elements;
587 }
588 }
589 } else {
590 newne = MIN(lv->req_elements, lv->max_elements);
591 lv->cur_columns = lv->menu_columns;
592 }
593 for (unsigned int i = newne; i < lv->cur_elements; i++) {
594 widget_free(WIDGET(lv->boxes[i].box));
595 }
596 lv->boxes = g_realloc(lv->boxes, newne * sizeof(_listview_row));
597 if (newne > 0) {
598 for (unsigned int i = lv->cur_elements; i < newne; i++) {
599 listview_create_row(lv, &(lv->boxes[i]));
600 widget *wid = WIDGET(lv->boxes[i].box);
602 lv);
603 if (wid != NULL) {
605 }
606
608 }
609 }
610 lv->rchanged = TRUE;
611 lv->cur_elements = newne;
612}
613
614void listview_set_num_elements(listview *lv, unsigned int rows) {
615 if (lv == NULL) {
616 return;
617 }
618 TICK_N("listview_set_num_elements");
619 lv->req_elements = rows;
620 if (lv->require_input && !lv->filtered) {
621 lv->req_elements = 0;
622 }
624 TICK_N("Set selected");
626 TICK_N("recompute elements");
628 TICK_N("queue redraw");
629}
630
632 if (lv != NULL) {
633 return lv->selected;
634 }
635 return 0;
636}
637
639 RofiOrientation orientation) {
640 if (lv == NULL || lv->boxes == NULL || lv->cur_elements == 0) {
641 return 0;
642 }
643 if (orientation == ROFI_ORIENTATION_HORIZONTAL) {
644 return widget_get_width(WIDGET(lv->boxes[0].box)) +
646 }
647 return lv->element_height +
649}
650
651void listview_set_selected(listview *lv, unsigned int selected) {
652 if (lv == NULL) {
653 return;
654 }
655 if (lv->req_elements > 0) {
656 lv->selected = MIN(selected, lv->req_elements - 1);
659 if (lv->sc_callback) {
660 lv->sc_callback(lv, lv->selected, lv->sc_udata);
661 }
662 } else {
663 if (lv->sc_callback) {
664 lv->sc_callback(lv, UINT32_MAX, lv->sc_udata);
665 }
666 }
667}
668
669static void listview_resize(widget *wid, short w, short h) {
670 listview *lv = (listview *)wid;
671 lv->widget.w = MAX(0, w);
672 lv->widget.h = MAX(0, h);
673 int height = lv->widget.h - widget_padding_get_padding_height(WIDGET(lv));
675 if (lv->widget.h == 0) {
676 lv->max_rows = lv->menu_lines;
677 } else {
678 lv->max_rows =
679 (spacing_vert + height) / (lv->element_height + spacing_vert);
680 }
681 lv->max_elements = lv->max_rows * lv->menu_columns;
682
687
689 height);
690
691 if (lv->type == BARVIEW) {
692 lv->max_elements = lv->menu_lines;
693 }
694
697}
698
700 gint y) {
701 widget *target = NULL;
702 gint rx, ry;
703 listview *lv = (listview *)wid;
704 if (widget_enabled(WIDGET(lv->scrollbar)) &&
705 widget_intersect(WIDGET(lv->scrollbar), x, y)) {
706 rx = x - widget_get_x_pos(WIDGET(lv->scrollbar));
707 ry = y - widget_get_y_pos(WIDGET(lv->scrollbar));
708 target = widget_find_mouse_target(WIDGET(lv->scrollbar), type, rx, ry);
709 }
710
711 unsigned int max = MIN(lv->cur_elements, lv->req_elements - lv->last_offset);
712 unsigned int i;
713 for (i = 0; i < max && target == NULL; i++) {
714 widget *w = WIDGET(lv->boxes[i].box);
715 if (widget_intersect(w, x, y)) {
716 rx = x - widget_get_x_pos(w);
717 ry = y - widget_get_y_pos(w);
718 target = widget_find_mouse_target(w, type, rx, ry);
719 }
720 }
721
722 return target;
723}
724
727 G_GNUC_UNUSED gint x, G_GNUC_UNUSED gint y,
728 G_GNUC_UNUSED void *user_data) {
729 listview *lv = (listview *)wid;
730 switch (action) {
731 case SCROLL_LEFT:
733 break;
734 case SCROLL_RIGHT:
736 break;
737 case SCROLL_DOWN:
739 break;
740 case SCROLL_UP:
741 listview_nav_up(lv);
742 break;
743 }
745}
746
748 widget *wid, MouseBindingListviewElementAction action, G_GNUC_UNUSED gint x,
749 G_GNUC_UNUSED gint y, void *user_data) {
750 listview *lv = (listview *)user_data;
751 unsigned int max = MIN(lv->cur_elements, lv->req_elements - lv->last_offset);
752 unsigned int i;
753 for (i = 0; i < max && WIDGET(lv->boxes[i].box) != wid; i++) {
754 }
755 if (i == max) {
757 }
758
759 gboolean custom = FALSE;
760 switch (action) {
763 break;
765 custom = TRUE;
769 lv->mouse_activated(lv, custom, lv->mouse_activated_data);
770 break;
771 }
773}
774
776 G_GNUC_UNUSED gint x,
777 G_GNUC_UNUSED gint y) {
778 listview *lv = (listview *)wid->parent;
779 unsigned int max = MIN(lv->cur_elements, lv->req_elements - lv->last_offset);
780 unsigned int i;
781 for (i = 0; i < max && WIDGET(lv->boxes[i].box) != wid; i++) {
782 }
783 if (i < max && (lv->last_offset + i) != listview_get_selected(lv)) {
785 }
786 return TRUE;
787}
788
789listview *listview_create(widget *parent, const char *name,
791 listview_page_changed_cb page_cb, void *udata,
792 unsigned int eh, gboolean reverse) {
793 listview *lv = g_malloc0(sizeof(listview));
794 widget_init(WIDGET(lv), parent, WIDGET_TYPE_LISTVIEW, name);
795 lv->listview_name = g_strdup(name);
802 // Maximum of 128 'line height'. To stop passing huge numbers, causing large
803 // stack size.
804 lv->eh = MIN(eh, 128);
805
806 lv->emode = PANGO_ELLIPSIZE_END;
807 lv->scrollbar = scrollbar_create(WIDGET(lv), "scrollbar");
808 // Calculate height of an element.
809 //
810 _listview_row row;
811 listview_create_row(lv, &row);
812 // FIXME: hack to scale hight correctly.
813 if (lv->eh > 1 && row.textbox) {
814 char buff[lv->eh * 2 + 1];
815 memset(buff, '\0', lv->eh * 2 + 1);
816 for (unsigned int i = 0; i < (lv->eh - 1); i++) {
817 buff[i * 2] = 'a';
818 buff[i * 2 + 1] = '\n';
819 };
820 textbox_moveresize(row.textbox, 0, 0, 100000000, -1);
821 textbox_text(row.textbox, buff);
822 }
823 // Make textbox very wide.
825 widget_free(WIDGET(row.box));
826
827 lv->callback = cb;
828 lv->udata = udata;
829
830 lv->page_callback = page_cb;
831
832 // Some settings.
834 lv->menu_columns =
836 lv->menu_lines =
838 lv->fixed_num_lines = rofi_theme_get_boolean(WIDGET(lv), "fixed-height",
839 config.fixed_num_lines);
840 lv->dynamic = rofi_theme_get_boolean(WIDGET(lv), "dynamic", TRUE);
841 lv->reverse = rofi_theme_get_boolean(WIDGET(lv), "reverse", reverse);
842 lv->pack_direction =
844 lv->cycle = rofi_theme_get_boolean(WIDGET(lv), "cycle", config.cycle);
845 lv->fixed_columns =
846 rofi_theme_get_boolean(WIDGET(lv), "fixed-columns", FALSE);
847
848 lv->require_input =
849 rofi_theme_get_boolean(WIDGET(lv), "require-input", FALSE);
850 lv->type = rofi_theme_get_orientation(WIDGET(lv), "layout",
852 if (lv->type == LISTVIEW) {
854 lv, rofi_theme_get_boolean(WIDGET(lv), "scrollbar", FALSE));
855 } else {
857 }
858 return lv;
859}
860
864
866 if (lv == NULL) {
867 return;
868 }
869 if (lv->req_elements == 0 || (lv->selected == 0 && !lv->cycle)) {
870 return;
871 }
872 if (lv->selected == 0) {
873 lv->selected = lv->req_elements;
874 }
875 lv->selected--;
877
878 if (lv->sc_callback) {
879 lv->sc_callback(lv, lv->selected, lv->sc_udata);
880 }
882}
884 if (lv == NULL) {
885 return;
886 }
887 if (lv->req_elements == 0 ||
888 (lv->selected == (lv->req_elements - 1) && !lv->cycle)) {
889 return;
890 }
891 lv->selected = lv->selected < lv->req_elements - 1
892 ? MIN(lv->req_elements - 1, lv->selected + 1)
893 : 0;
895 if (lv->sc_callback) {
896 lv->sc_callback(lv, lv->selected, lv->sc_udata);
897 }
899}
901 if (lv == NULL) {
902 return;
903 }
905}
907 if (lv == NULL) {
908 return;
909 }
911}
912
914 if (lv->selected >= lv->cur_columns) {
915 lv->selected -= lv->cur_columns;
916 if (lv->sc_callback) {
917 lv->sc_callback(lv, lv->selected, lv->sc_udata);
918 }
920 }
921}
923 if ((lv->selected + lv->cur_columns) < lv->req_elements) {
924 lv->selected += lv->cur_columns;
925 if (lv->sc_callback) {
926 lv->sc_callback(lv, lv->selected, lv->sc_udata);
927 }
929 }
930}
931
933 if (lv == NULL) {
934 return;
935 }
937 if (lv->reverse) {
939 } else {
941 }
942 return;
943 }
944 if (lv->reverse) {
946 } else {
948 }
949}
951 if (lv == NULL) {
952 return;
953 }
955 if (lv->reverse) {
957 } else {
959 }
960 return;
961 }
962 if (lv->reverse) {
964 } else {
966 }
967}
968
970 if (lv == NULL) {
971 return;
972 }
973 if (lv->max_rows == 0) {
974 return;
975 }
978 return;
979 }
980 if (lv->type == BARVIEW) {
982 return;
983 }
984 if (lv->selected >= lv->max_rows) {
985 lv->selected -= lv->max_rows;
986 if (lv->sc_callback) {
987 lv->sc_callback(lv, lv->selected, lv->sc_udata);
988 }
990 }
991}
993 if (lv == NULL) {
994 return;
995 }
996 if (lv->max_rows == 0) {
997 return;
998 }
1001 return;
1002 }
1003 if (lv->type == BARVIEW) {
1005 return;
1006 }
1007 if ((lv->selected + lv->max_rows) < lv->req_elements) {
1008 lv->selected += lv->max_rows;
1009 if (lv->sc_callback) {
1010 lv->sc_callback(lv, lv->selected, lv->sc_udata);
1011 }
1013 } else if (lv->selected < (lv->req_elements - 1)) {
1014 // We do not want to move to last item, UNLESS the last column is only
1015 // partially filled, then we still want to move column and select last
1016 // entry. First check the column we are currently in.
1017 int col = lv->selected / lv->max_rows;
1018 // Check total number of columns.
1019 int ncol = lv->req_elements / lv->max_rows;
1020 // If there is an extra column, move.
1021 if (col != ncol) {
1022 lv->selected = lv->req_elements - 1;
1023 if (lv->sc_callback) {
1024 lv->sc_callback(lv, lv->selected, lv->sc_udata);
1025 }
1027 }
1028 }
1029}
1030
1032 if (lv == NULL) {
1033 return;
1034 }
1035 if (lv->type == BARVIEW) {
1036 if (lv->last_offset == 0) {
1037 lv->selected = 0;
1038 } else {
1039 lv->selected = lv->last_offset - 1;
1040 }
1043 return;
1044 }
1045
1046 if (lv->selected < lv->max_elements) {
1047 lv->selected = 0;
1048 } else {
1049 lv->selected -= (lv->max_elements);
1050 }
1051 if (lv->sc_callback) {
1052 lv->sc_callback(lv, lv->selected, lv->sc_udata);
1053 }
1055}
1057 if (lv == NULL) {
1058 return;
1059 }
1060 if (lv->req_elements == 0) {
1061 return;
1062 }
1063 if (lv->type == BARVIEW) {
1064 unsigned int new = lv->last_offset + lv->barview.cur_visible;
1065 lv->selected = MIN(new, lv->req_elements - 1);
1067
1068 if (lv->sc_callback) {
1069 lv->sc_callback(lv, lv->selected, lv->sc_udata);
1070 }
1072 return;
1073 }
1074 lv->selected += (lv->max_elements);
1075 if (lv->selected >= lv->req_elements) {
1076 lv->selected = lv->req_elements - 1;
1077 }
1078 if (lv->sc_callback) {
1079 lv->sc_callback(lv, lv->selected, lv->sc_udata);
1080 }
1082}
1083
1085 if (lv == NULL) {
1086 return;
1087 }
1088 if (lv->reverse) {
1090 } else {
1092 }
1093}
1095 if (lv == NULL) {
1096 return;
1097 }
1098 if (lv->reverse) {
1100 } else {
1102 }
1103}
1104
1106 G_GNUC_UNUSED const int width) {
1107 listview *lv = (listview *)wid;
1108 if (lv == NULL || lv->widget.enabled == FALSE) {
1109 return 0;
1110 }
1112 int h = lv->menu_lines;
1113 if (!(lv->fixed_num_lines)) {
1114 if (lv->dynamic) {
1115 h = MIN(lv->menu_lines, lv->req_elements);
1116 } else {
1117 h = MIN(lv->menu_lines, lv->max_displayed_lines);
1118 }
1119 }
1120 if (lv->type == BARVIEW) {
1121 h = MIN(h, 1);
1122 }
1123 if (h == 0) {
1124 if (lv->dynamic && !lv->fixed_num_lines) {
1125 // Hide widget fully.
1126 return 0;
1127 }
1129 }
1130 int height = widget_padding_get_padding_height(WIDGET(lv));
1131 height += h * (lv->element_height + spacing) - spacing;
1132 return height;
1133}
1134
1135void listview_set_show_scrollbar(listview *lv, gboolean enabled) {
1136 if (lv) {
1137 if (enabled) {
1139 } else {
1141 }
1143 }
1144}
1145
1147 if (lv) {
1148 lv->scroll_type = type;
1149 }
1150}
1151
1154 void *udata) {
1155 if (lv) {
1156 lv->mouse_activated = cb;
1157 lv->mouse_activated_data = udata;
1158 }
1159}
1160
1161void listview_set_max_lines(listview *lv, unsigned int max_lines) {
1162 if (lv) {
1163 lv->max_displayed_lines = max_lines;
1164 }
1165}
1166
1168 if (lv) {
1169 return lv->fixed_num_lines;
1170 }
1171 return FALSE;
1172}
1174 if (lv) {
1175 lv->fixed_num_lines = TRUE;
1176 }
1177}
1178
1179void listview_set_ellipsize(listview *lv, PangoEllipsizeMode mode) {
1180 if (lv) {
1181 lv->emode = mode;
1182 for (unsigned int i = 0; i < lv->cur_elements; i++) {
1184 }
1185 }
1186}
1187
1189 if (lv) {
1190 PangoEllipsizeMode mode = lv->emode;
1191 if (mode == PANGO_ELLIPSIZE_START) {
1192 mode = PANGO_ELLIPSIZE_MIDDLE;
1193 } else if (mode == PANGO_ELLIPSIZE_MIDDLE) {
1194 mode = PANGO_ELLIPSIZE_END;
1195 } else if (mode == PANGO_ELLIPSIZE_END) {
1196 mode = PANGO_ELLIPSIZE_START;
1197 }
1198 lv->emode = mode;
1199 for (unsigned int i = 0; i < lv->cur_elements; i++) {
1200 textbox_set_ellipsize(lv->boxes[i].textbox, mode);
1201 }
1202 }
1203}
1204
1205void listview_set_filtered(listview *lv, gboolean filtered) {
1206 if (lv) {
1207 lv->filtered = filtered;
1208 }
1209}
1210
1212 listview *lv, listview_selection_changed_callback cb, void *udata) {
1213 lv->sc_callback = cb;
1214 lv->sc_udata = udata;
1215}
#define DEFAULT_SPACING
Definition box.c:38
#define rofi_fallthrough
Definition helper.h:540
MouseBindingListviewElementAction
Definition keyb.h:167
MouseBindingListviewAction
Definition keyb.h:157
@ ACCEPT_HOVERED_ENTRY
Definition keyb.h:169
@ ACCEPT_HOVERED_CUSTOM
Definition keyb.h:170
@ SELECT_HOVERED_ENTRY
Definition keyb.h:168
@ SCROLL_LEFT
Definition keyb.h:158
@ SCROLL_DOWN
Definition keyb.h:160
@ SCROLL_RIGHT
Definition keyb.h:159
@ SCROLL_UP
Definition keyb.h:161
void scrollbar_set_max_value(scrollbar *sb, unsigned int max)
Definition scrollbar.c:133
scrollbar * scrollbar_create(widget *parent, const char *name)
Definition scrollbar.c:104
void scrollbar_set_handle(scrollbar *sb, unsigned int pos)
Definition scrollbar.c:139
void scrollbar_set_handle_length(scrollbar *sb, unsigned int pos_length)
Definition scrollbar.c:145
#define TICK_N(a)
Definition timings.h:69
TextBoxFontType
Definition textbox.h:103
void textbox_set_ellipsize(textbox *tb, PangoEllipsizeMode mode)
Definition textbox.c:1150
textbox * textbox_create(widget *parent, WidgetType type, const char *name, TextboxFlags flags, TextBoxFontType tbft, const char *text, double xalign, double yalign)
Definition textbox.c:204
void textbox_moveresize(textbox *tb, int x, int y, int w, int h)
Definition textbox.c:447
void textbox_text(textbox *tb, const char *text)
Definition textbox.c:412
@ TB_AUTOHEIGHT
Definition textbox.h:93
@ TB_WRAP
Definition textbox.h:97
@ URGENT
Definition textbox.h:107
@ ACTIVE
Definition textbox.h:109
@ HIGHLIGHT
Definition textbox.h:118
@ NORMAL
Definition textbox.h:105
@ STATE_MASK
Definition textbox.h:122
@ ALT
Definition textbox.h:116
@ FMOD_MASK
Definition textbox.h:120
WidgetTriggerActionResult textbox_button_trigger_action(widget *wid, MouseBindingMouseDefaultAction action, G_GNUC_UNUSED gint x, G_GNUC_UNUSED gint y, G_GNUC_UNUSED void *user_data)
Definition view.c:1584
void box_add(box *wid, widget *child, gboolean expand)
Definition box.c:287
box * box_create(widget *parent, const char *name, RofiOrientation type)
Definition box.c:347
struct _box box
Definition box.h:49
icon * icon_create(widget *parent, const char *name)
Definition icon.c:161
struct _icon icon
Definition icon.h:44
void listview_nav_page_next(listview *lv)
Definition listview.c:1094
void listview_set_fixed_num_lines(listview *lv)
Definition listview.c:1173
struct _listview listview
Definition listview.h:45
listview * listview_create(widget *parent, const char *name, listview_update_callback cb, listview_page_changed_cb page_cb, void *udata, unsigned int eh, gboolean reverse)
Definition listview.c:789
void listview_set_show_scrollbar(listview *lv, gboolean enabled)
Definition listview.c:1135
void listview_set_num_elements(listview *lv, unsigned int rows)
Definition listview.c:614
void listview_nav_right(listview *lv)
Definition listview.c:992
void listview_set_mouse_activated_cb(listview *lv, listview_mouse_activated_cb cb, void *udata)
Definition listview.c:1152
void listview_toggle_ellipsizing(listview *lv)
Definition listview.c:1188
void listview_set_ellipsize(listview *lv, PangoEllipsizeMode mode)
Definition listview.c:1179
void listview_set_selected(listview *lv, unsigned int selected)
Definition listview.c:651
void listview_set_max_lines(listview *lv, unsigned int max_lines)
Definition listview.c:1161
void listview_nav_left(listview *lv)
Definition listview.c:969
void listview_set_scroll_type(listview *lv, ScrollType type)
Definition listview.c:1146
void(* listview_selection_changed_callback)(listview *lv, unsigned int index, void *udata)
Definition listview.h:79
gboolean listview_get_fixed_num_lines(listview *lv)
Definition listview.c:1167
void listview_nav_prev(listview *lv)
Definition listview.c:906
unsigned int listview_get_selected(listview *lv)
Definition listview.c:631
ScrollType
Definition listview.h:50
void(* listview_page_changed_cb)(void)
Definition listview.h:91
void(* listview_mouse_activated_cb)(listview *, gboolean, void *)
Definition listview.h:85
void(* listview_update_callback)(textbox *tb, icon *ico, unsigned int entry, void *udata, TextBoxFontType *type, gboolean full)
Definition listview.h:68
void listview_set_filtered(listview *lv, gboolean filtered)
Definition listview.c:1205
void listview_nav_up(listview *lv)
Definition listview.c:932
void listview_nav_next(listview *lv)
Definition listview.c:900
void listview_nav_page_prev(listview *lv)
Definition listview.c:1084
unsigned int listview_get_element_pitch(listview *lv, RofiOrientation orientation)
Definition listview.c:638
void listview_set_selection_changed_callback(listview *lv, listview_selection_changed_callback cb, void *udata)
Definition listview.c:1211
void listview_nav_down(listview *lv)
Definition listview.c:950
@ LISTVIEW_SCROLL_PER_PAGE
Definition listview.h:52
void widget_queue_redraw(widget *wid)
Definition widget.c:487
void widget_move(widget *wid, short x, short y)
Definition widget.c:107
static void widget_enable(widget *wid)
Definition widget.h:176
void widget_free(widget *wid)
Definition widget.c:425
void widget_draw(widget *wid, cairo_t *d)
Definition widget.c:140
int widget_get_width(widget *wid)
Definition widget.c:446
struct _widget widget
Definition widget.h:49
int widget_get_y_pos(widget *wid)
Definition widget.c:461
void widget_set_type(widget *wid, WidgetType type)
Definition widget.c:114
WidgetType
Definition widget.h:54
int widget_get_desired_width(widget *wid, const int height)
Definition widget.c:653
int widget_get_x_pos(widget *wid)
Definition widget.c:455
void widget_resize(widget *wid, short w, short h)
Definition widget.c:92
#define WIDGET(a)
Definition widget.h:117
static void widget_disable(widget *wid)
Definition widget.h:168
WidgetTriggerActionResult
Definition widget.h:74
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
gboolean widget_enabled(widget *wid)
Definition widget.c:121
int widget_intersect(const widget *wid, int x, int y)
Definition widget.c:80
widget * widget_find_mouse_target(widget *wid, WidgetType type, gint x, gint y)
Definition widget.c:510
@ WIDGET_TYPE_LISTVIEW_ELEMENT
Definition widget.h:60
@ WIDGET_TYPE_TEXTBOX_TEXT
Definition widget.h:68
@ WIDGET_TYPE_EDITBOX
Definition widget.h:62
@ WIDGET_TYPE_LISTVIEW
Definition widget.h:58
@ WIDGET_TRIGGER_ACTION_RESULT_HANDLED
Definition widget.h:78
@ WIDGET_TRIGGER_ACTION_RESULT_IGNORED
Definition widget.h:76
MoveDirection
Definition listview.c:59
@ RIGHT_TO_LEFT
Definition listview.c:59
@ LEFT_TO_RIGHT
Definition listview.c:59
static void listview_nav_column_right_int(listview *lv)
Definition listview.c:922
static unsigned int scroll_per_page(listview *lv)
Definition listview.c:276
static void listview_nav_up_int(listview *lv)
Definition listview.c:865
static unsigned int scroll_per_page_barview(listview *lv)
Definition listview.c:262
#define BARVIEW
Definition listview.c:53
const char *const listview_theme_prop_names[][3]
Definition listview.c:141
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:1056
static void listview_resize(widget *wid, short w, short h)
Definition listview.c:669
static widget * listview_find_mouse_target(widget *wid, WidgetType type, gint x, gint y)
Definition listview.c:699
static void _listview_draw(widget *wid, cairo_t *draw)
Definition listview.c:563
static void listview_nav_page_prev_int(listview *lv)
Definition listview.c:1031
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:726
static void listview_recompute_elements(listview *lv)
Definition listview.c:574
static void listview_add_widget(listview *lv, _listview_row *row, widget *wid, const char *label)
Definition listview.c:172
static unsigned int scroll_continious_elements(listview *lv)
Definition listview.c:304
static unsigned int scroll_continious_rows(listview *lv)
Definition listview.c:327
static void barview_draw(widget *wid, cairo_t *draw)
Definition listview.c:372
static void listview_free(widget *wid)
Definition listview.c:251
static void listview_nav_column_left_int(listview *lv)
Definition listview.c:913
static void listview_nav_down_int(listview *lv)
Definition listview.c:883
#define LISTVIEW
Definition listview.c:51
static void listview_create_row(listview *lv, _listview_row *row)
Definition listview.c:224
static void listview_draw(widget *wid, cairo_t *draw)
Definition listview.c:452
static int listview_get_desired_height(widget *wid, const int width)
static void update_element(listview *lv, unsigned int tb, unsigned int index, gboolean full)
Definition listview.c:351
static void listview_set_state(_listview_row r, TextBoxFontType tbft)
Definition listview.c:150
RofiOrientation
Definition rofi-types.h:139
@ ROFI_ORIENTATION_HORIZONTAL
Definition rofi-types.h:141
@ ROFI_ORIENTATION_VERTICAL
Definition rofi-types.h:140
Settings config
#define DEFAULT_MENU_LINES
Definition settings.h:231
#define DEFAULT_MENU_COLUMNS
Definition settings.h:233
textbox * index
Definition listview.c:64
icon * icon
Definition listview.c:65
textbox * textbox
Definition listview.c:63
unsigned int cur_columns
Definition listview.c:91
void * mouse_activated_data
Definition listview.c:123
gboolean cycle
Definition listview.c:106
unsigned int rchanged
Definition listview.c:75
unsigned int max_rows
Definition listview.c:86
unsigned int cur_page
Definition listview.c:81
unsigned int menu_columns
Definition listview.c:98
struct _listview::@210125353307244200152210332376147172177312067320 barview
unsigned int max_displayed_lines
Definition listview.c:97
widget widget
Definition listview.c:69
void * udata
Definition listview.c:114
PangoEllipsizeMode emode
Definition listview.c:129
unsigned int cur_visible
Definition listview.c:133
listview_update_callback callback
Definition listview.c:113
listview_mouse_activated_cb mouse_activated
Definition listview.c:122
void * sc_udata
Definition listview.c:117
unsigned int req_elements
Definition listview.c:92
unsigned int element_height
Definition listview.c:85
unsigned int last_offset
Definition listview.c:82
unsigned int cur_elements
Definition listview.c:93
unsigned int dynamic
Definition listview.c:100
guint32 last_click
Definition listview.c:121
listview_page_changed_cb page_callback
Definition listview.c:125
MoveDirection direction
Definition listview.c:132
char * listview_name
Definition listview.c:127
gboolean filtered
Definition listview.c:104
scrollbar * scrollbar
Definition listview.c:111
unsigned int max_elements
Definition listview.c:87
gboolean require_input
Definition listview.c:103
unsigned int fixed_num_lines
Definition listview.c:99
unsigned int reverse
Definition listview.c:102
listview_selection_changed_callback sc_callback
Definition listview.c:116
RofiDistance spacing
Definition listview.c:95
gboolean fixed_columns
Definition listview.c:90
RofiOrientation type
Definition listview.c:71
unsigned int selected
Definition listview.c:83
unsigned int eh
Definition listview.c:101
ScrollType scroll_type
Definition listview.c:108
gboolean scrollbar_scroll
Definition listview.c:119
RofiOrientation pack_direction
Definition listview.c:78
_listview_row * boxes
Definition listview.c:110
unsigned int menu_lines
Definition listview.c:96
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)
int rofi_theme_get_integer(const widget *wid, const char *property, int def)
Definition theme.c:842
int distance_get_pixel(RofiDistance d, RofiOrientation ori)
Definition theme.c:1404
int rofi_theme_get_boolean(const widget *wid, const char *property, int def)
Definition theme.c:903
RofiOrientation rofi_theme_get_orientation(const widget *wid, const char *property, RofiOrientation def)
Definition theme.c:931
RofiDistance rofi_theme_get_distance(const widget *wid, const char *property, int def)
Definition theme.c:877
GList * rofi_theme_get_list_strings(const widget *wid, const char *property)
Definition theme.c:1260
const char * rofi_theme_get_string(const widget *wid, const char *property, const char *def)
Definition theme.c:989
void widget_set_state(widget *wid, const char *state)
Definition widget.c:63
void widget_init(widget *wid, widget *parent, WidgetType type, const char *name)
Definition widget.c:36
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