rofi 1.7.9
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 xcb_timestamp_t 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 d -= lv->cur_columns - 1;
502 }
503 }
504 for (unsigned int i = 0; i < max; i++) {
506 unsigned int ex = left_offset + ((i) % lv->cur_columns) *
507 (element_width + spacing_hori);
508 unsigned int ey = 0;
509 if (lv->reverse) {
510 ey = wid->h -
512 ((i) / lv->cur_columns) *
513 (lv->element_height + spacing_vert)) -
514 lv->element_height;
515
516 if ((i) / lv->cur_columns == (lv->cur_columns - 1)) {
517 ex += d;
518 }
519 } else {
520 ey = top_offset +
521 ((i) / lv->cur_columns) * (lv->element_height + spacing_vert);
522
523 if ((i) / lv->cur_columns == (lv->cur_columns - 1)) {
524 ex += d;
525 }
526 }
527 widget_move(WIDGET(lv->boxes[i].box), ex, ey);
528 widget_resize(WIDGET(lv->boxes[i].box), element_width,
529 lv->element_height);
530
531 } else {
532 unsigned int ex = left_offset + ((i) / lv->max_rows) *
533 (element_width + spacing_hori);
534
535 if ((i) / lv->max_rows == (lv->cur_columns - 1)) {
536 ex += d;
537 }
538 unsigned int ey = 0;
539 if (lv->reverse) {
540 ey = wid->h -
542 ((i) % lv->max_rows) * (lv->element_height + spacing_vert)) -
543 lv->element_height;
544 } else {
545 ey = top_offset +
546 ((i) % lv->max_rows) * (lv->element_height + spacing_vert);
547 }
548 widget_move(WIDGET(lv->boxes[i].box), ex, ey);
549 widget_resize(WIDGET(lv->boxes[i].box), element_width,
550 lv->element_height);
551 }
552 update_element(lv, i, i + offset, TRUE);
553 widget_draw(WIDGET(lv->boxes[i].box), draw);
554 }
555 lv->rchanged = FALSE;
556 } else {
557 for (unsigned int i = 0; i < max; i++) {
558 update_element(lv, i, i + offset, TRUE);
559 widget_draw(WIDGET(lv->boxes[i].box), draw);
560 }
561 }
562 }
563 widget_draw(WIDGET(lv->scrollbar), draw);
564}
568 gint x, gint y, void *user_data);
569static gboolean listview_element_motion_notify(widget *wid, gint x, gint y);
570
571static void _listview_draw(widget *wid, cairo_t *draw) {
572 listview *lv = (listview *)wid;
573 if (lv->type == LISTVIEW) {
574 listview_draw(wid, draw);
575 } else {
576 barview_draw(wid, draw);
577 }
578}
579
583 unsigned int newne = 0;
584 if (lv->max_rows == 0) {
585 return;
586 }
587 if (!(lv->fixed_columns) && lv->req_elements < lv->max_elements) {
588 newne = lv->req_elements;
590 lv->cur_columns = (lv->req_elements + (lv->max_rows - 1)) / lv->max_rows;
591 } else {
592 lv->cur_columns = lv->menu_columns;
593 if (lv->req_elements < lv->menu_columns) {
594 lv->cur_columns = lv->req_elements;
595 }
596 }
597 } else {
598 newne = MIN(lv->req_elements, lv->max_elements);
599 lv->cur_columns = lv->menu_columns;
600 }
601 for (unsigned int i = newne; i < lv->cur_elements; i++) {
602 widget_free(WIDGET(lv->boxes[i].box));
603 }
604 lv->boxes = g_realloc(lv->boxes, newne * sizeof(_listview_row));
605 if (newne > 0) {
606 for (unsigned int i = lv->cur_elements; i < newne; i++) {
607 listview_create_row(lv, &(lv->boxes[i]));
608 widget *wid = WIDGET(lv->boxes[i].box);
610 lv);
611 if (wid != NULL) {
613 }
614
616 }
617 }
618 lv->rchanged = TRUE;
619 lv->cur_elements = newne;
620}
621
622void listview_set_num_elements(listview *lv, unsigned int rows) {
623 if (lv == NULL) {
624 return;
625 }
626 TICK_N("listview_set_num_elements");
627 lv->req_elements = rows;
628 if (lv->require_input && !lv->filtered) {
629 lv->req_elements = 0;
630 }
632 TICK_N("Set selected");
634 TICK_N("recompute elements");
636 TICK_N("queue redraw");
637}
638
640 if (lv != NULL) {
641 return lv->selected;
642 }
643 return 0;
644}
645
646void listview_set_selected(listview *lv, unsigned int selected) {
647 if (lv == NULL) {
648 return;
649 }
650 if (lv->req_elements > 0) {
651 lv->selected = MIN(selected, lv->req_elements - 1);
654 if (lv->sc_callback) {
655 lv->sc_callback(lv, lv->selected, lv->sc_udata);
656 }
657 } else {
658 if (lv->sc_callback) {
659 lv->sc_callback(lv, UINT32_MAX, lv->sc_udata);
660 }
661 }
662}
663
664static void listview_resize(widget *wid, short w, short h) {
665 listview *lv = (listview *)wid;
666 lv->widget.w = MAX(0, w);
667 lv->widget.h = MAX(0, h);
668 int height = lv->widget.h - widget_padding_get_padding_height(WIDGET(lv));
670 if (lv->widget.h == 0) {
671 lv->max_rows = lv->menu_lines;
672 } else {
673 lv->max_rows =
674 (spacing_vert + height) / (lv->element_height + spacing_vert);
675 }
676 lv->max_elements = lv->max_rows * lv->menu_columns;
677
682
684 height);
685
686 if (lv->type == BARVIEW) {
687 lv->max_elements = lv->menu_lines;
688 }
689
692}
693
695 gint y) {
696 widget *target = NULL;
697 gint rx, ry;
698 listview *lv = (listview *)wid;
699 if (widget_enabled(WIDGET(lv->scrollbar)) &&
700 widget_intersect(WIDGET(lv->scrollbar), x, y)) {
701 rx = x - widget_get_x_pos(WIDGET(lv->scrollbar));
702 ry = y - widget_get_y_pos(WIDGET(lv->scrollbar));
703 target = widget_find_mouse_target(WIDGET(lv->scrollbar), type, rx, ry);
704 }
705
706 unsigned int max = MIN(lv->cur_elements, lv->req_elements - lv->last_offset);
707 unsigned int i;
708 for (i = 0; i < max && target == NULL; i++) {
709 widget *w = WIDGET(lv->boxes[i].box);
710 if (widget_intersect(w, x, y)) {
711 rx = x - widget_get_x_pos(w);
712 ry = y - widget_get_y_pos(w);
713 target = widget_find_mouse_target(w, type, rx, ry);
714 }
715 }
716
717 return target;
718}
719
722 G_GNUC_UNUSED gint x, G_GNUC_UNUSED gint y,
723 G_GNUC_UNUSED void *user_data) {
724 listview *lv = (listview *)wid;
725 switch (action) {
726 case SCROLL_LEFT:
728 break;
729 case SCROLL_RIGHT:
731 break;
732 case SCROLL_DOWN:
734 break;
735 case SCROLL_UP:
736 listview_nav_up(lv);
737 break;
738 }
740}
741
743 widget *wid, MouseBindingListviewElementAction action, G_GNUC_UNUSED gint x,
744 G_GNUC_UNUSED gint y, void *user_data) {
745 listview *lv = (listview *)user_data;
746 unsigned int max = MIN(lv->cur_elements, lv->req_elements - lv->last_offset);
747 unsigned int i;
748 for (i = 0; i < max && WIDGET(lv->boxes[i].box) != wid; i++) {
749 }
750 if (i == max) {
752 }
753
754 gboolean custom = FALSE;
755 switch (action) {
758 break;
760 custom = TRUE;
764 lv->mouse_activated(lv, custom, lv->mouse_activated_data);
765 break;
766 }
768}
769
771 G_GNUC_UNUSED gint x,
772 G_GNUC_UNUSED gint y) {
773 listview *lv = (listview *)wid->parent;
774 unsigned int max = MIN(lv->cur_elements, lv->req_elements - lv->last_offset);
775 unsigned int i;
776 for (i = 0; i < max && WIDGET(lv->boxes[i].box) != wid; i++) {
777 }
778 if (i < max && (lv->last_offset + i) != listview_get_selected(lv)) {
780 }
781 return TRUE;
782}
783
784listview *listview_create(widget *parent, const char *name,
786 listview_page_changed_cb page_cb, void *udata,
787 unsigned int eh, gboolean reverse) {
788 listview *lv = g_malloc0(sizeof(listview));
789 widget_init(WIDGET(lv), parent, WIDGET_TYPE_LISTVIEW, name);
790 lv->listview_name = g_strdup(name);
797 lv->eh = eh;
798
799 lv->emode = PANGO_ELLIPSIZE_END;
800 lv->scrollbar = scrollbar_create(WIDGET(lv), "scrollbar");
801 // Calculate height of an element.
802 //
803 _listview_row row;
804 listview_create_row(lv, &row);
805 // FIXME: hack to scale hight correctly.
806 if (lv->eh > 1 && row.textbox) {
807 char buff[lv->eh * 2 + 1];
808 memset(buff, '\0', lv->eh * 2 + 1);
809 for (unsigned int i = 0; i < (lv->eh - 1); i++) {
810 buff[i * 2] = 'a';
811 buff[i * 2 + 1] = '\n';
812 };
813 textbox_moveresize(row.textbox, 0, 0, 100000000, -1);
814 textbox_text(row.textbox, buff);
815 }
816 // Make textbox very wide.
818 widget_free(WIDGET(row.box));
819
820 lv->callback = cb;
821 lv->udata = udata;
822
823 lv->page_callback = page_cb;
824
825 // Some settings.
827 lv->menu_columns =
829 lv->menu_lines =
831 lv->fixed_num_lines = rofi_theme_get_boolean(WIDGET(lv), "fixed-height",
832 config.fixed_num_lines);
833 lv->dynamic = rofi_theme_get_boolean(WIDGET(lv), "dynamic", TRUE);
834 lv->reverse = rofi_theme_get_boolean(WIDGET(lv), "reverse", reverse);
835 lv->pack_direction =
837 lv->cycle = rofi_theme_get_boolean(WIDGET(lv), "cycle", config.cycle);
838 lv->fixed_columns =
839 rofi_theme_get_boolean(WIDGET(lv), "fixed-columns", FALSE);
840
841 lv->require_input =
842 rofi_theme_get_boolean(WIDGET(lv), "require-input", FALSE);
843 lv->type = rofi_theme_get_orientation(WIDGET(lv), "layout",
845 if (lv->type == LISTVIEW) {
847 lv, rofi_theme_get_boolean(WIDGET(lv), "scrollbar", FALSE));
848 } else {
850 }
851 return lv;
852}
853
857
859 if (lv == NULL) {
860 return;
861 }
862 if (lv->req_elements == 0 || (lv->selected == 0 && !lv->cycle)) {
863 return;
864 }
865 if (lv->selected == 0) {
866 lv->selected = lv->req_elements;
867 }
868 lv->selected--;
870
871 if (lv->sc_callback) {
872 lv->sc_callback(lv, lv->selected, lv->sc_udata);
873 }
875}
877 if (lv == NULL) {
878 return;
879 }
880 if (lv->req_elements == 0 ||
881 (lv->selected == (lv->req_elements - 1) && !lv->cycle)) {
882 return;
883 }
884 lv->selected = lv->selected < lv->req_elements - 1
885 ? MIN(lv->req_elements - 1, lv->selected + 1)
886 : 0;
888 if (lv->sc_callback) {
889 lv->sc_callback(lv, lv->selected, lv->sc_udata);
890 }
892}
894 if (lv == NULL) {
895 return;
896 }
898}
900 if (lv == NULL) {
901 return;
902 }
904}
905
907 if (lv->selected >= lv->cur_columns) {
908 lv->selected -= lv->cur_columns;
909 if (lv->sc_callback) {
910 lv->sc_callback(lv, lv->selected, lv->sc_udata);
911 }
913 }
914}
916 if ((lv->selected + lv->cur_columns) < lv->req_elements) {
917 lv->selected += lv->cur_columns;
918 if (lv->sc_callback) {
919 lv->sc_callback(lv, lv->selected, lv->sc_udata);
920 }
922 }
923}
924
926 if (lv == NULL) {
927 return;
928 }
930 if (lv->reverse) {
932 } else {
934 }
935 return;
936 }
937 if (lv->reverse) {
939 } else {
941 }
942}
944 if (lv == NULL) {
945 return;
946 }
948 if (lv->reverse) {
950 } else {
952 }
953 return;
954 }
955 if (lv->reverse) {
957 } else {
959 }
960}
961
963 if (lv == NULL) {
964 return;
965 }
966 if (lv->max_rows == 0) {
967 return;
968 }
971 return;
972 }
973 if (lv->type == BARVIEW) {
975 return;
976 }
977 if (lv->selected >= lv->max_rows) {
978 lv->selected -= lv->max_rows;
979 if (lv->sc_callback) {
980 lv->sc_callback(lv, lv->selected, lv->sc_udata);
981 }
983 }
984}
986 if (lv == NULL) {
987 return;
988 }
989 if (lv->max_rows == 0) {
990 return;
991 }
994 return;
995 }
996 if (lv->type == BARVIEW) {
998 return;
999 }
1000 if ((lv->selected + lv->max_rows) < lv->req_elements) {
1001 lv->selected += lv->max_rows;
1002 if (lv->sc_callback) {
1003 lv->sc_callback(lv, lv->selected, lv->sc_udata);
1004 }
1006 } else if (lv->selected < (lv->req_elements - 1)) {
1007 // We do not want to move to last item, UNLESS the last column is only
1008 // partially filled, then we still want to move column and select last
1009 // entry. First check the column we are currently in.
1010 int col = lv->selected / lv->max_rows;
1011 // Check total number of columns.
1012 int ncol = lv->req_elements / lv->max_rows;
1013 // If there is an extra column, move.
1014 if (col != ncol) {
1015 lv->selected = lv->req_elements - 1;
1016 if (lv->sc_callback) {
1017 lv->sc_callback(lv, lv->selected, lv->sc_udata);
1018 }
1020 }
1021 }
1022}
1023
1025 if (lv == NULL) {
1026 return;
1027 }
1028 if (lv->type == BARVIEW) {
1029 if (lv->last_offset == 0) {
1030 lv->selected = 0;
1031 } else {
1032 lv->selected = lv->last_offset - 1;
1033 }
1036 return;
1037 }
1038
1039 if (lv->selected < lv->max_elements) {
1040 lv->selected = 0;
1041 } else {
1042 lv->selected -= (lv->max_elements);
1043 }
1044 if (lv->sc_callback) {
1045 lv->sc_callback(lv, lv->selected, lv->sc_udata);
1046 }
1048}
1050 if (lv == NULL) {
1051 return;
1052 }
1053 if (lv->req_elements == 0) {
1054 return;
1055 }
1056 if (lv->type == BARVIEW) {
1057 unsigned int new = lv->last_offset + lv->barview.cur_visible;
1058 lv->selected = MIN(new, lv->req_elements - 1);
1060
1061 if (lv->sc_callback) {
1062 lv->sc_callback(lv, lv->selected, lv->sc_udata);
1063 }
1065 return;
1066 }
1067 lv->selected += (lv->max_elements);
1068 if (lv->selected >= lv->req_elements) {
1069 lv->selected = lv->req_elements - 1;
1070 }
1071 if (lv->sc_callback) {
1072 lv->sc_callback(lv, lv->selected, lv->sc_udata);
1073 }
1075}
1076
1078 if (lv == NULL) {
1079 return;
1080 }
1081 if (lv->reverse) {
1083 } else {
1085 }
1086}
1088 if (lv == NULL) {
1089 return;
1090 }
1091 if (lv->reverse) {
1093 } else {
1095 }
1096}
1097
1099 G_GNUC_UNUSED const int width) {
1100 listview *lv = (listview *)wid;
1101 if (lv == NULL || lv->widget.enabled == FALSE) {
1102 return 0;
1103 }
1105 int h = lv->menu_lines;
1106 if (!(lv->fixed_num_lines)) {
1107 if (lv->dynamic) {
1108 h = MIN(lv->menu_lines, lv->req_elements);
1109 } else {
1110 h = MIN(lv->menu_lines, lv->max_displayed_lines);
1111 }
1112 }
1113 if (lv->type == BARVIEW) {
1114 h = MIN(h, 1);
1115 }
1116 if (h == 0) {
1117 if (lv->dynamic && !lv->fixed_num_lines) {
1118 // Hide widget fully.
1119 return 0;
1120 }
1122 }
1123 int height = widget_padding_get_padding_height(WIDGET(lv));
1124 height += h * (lv->element_height + spacing) - spacing;
1125 return height;
1126}
1127
1128void listview_set_show_scrollbar(listview *lv, gboolean enabled) {
1129 if (lv) {
1130 if (enabled) {
1132 } else {
1134 }
1136 }
1137}
1138
1140 if (lv) {
1141 lv->scroll_type = type;
1142 }
1143}
1144
1147 void *udata) {
1148 if (lv) {
1149 lv->mouse_activated = cb;
1150 lv->mouse_activated_data = udata;
1151 }
1152}
1153
1154void listview_set_max_lines(listview *lv, unsigned int max_lines) {
1155 if (lv) {
1156 lv->max_displayed_lines = max_lines;
1157 }
1158}
1159
1161 if (lv) {
1162 return lv->fixed_num_lines;
1163 }
1164 return FALSE;
1165}
1167 if (lv) {
1168 lv->fixed_num_lines = TRUE;
1169 }
1170}
1171
1172void listview_set_ellipsize(listview *lv, PangoEllipsizeMode mode) {
1173 if (lv) {
1174 lv->emode = mode;
1175 for (unsigned int i = 0; i < lv->cur_elements; i++) {
1177 }
1178 }
1179}
1180
1182 if (lv) {
1183 PangoEllipsizeMode mode = lv->emode;
1184 if (mode == PANGO_ELLIPSIZE_START) {
1185 mode = PANGO_ELLIPSIZE_MIDDLE;
1186 } else if (mode == PANGO_ELLIPSIZE_MIDDLE) {
1187 mode = PANGO_ELLIPSIZE_END;
1188 } else if (mode == PANGO_ELLIPSIZE_END) {
1189 mode = PANGO_ELLIPSIZE_START;
1190 }
1191 lv->emode = mode;
1192 for (unsigned int i = 0; i < lv->cur_elements; i++) {
1193 textbox_set_ellipsize(lv->boxes[i].textbox, mode);
1194 }
1195 }
1196}
1197
1198void listview_set_filtered(listview *lv, gboolean filtered) {
1199 if (lv) {
1200 lv->filtered = filtered;
1201 }
1202}
1203
1205 listview *lv, listview_selection_changed_callback cb, void *udata) {
1206 lv->sc_callback = cb;
1207 lv->sc_udata = udata;
1208}
#define DEFAULT_SPACING
Definition box.c:38
#define rofi_fallthrough
Definition helper.h:466
MouseBindingListviewElementAction
Definition keyb.h:165
MouseBindingListviewAction
Definition keyb.h:155
@ ACCEPT_HOVERED_ENTRY
Definition keyb.h:167
@ ACCEPT_HOVERED_CUSTOM
Definition keyb.h:168
@ SELECT_HOVERED_ENTRY
Definition keyb.h:166
@ SCROLL_LEFT
Definition keyb.h:156
@ SCROLL_DOWN
Definition keyb.h:158
@ SCROLL_RIGHT
Definition keyb.h:157
@ SCROLL_UP
Definition keyb.h:159
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:104
void textbox_set_ellipsize(textbox *tb, PangoEllipsizeMode mode)
Definition textbox.c:1090
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:439
void textbox_text(textbox *tb, const char *text)
Definition textbox.c:404
@ TB_AUTOHEIGHT
Definition textbox.h:94
@ TB_WRAP
Definition textbox.h:98
@ URGENT
Definition textbox.h:108
@ ACTIVE
Definition textbox.h:110
@ HIGHLIGHT
Definition textbox.h:119
@ NORMAL
Definition textbox.h:106
@ STATE_MASK
Definition textbox.h:123
@ ALT
Definition textbox.h:117
@ FMOD_MASK
Definition textbox.h:121
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:2273
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:1087
void listview_set_fixed_num_lines(listview *lv)
Definition listview.c:1166
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:784
void listview_set_show_scrollbar(listview *lv, gboolean enabled)
Definition listview.c:1128
void listview_set_num_elements(listview *lv, unsigned int rows)
Definition listview.c:622
void listview_nav_right(listview *lv)
Definition listview.c:985
void listview_set_mouse_activated_cb(listview *lv, listview_mouse_activated_cb cb, void *udata)
Definition listview.c:1145
void listview_toggle_ellipsizing(listview *lv)
Definition listview.c:1181
void listview_set_ellipsize(listview *lv, PangoEllipsizeMode mode)
Definition listview.c:1172
void listview_set_selected(listview *lv, unsigned int selected)
Definition listview.c:646
void listview_set_max_lines(listview *lv, unsigned int max_lines)
Definition listview.c:1154
void listview_nav_left(listview *lv)
Definition listview.c:962
void listview_set_scroll_type(listview *lv, ScrollType type)
Definition listview.c:1139
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:1160
void listview_nav_prev(listview *lv)
Definition listview.c:899
unsigned int listview_get_selected(listview *lv)
Definition listview.c:639
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:1198
void listview_nav_up(listview *lv)
Definition listview.c:925
void listview_nav_next(listview *lv)
Definition listview.c:893
void listview_nav_page_prev(listview *lv)
Definition listview.c:1077
void listview_set_selection_changed_callback(listview *lv, listview_selection_changed_callback cb, void *udata)
Definition listview.c:1204
void listview_nav_down(listview *lv)
Definition listview.c:943
@ 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:178
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:51
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:56
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: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: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: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: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:915
static unsigned int scroll_per_page(listview *lv)
Definition listview.c:276
static void listview_nav_up_int(listview *lv)
Definition listview.c:858
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:1049
static void listview_resize(widget *wid, short w, short h)
Definition listview.c:664
static widget * listview_find_mouse_target(widget *wid, WidgetType type, gint x, gint y)
Definition listview.c:694
static void _listview_draw(widget *wid, cairo_t *draw)
Definition listview.c:571
static void listview_nav_page_prev_int(listview *lv)
Definition listview.c:1024
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:721
static void listview_recompute_elements(listview *lv)
Definition listview.c:582
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:906
static void listview_nav_down_int(listview *lv)
Definition listview.c:876
#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:214
#define DEFAULT_MENU_COLUMNS
Definition settings.h:216
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
unsigned int max_displayed_lines
Definition listview.c:97
widget widget
Definition listview.c:69
struct _listview::@321074352032300151035155160222066027301023133214 barview
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
xcb_timestamp_t last_click
Definition listview.c:121
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
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: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: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