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