2016-10-08 12:57:59 -04:00
|
|
|
/**
|
|
|
|
* MIT/X11 License
|
2017-01-03 11:59:28 -05:00
|
|
|
* Modified (c) 2017 Qball Cow <qball@gmpclient.org>
|
2016-10-08 12:57:59 -04:00
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
|
|
* a copy of this software and associated documentation files (the
|
|
|
|
* "Software"), to deal in the Software without restriction, including
|
|
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
|
|
* the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be
|
|
|
|
* included in all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
|
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
|
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
|
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
|
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
#include <glib.h>
|
|
|
|
#include <widgets/widget.h>
|
|
|
|
#include <widgets/textbox.h>
|
|
|
|
#include <widgets/listview.h>
|
|
|
|
#include <widgets/scrollbar.h>
|
|
|
|
|
2016-12-11 11:06:31 -05:00
|
|
|
#include "settings.h"
|
|
|
|
#include "theme.h"
|
|
|
|
|
2017-01-08 15:36:06 -05:00
|
|
|
#define DEFAULT_SPACING 2
|
2017-01-01 09:39:02 -05:00
|
|
|
|
2016-10-08 12:57:59 -04:00
|
|
|
struct _listview
|
|
|
|
{
|
|
|
|
widget widget;
|
|
|
|
// RChanged
|
|
|
|
// Text needs to be repainted.
|
|
|
|
unsigned int rchanged;
|
|
|
|
// Administration
|
|
|
|
|
|
|
|
unsigned int cur_page;
|
|
|
|
unsigned int last_offset;
|
|
|
|
unsigned int selected;
|
|
|
|
|
|
|
|
unsigned int element_height;
|
|
|
|
unsigned int element_width;
|
|
|
|
unsigned int max_rows;
|
|
|
|
unsigned int max_elements;
|
|
|
|
|
|
|
|
//
|
|
|
|
unsigned int cur_columns;
|
|
|
|
unsigned int req_elements;
|
|
|
|
unsigned int cur_elements;
|
|
|
|
|
2017-01-03 08:25:24 -05:00
|
|
|
Distance spacing;
|
2016-10-08 12:57:59 -04:00
|
|
|
unsigned int menu_lines;
|
2017-01-02 15:19:27 -05:00
|
|
|
unsigned int max_displayed_lines;
|
2016-10-08 12:57:59 -04:00
|
|
|
unsigned int menu_columns;
|
|
|
|
unsigned int fixed_num_lines;
|
2017-01-03 08:25:24 -05:00
|
|
|
unsigned int dynamic;
|
2016-12-28 07:46:53 -05:00
|
|
|
unsigned int eh;
|
2017-01-03 14:22:29 -05:00
|
|
|
unsigned int reverse;
|
2016-10-08 12:57:59 -04:00
|
|
|
gboolean cycle;
|
|
|
|
gboolean multi_select;
|
|
|
|
|
|
|
|
ScrollType scroll_type;
|
|
|
|
|
|
|
|
textbox **boxes;
|
|
|
|
scrollbar *scrollbar;
|
|
|
|
|
|
|
|
listview_update_callback callback;
|
|
|
|
void *udata;
|
|
|
|
|
2016-10-25 15:19:39 -04:00
|
|
|
gboolean scrollbar_scroll;
|
|
|
|
|
2016-10-08 12:57:59 -04:00
|
|
|
xcb_timestamp_t last_click;
|
|
|
|
listview_mouse_activated_cb mouse_activated;
|
|
|
|
void *mouse_activated_data;
|
2017-01-08 10:09:24 -05:00
|
|
|
|
2017-01-08 15:36:06 -05:00
|
|
|
char *listview_name;
|
2016-10-08 12:57:59 -04:00
|
|
|
};
|
|
|
|
|
2016-12-31 17:00:06 -05:00
|
|
|
static int listview_get_desired_height ( widget *wid );
|
|
|
|
|
2016-10-18 07:49:24 -04:00
|
|
|
static void listview_free ( widget *wid )
|
2016-10-08 12:57:59 -04:00
|
|
|
{
|
2016-10-18 07:49:24 -04:00
|
|
|
listview *lv = (listview *) wid;
|
|
|
|
for ( unsigned int i = 0; i < lv->cur_elements; i++ ) {
|
|
|
|
widget_free ( WIDGET ( lv->boxes [i] ) );
|
|
|
|
}
|
|
|
|
g_free ( lv->boxes );
|
|
|
|
|
2017-01-08 15:36:06 -05:00
|
|
|
g_free ( lv->listview_name );
|
2016-10-18 07:49:24 -04:00
|
|
|
widget_free ( WIDGET ( lv->scrollbar ) );
|
2016-10-08 12:57:59 -04:00
|
|
|
g_free ( lv );
|
|
|
|
}
|
|
|
|
static unsigned int scroll_per_page ( listview * lv )
|
|
|
|
{
|
|
|
|
int offset = 0;
|
|
|
|
|
|
|
|
// selected row is always visible.
|
|
|
|
// If selected is visible do not scroll.
|
|
|
|
if ( ( ( lv->selected - ( lv->last_offset ) ) < ( lv->max_elements ) ) && ( lv->selected >= ( lv->last_offset ) ) ) {
|
|
|
|
offset = lv->last_offset;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
// Do paginating
|
|
|
|
unsigned int page = ( lv->max_elements > 0 ) ? ( lv->selected / lv->max_elements ) : 0;
|
|
|
|
offset = page * lv->max_elements;
|
|
|
|
if ( page != lv->cur_page ) {
|
|
|
|
lv->cur_page = page;
|
|
|
|
lv->rchanged = TRUE;
|
|
|
|
}
|
|
|
|
// Set the position
|
2016-10-25 15:19:39 -04:00
|
|
|
//scrollbar_set_handle ( lv->scrollbar, page * lv->max_elements );
|
2016-10-08 12:57:59 -04:00
|
|
|
}
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int scroll_continious ( listview *lv )
|
|
|
|
{
|
|
|
|
unsigned int middle = ( lv->max_rows - ( ( lv->max_rows & 1 ) == 0 ) ) / 2;
|
|
|
|
unsigned int offset = 0;
|
|
|
|
if ( lv->selected > middle ) {
|
|
|
|
if ( lv->selected < ( lv->req_elements - ( lv->max_rows - middle ) ) ) {
|
|
|
|
offset = lv->selected - middle;
|
|
|
|
}
|
|
|
|
// Don't go below zero.
|
|
|
|
else if ( lv->req_elements > lv->max_rows ) {
|
|
|
|
offset = lv->req_elements - lv->max_rows;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( offset != lv->cur_page ) {
|
2016-10-25 15:19:39 -04:00
|
|
|
//scrollbar_set_handle ( lv->scrollbar, offset );
|
2016-10-08 12:57:59 -04:00
|
|
|
lv->cur_page = offset;
|
|
|
|
lv->rchanged = TRUE;
|
|
|
|
}
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void update_element ( listview *lv, unsigned int tb, unsigned int index, gboolean full )
|
|
|
|
{
|
|
|
|
// Select drawing mode
|
|
|
|
TextBoxFontType type = ( index & 1 ) == 0 ? NORMAL : ALT;
|
|
|
|
type = ( index ) == lv->selected ? HIGHLIGHT : type;
|
|
|
|
|
|
|
|
if ( lv->callback ) {
|
|
|
|
lv->callback ( lv->boxes[tb], index, lv->udata, type, full );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void listview_draw ( widget *wid, cairo_t *draw )
|
|
|
|
{
|
|
|
|
unsigned int offset = 0;
|
|
|
|
listview *lv = (listview *) wid;
|
|
|
|
if ( lv->scroll_type == LISTVIEW_SCROLL_CONTINIOUS ) {
|
|
|
|
offset = scroll_continious ( lv );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
offset = scroll_per_page ( lv );
|
|
|
|
}
|
2016-12-20 13:29:46 -05:00
|
|
|
// Set these all together to make sure they update consistently.
|
|
|
|
scrollbar_set_max_value ( lv->scrollbar, lv->req_elements );
|
|
|
|
scrollbar_set_handle_length ( lv->scrollbar, lv->cur_columns * lv->max_rows );
|
2017-01-03 14:28:19 -05:00
|
|
|
if ( lv->reverse ) {
|
2017-01-08 15:36:06 -05:00
|
|
|
scrollbar_set_handle ( lv->scrollbar, lv->req_elements - lv->selected - 1 );
|
|
|
|
}
|
|
|
|
else {
|
2017-01-03 14:28:19 -05:00
|
|
|
scrollbar_set_handle ( lv->scrollbar, lv->selected );
|
|
|
|
}
|
2016-10-08 12:57:59 -04:00
|
|
|
lv->last_offset = offset;
|
2017-01-03 13:23:09 -05:00
|
|
|
int spacing_vert = distance_get_pixel ( lv->spacing, ORIENTATION_VERTICAL );
|
|
|
|
int spacing_hori = distance_get_pixel ( lv->spacing, ORIENTATION_HORIZONTAL );
|
2017-01-06 10:41:23 -05:00
|
|
|
|
|
|
|
int left_offset = widget_padding_get_left ( wid );
|
2017-01-06 14:36:06 -05:00
|
|
|
int top_offset = widget_padding_get_top ( wid );
|
2017-01-06 10:41:23 -05:00
|
|
|
if ( lv->scrollbar->widget.index == 0 ) {
|
|
|
|
left_offset += spacing_hori + lv->scrollbar->widget.w;
|
|
|
|
}
|
2016-10-08 12:57:59 -04:00
|
|
|
if ( lv->cur_elements > 0 && lv->max_rows > 0 ) {
|
|
|
|
// Set new x/y possition.
|
|
|
|
unsigned int max = MIN ( lv->cur_elements, lv->req_elements - offset );
|
|
|
|
if ( lv->rchanged ) {
|
2017-01-03 13:23:09 -05:00
|
|
|
unsigned int width = lv->widget.w - spacing_hori * ( lv->cur_columns - 1 );
|
2016-12-29 11:04:16 -05:00
|
|
|
width -= widget_padding_get_padding_width ( wid );
|
2016-10-08 12:57:59 -04:00
|
|
|
if ( widget_enabled ( WIDGET ( lv->scrollbar ) ) ) {
|
2017-01-03 13:23:09 -05:00
|
|
|
width -= spacing_hori;
|
2016-10-17 12:21:03 -04:00
|
|
|
width -= widget_get_width ( WIDGET ( lv->scrollbar ) );
|
2016-10-08 12:57:59 -04:00
|
|
|
}
|
|
|
|
unsigned int element_width = ( width ) / lv->cur_columns;
|
|
|
|
for ( unsigned int i = 0; i < max; i++ ) {
|
2017-01-06 10:41:23 -05:00
|
|
|
unsigned int ex = left_offset + ( ( i ) / lv->max_rows ) * ( element_width + spacing_hori );
|
2017-01-03 14:22:29 -05:00
|
|
|
if ( lv->reverse ) {
|
2017-01-08 15:36:06 -05:00
|
|
|
unsigned int ey = wid->h - ( widget_padding_get_bottom ( wid ) + ( ( i ) % lv->max_rows ) * ( lv->element_height + spacing_vert ) ) - lv->element_height;
|
2017-01-03 14:22:29 -05:00
|
|
|
textbox_moveresize ( lv->boxes[i], ex, ey, element_width, lv->element_height );
|
2017-01-08 15:36:06 -05:00
|
|
|
}
|
|
|
|
else {
|
2017-01-06 14:36:06 -05:00
|
|
|
unsigned int ey = top_offset + ( ( i ) % lv->max_rows ) * ( lv->element_height + spacing_vert );
|
2017-01-03 14:22:29 -05:00
|
|
|
textbox_moveresize ( lv->boxes[i], ex, ey, element_width, lv->element_height );
|
|
|
|
}
|
2016-10-08 12:57:59 -04:00
|
|
|
|
|
|
|
update_element ( lv, i, i + offset, TRUE );
|
|
|
|
widget_draw ( WIDGET ( lv->boxes[i] ), draw );
|
|
|
|
}
|
|
|
|
lv->rchanged = FALSE;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
for ( unsigned int i = 0; i < max; i++ ) {
|
|
|
|
update_element ( lv, i, i + offset, FALSE );
|
|
|
|
widget_draw ( WIDGET ( lv->boxes[i] ), draw );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-01-06 16:12:24 -05:00
|
|
|
widget_draw ( WIDGET ( lv->scrollbar ), draw );
|
2016-10-08 12:57:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void listview_recompute_elements ( listview *lv )
|
|
|
|
{
|
|
|
|
unsigned int newne = 0;
|
|
|
|
if ( lv->max_rows == 0 ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ( lv->req_elements < lv->max_elements ) {
|
|
|
|
newne = lv->req_elements;
|
|
|
|
lv->cur_columns = ( lv->req_elements + ( lv->max_rows - 1 ) ) / lv->max_rows;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
newne = lv->max_elements;
|
|
|
|
lv->cur_columns = lv->menu_columns;
|
|
|
|
}
|
|
|
|
for ( unsigned int i = newne; i < lv->cur_elements; i++ ) {
|
|
|
|
widget_free ( WIDGET ( lv->boxes[i] ) );
|
|
|
|
}
|
|
|
|
lv->boxes = g_realloc ( lv->boxes, newne * sizeof ( textbox* ) );
|
|
|
|
if ( newne > 0 ) {
|
2017-02-03 14:49:16 -05:00
|
|
|
char *name = g_strjoin ( ".", lv->listview_name, "element", NULL );
|
2016-10-08 12:57:59 -04:00
|
|
|
for ( unsigned int i = lv->cur_elements; i < newne; i++ ) {
|
|
|
|
TextboxFlags flags = ( lv->multi_select ) ? TB_INDICATOR : 0;
|
2016-12-27 18:37:32 -05:00
|
|
|
lv->boxes[i] = textbox_create ( name, flags, NORMAL, "" );
|
2016-10-08 12:57:59 -04:00
|
|
|
}
|
2017-02-03 14:49:16 -05:00
|
|
|
g_free ( name );
|
2016-10-08 12:57:59 -04:00
|
|
|
}
|
2017-01-08 15:36:06 -05:00
|
|
|
lv->rchanged = TRUE;
|
2016-10-08 12:57:59 -04:00
|
|
|
lv->cur_elements = newne;
|
|
|
|
}
|
|
|
|
|
|
|
|
void listview_set_num_elements ( listview *lv, unsigned int rows )
|
|
|
|
{
|
|
|
|
lv->req_elements = rows;
|
|
|
|
listview_set_selected ( lv, lv->selected );
|
|
|
|
listview_recompute_elements ( lv );
|
|
|
|
widget_queue_redraw ( WIDGET ( lv ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int listview_get_selected ( listview *lv )
|
|
|
|
{
|
|
|
|
if ( lv != NULL ) {
|
|
|
|
return lv->selected;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void listview_set_selected ( listview *lv, unsigned int selected )
|
|
|
|
{
|
|
|
|
if ( lv && lv->req_elements > 0 ) {
|
|
|
|
lv->selected = MIN ( selected, lv->req_elements - 1 );
|
|
|
|
widget_queue_redraw ( WIDGET ( lv ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void listview_resize ( widget *wid, short w, short h )
|
|
|
|
{
|
|
|
|
listview *lv = (listview *) wid;
|
2017-01-08 15:36:06 -05:00
|
|
|
lv->widget.w = MAX ( 0, w );
|
|
|
|
lv->widget.h = MAX ( 0, h );
|
|
|
|
int height = lv->widget.h - widget_padding_get_padding_height ( WIDGET ( lv ) );
|
|
|
|
int spacing_vert = distance_get_pixel ( lv->spacing, ORIENTATION_VERTICAL );
|
2017-01-03 13:23:09 -05:00
|
|
|
lv->max_rows = ( spacing_vert + height ) / ( lv->element_height + spacing_vert );
|
2016-10-08 12:57:59 -04:00
|
|
|
lv->max_elements = lv->max_rows * lv->menu_columns;
|
|
|
|
|
2017-01-08 15:36:06 -05:00
|
|
|
if ( lv->scrollbar->widget.index == 0 ) {
|
2017-01-06 10:41:23 -05:00
|
|
|
widget_move ( WIDGET ( lv->scrollbar ),
|
2017-01-08 15:36:06 -05:00
|
|
|
widget_padding_get_left ( WIDGET ( lv ) ),
|
|
|
|
widget_padding_get_top ( WIDGET ( lv ) ) );
|
|
|
|
}
|
|
|
|
else {
|
2017-01-06 10:41:23 -05:00
|
|
|
widget_move ( WIDGET ( lv->scrollbar ),
|
2017-01-08 15:36:06 -05:00
|
|
|
lv->widget.w - widget_padding_get_right ( WIDGET ( lv ) ) - widget_get_width ( WIDGET ( lv->scrollbar ) ),
|
|
|
|
widget_padding_get_top ( WIDGET ( lv ) ) );
|
2017-01-06 10:41:23 -05:00
|
|
|
}
|
2016-12-28 07:17:02 -05:00
|
|
|
widget_resize ( WIDGET ( lv->scrollbar ), widget_get_width ( WIDGET ( lv->scrollbar ) ), height );
|
2016-10-08 12:57:59 -04:00
|
|
|
|
|
|
|
listview_recompute_elements ( lv );
|
|
|
|
widget_queue_redraw ( wid );
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean listview_scrollbar_clicked ( widget *sb, xcb_button_press_event_t * xce, void *udata )
|
|
|
|
{
|
|
|
|
listview *lv = (listview *) udata;
|
|
|
|
|
|
|
|
unsigned int sel = scrollbar_clicked ( (scrollbar *) sb, xce->event_y );
|
|
|
|
listview_set_selected ( lv, sel );
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean listview_clicked ( widget *wid, xcb_button_press_event_t *xce, G_GNUC_UNUSED void *udata )
|
|
|
|
{
|
|
|
|
listview *lv = (listview *) wid;
|
2016-10-25 15:19:39 -04:00
|
|
|
lv->scrollbar_scroll = FALSE;
|
2016-10-08 12:57:59 -04:00
|
|
|
if ( widget_enabled ( WIDGET ( lv->scrollbar ) ) && widget_intersect ( WIDGET ( lv->scrollbar ), xce->event_x, xce->event_y ) ) {
|
|
|
|
// Forward to handler of scrollbar.
|
|
|
|
xcb_button_press_event_t xce2 = *xce;
|
2016-10-25 15:19:39 -04:00
|
|
|
xce->event_x -= widget_get_x_pos ( WIDGET ( lv->scrollbar ) );
|
|
|
|
xce->event_y -= widget_get_y_pos ( WIDGET ( lv->scrollbar ) );
|
|
|
|
lv->scrollbar_scroll = TRUE;
|
2016-10-08 12:57:59 -04:00
|
|
|
return widget_clicked ( WIDGET ( lv->scrollbar ), &xce2 );
|
|
|
|
}
|
|
|
|
// Handle the boxes.
|
|
|
|
unsigned int max = MIN ( lv->cur_elements, lv->req_elements - lv->last_offset );
|
|
|
|
for ( unsigned int i = 0; i < max; i++ ) {
|
|
|
|
widget *w = WIDGET ( lv->boxes[i] );
|
|
|
|
if ( widget_intersect ( w, xce->event_x, xce->event_y ) ) {
|
|
|
|
if ( ( lv->last_offset + i ) == lv->selected ) {
|
|
|
|
if ( ( xce->time - lv->last_click ) < 200 ) {
|
|
|
|
// Somehow signal we accepted item.
|
|
|
|
lv->mouse_activated ( lv, xce, lv->mouse_activated_data );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
listview_set_selected ( lv, lv->last_offset + i );
|
|
|
|
}
|
|
|
|
lv->last_click = xce->time;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2016-10-25 15:19:39 -04:00
|
|
|
static gboolean listview_motion_notify ( widget *wid, xcb_motion_notify_event_t *xme )
|
|
|
|
{
|
|
|
|
listview *lv = (listview *) wid;
|
|
|
|
if ( widget_enabled ( WIDGET ( lv->scrollbar ) ) && lv->scrollbar_scroll ) {
|
|
|
|
xcb_motion_notify_event_t xle = *xme;
|
|
|
|
xle.event_x -= wid->x;
|
|
|
|
xle.event_y -= wid->y;
|
|
|
|
widget_motion_notify ( WIDGET ( lv->scrollbar ), &xle );
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
2017-01-04 07:34:02 -05:00
|
|
|
listview *listview_create ( const char *name, listview_update_callback cb, void *udata, unsigned int eh, gboolean reverse )
|
2016-10-08 12:57:59 -04:00
|
|
|
{
|
2017-01-08 15:36:06 -05:00
|
|
|
listview *lv = g_malloc0 ( sizeof ( listview ) );
|
|
|
|
gchar *box = g_strjoin ( ".", name, "box", NULL );
|
2017-01-08 10:09:24 -05:00
|
|
|
widget_init ( WIDGET ( lv ), box );
|
2017-01-08 15:36:06 -05:00
|
|
|
g_free ( box );
|
2017-01-08 10:09:24 -05:00
|
|
|
lv->listview_name = g_strdup ( name );
|
2016-12-30 12:31:30 -05:00
|
|
|
lv->widget.free = listview_free;
|
|
|
|
lv->widget.resize = listview_resize;
|
|
|
|
lv->widget.draw = listview_draw;
|
|
|
|
lv->widget.clicked = listview_clicked;
|
|
|
|
lv->widget.motion_notify = listview_motion_notify;
|
|
|
|
lv->widget.get_desired_height = listview_get_desired_height;
|
|
|
|
lv->widget.enabled = TRUE;
|
2017-01-08 15:36:06 -05:00
|
|
|
lv->eh = eh;
|
2016-10-08 12:57:59 -04:00
|
|
|
|
2017-01-08 15:36:06 -05:00
|
|
|
char *n = g_strjoin ( ".", lv->listview_name, "scrollbar", NULL );
|
2017-01-06 09:36:06 -05:00
|
|
|
lv->scrollbar = scrollbar_create ( n );
|
2017-01-06 10:41:23 -05:00
|
|
|
// Default position on right.
|
2017-01-08 15:36:06 -05:00
|
|
|
lv->scrollbar->widget.index = rofi_theme_get_integer_exact ( WIDGET ( lv->scrollbar ), "index", 1 );
|
|
|
|
g_free ( n );
|
2016-10-08 12:57:59 -04:00
|
|
|
widget_set_clicked_handler ( WIDGET ( lv->scrollbar ), listview_scrollbar_clicked, lv );
|
|
|
|
lv->scrollbar->widget.parent = WIDGET ( lv );
|
|
|
|
// Calculate height of an element.
|
2016-12-28 07:46:53 -05:00
|
|
|
//
|
2017-01-08 15:36:06 -05:00
|
|
|
char *tb_name = g_strjoin ( ".", lv->listview_name, "element", NULL );
|
|
|
|
textbox *tb = textbox_create ( tb_name, 0, NORMAL, "" );
|
|
|
|
lv->element_height = textbox_get_estimated_height ( tb, lv->eh );
|
|
|
|
g_free ( tb_name );
|
2017-01-09 17:08:11 -05:00
|
|
|
widget_free ( WIDGET ( tb ) );
|
2016-10-08 12:57:59 -04:00
|
|
|
|
|
|
|
lv->callback = cb;
|
|
|
|
lv->udata = udata;
|
2016-12-11 11:06:31 -05:00
|
|
|
|
|
|
|
// Some settings.
|
2017-01-04 16:27:27 -05:00
|
|
|
lv->spacing = rofi_theme_get_distance ( WIDGET ( lv ), "spacing", DEFAULT_SPACING );
|
|
|
|
lv->menu_columns = rofi_theme_get_integer ( WIDGET ( lv ), "columns", config.menu_columns );
|
|
|
|
lv->fixed_num_lines = rofi_theme_get_boolean ( WIDGET ( lv ), "fixed-height", config.fixed_num_lines );
|
2017-01-08 15:36:06 -05:00
|
|
|
lv->dynamic = rofi_theme_get_boolean ( WIDGET ( lv ), "dynamic", TRUE );
|
|
|
|
lv->reverse = rofi_theme_get_boolean ( WIDGET ( lv ), "reverse", reverse );
|
|
|
|
listview_set_show_scrollbar ( lv, rofi_theme_get_boolean ( WIDGET ( lv ), "scrollbar", !config.hide_scrollbar ) );
|
2017-01-04 16:27:27 -05:00
|
|
|
lv->cycle = rofi_theme_get_boolean ( WIDGET ( lv ), "cycle", config.cycle );
|
2016-12-11 11:06:31 -05:00
|
|
|
|
2016-10-08 12:57:59 -04:00
|
|
|
return lv;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Navigation commands.
|
|
|
|
*/
|
|
|
|
|
2017-01-03 14:22:29 -05:00
|
|
|
static void listview_nav_up_int ( listview *lv )
|
2016-10-08 12:57:59 -04:00
|
|
|
{
|
|
|
|
if ( lv == NULL ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ( lv->req_elements == 0 || ( lv->selected == 0 && !lv->cycle ) ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ( lv->selected == 0 ) {
|
|
|
|
lv->selected = lv->req_elements;
|
|
|
|
}
|
|
|
|
lv->selected--;
|
|
|
|
widget_queue_redraw ( WIDGET ( lv ) );
|
|
|
|
}
|
2017-01-03 14:22:29 -05:00
|
|
|
static void listview_nav_down_int ( listview *lv )
|
2016-10-08 12:57:59 -04:00
|
|
|
{
|
|
|
|
if ( lv == NULL ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ( lv->req_elements == 0 || ( lv->selected == ( lv->req_elements - 1 ) && !lv->cycle ) ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
lv->selected = lv->selected < lv->req_elements - 1 ? MIN ( lv->req_elements - 1, lv->selected + 1 ) : 0;
|
|
|
|
|
|
|
|
widget_queue_redraw ( WIDGET ( lv ) );
|
|
|
|
}
|
|
|
|
|
2017-01-03 14:22:29 -05:00
|
|
|
void listview_nav_up ( listview *lv )
|
|
|
|
{
|
|
|
|
if ( lv->reverse ) {
|
|
|
|
listview_nav_down_int ( lv );
|
2017-01-08 15:36:06 -05:00
|
|
|
}
|
|
|
|
else {
|
2017-01-03 14:22:29 -05:00
|
|
|
listview_nav_up_int ( lv );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void listview_nav_down ( listview *lv )
|
|
|
|
{
|
|
|
|
if ( lv->reverse ) {
|
|
|
|
listview_nav_up_int ( lv );
|
2017-01-08 15:36:06 -05:00
|
|
|
}
|
|
|
|
else {
|
2017-01-03 14:22:29 -05:00
|
|
|
listview_nav_down_int ( lv );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-08 12:57:59 -04:00
|
|
|
void listview_nav_left ( listview *lv )
|
|
|
|
{
|
|
|
|
if ( lv == NULL ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ( lv->selected >= lv->max_rows ) {
|
|
|
|
lv->selected -= lv->max_rows;
|
|
|
|
widget_queue_redraw ( WIDGET ( lv ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void listview_nav_right ( listview *lv )
|
|
|
|
{
|
|
|
|
if ( lv == NULL ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ( ( lv->selected + lv->max_rows ) < lv->req_elements ) {
|
|
|
|
lv->selected += lv->max_rows;
|
|
|
|
widget_queue_redraw ( WIDGET ( lv ) );
|
|
|
|
}
|
|
|
|
else if ( lv->selected < ( lv->req_elements - 1 ) ) {
|
|
|
|
// We do not want to move to last item, UNLESS the last column is only
|
|
|
|
// partially filled, then we still want to move column and select last entry.
|
|
|
|
// First check the column we are currently in.
|
|
|
|
int col = lv->selected / lv->max_rows;
|
|
|
|
// Check total number of columns.
|
|
|
|
int ncol = lv->req_elements / lv->max_rows;
|
|
|
|
// If there is an extra column, move.
|
|
|
|
if ( col != ncol ) {
|
|
|
|
lv->selected = lv->req_elements - 1;
|
|
|
|
widget_queue_redraw ( WIDGET ( lv ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-03 14:22:29 -05:00
|
|
|
static void listview_nav_page_prev_int ( listview *lv )
|
2016-10-08 12:57:59 -04:00
|
|
|
{
|
|
|
|
if ( lv == NULL ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ( lv->selected < lv->max_elements ) {
|
|
|
|
lv->selected = 0;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
lv->selected -= ( lv->max_elements );
|
|
|
|
}
|
|
|
|
widget_queue_redraw ( WIDGET ( lv ) );
|
|
|
|
}
|
2017-01-03 14:22:29 -05:00
|
|
|
static void listview_nav_page_next_int ( listview *lv )
|
2016-10-08 12:57:59 -04:00
|
|
|
{
|
|
|
|
if ( lv == NULL ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ( lv->req_elements == 0 ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
lv->selected += ( lv->max_elements );
|
|
|
|
if ( lv->selected >= lv->req_elements ) {
|
|
|
|
lv->selected = lv->req_elements - 1;
|
|
|
|
}
|
|
|
|
widget_queue_redraw ( WIDGET ( lv ) );
|
|
|
|
}
|
|
|
|
|
2017-01-03 14:22:29 -05:00
|
|
|
void listview_nav_page_prev ( listview *lv )
|
|
|
|
{
|
2017-01-08 15:36:06 -05:00
|
|
|
if ( lv->reverse ) {
|
2017-01-03 14:22:29 -05:00
|
|
|
listview_nav_page_next_int ( lv );
|
2017-01-08 15:36:06 -05:00
|
|
|
}
|
|
|
|
else {
|
2017-01-03 14:22:29 -05:00
|
|
|
listview_nav_page_prev_int ( lv );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void listview_nav_page_next ( listview *lv )
|
|
|
|
{
|
2017-01-08 15:36:06 -05:00
|
|
|
if ( lv->reverse ) {
|
2017-01-03 14:22:29 -05:00
|
|
|
listview_nav_page_prev_int ( lv );
|
2017-01-08 15:36:06 -05:00
|
|
|
}
|
|
|
|
else {
|
2017-01-03 14:22:29 -05:00
|
|
|
listview_nav_page_next_int ( lv );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-31 17:00:06 -05:00
|
|
|
static int listview_get_desired_height ( widget *wid )
|
2016-10-08 12:57:59 -04:00
|
|
|
{
|
2017-01-09 16:40:11 -05:00
|
|
|
listview *lv = (listview *) wid;
|
2017-01-08 15:36:06 -05:00
|
|
|
if ( lv == NULL || lv->widget.enabled == FALSE ) {
|
2016-10-08 12:57:59 -04:00
|
|
|
return 0;
|
|
|
|
}
|
2017-01-09 02:39:02 -05:00
|
|
|
int spacing = distance_get_pixel ( lv->spacing, ORIENTATION_VERTICAL );
|
2017-01-09 16:40:11 -05:00
|
|
|
int h = lv->menu_lines;
|
2016-10-08 12:57:59 -04:00
|
|
|
if ( !( lv->fixed_num_lines ) ) {
|
2017-01-03 08:25:24 -05:00
|
|
|
if ( lv->dynamic ) {
|
|
|
|
h = MIN ( lv->menu_lines, lv->req_elements );
|
2017-01-08 15:36:06 -05:00
|
|
|
}
|
|
|
|
else {
|
2017-01-03 08:25:24 -05:00
|
|
|
h = MIN ( lv->menu_lines, lv->max_displayed_lines );
|
|
|
|
}
|
2016-10-08 12:57:59 -04:00
|
|
|
}
|
|
|
|
if ( h == 0 ) {
|
2017-01-08 15:36:06 -05:00
|
|
|
if ( lv->dynamic && !lv->fixed_num_lines ) {
|
2017-01-06 07:42:00 -05:00
|
|
|
// Hide widget fully.
|
|
|
|
return 0;
|
|
|
|
}
|
2017-01-08 15:36:06 -05:00
|
|
|
return widget_padding_get_padding_height ( WIDGET ( lv ) );
|
2016-10-08 12:57:59 -04:00
|
|
|
}
|
2017-01-08 15:36:06 -05:00
|
|
|
int height = widget_padding_get_padding_height ( WIDGET ( lv ) );
|
|
|
|
height += h * ( lv->element_height + spacing ) - spacing;
|
2017-01-02 18:59:37 -05:00
|
|
|
return height;
|
2016-10-08 12:57:59 -04:00
|
|
|
}
|
|
|
|
|
2016-12-09 16:16:31 -05:00
|
|
|
void listview_set_show_scrollbar ( listview *lv, gboolean enabled )
|
2016-10-08 12:57:59 -04:00
|
|
|
{
|
|
|
|
if ( lv ) {
|
|
|
|
if ( enabled ) {
|
|
|
|
widget_enable ( WIDGET ( lv->scrollbar ) );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
widget_disable ( WIDGET ( lv->scrollbar ) );
|
|
|
|
}
|
|
|
|
listview_recompute_elements ( lv );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void listview_set_scroll_type ( listview *lv, ScrollType type )
|
|
|
|
{
|
|
|
|
if ( lv ) {
|
|
|
|
lv->scroll_type = type;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void listview_set_mouse_activated_cb ( listview *lv, listview_mouse_activated_cb cb, void *udata )
|
|
|
|
{
|
|
|
|
if ( lv ) {
|
|
|
|
lv->mouse_activated = cb;
|
|
|
|
lv->mouse_activated_data = udata;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void listview_set_multi_select ( listview *lv, gboolean enable )
|
|
|
|
{
|
|
|
|
if ( lv ) {
|
|
|
|
lv->multi_select = enable;
|
|
|
|
}
|
|
|
|
}
|
2017-01-02 15:19:27 -05:00
|
|
|
void listview_set_num_lines ( listview *lv, unsigned int num_lines )
|
|
|
|
{
|
|
|
|
if ( lv ) {
|
|
|
|
lv->menu_lines = num_lines;
|
|
|
|
}
|
|
|
|
}
|
2017-01-08 10:49:46 -05:00
|
|
|
|
|
|
|
unsigned int listview_get_num_lines ( listview *lv )
|
|
|
|
{
|
|
|
|
if ( lv ) {
|
|
|
|
return lv->menu_lines;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2017-01-02 15:19:27 -05:00
|
|
|
void listview_set_max_lines ( listview *lv, unsigned int max_lines )
|
|
|
|
{
|
|
|
|
if ( lv ) {
|
|
|
|
lv->max_displayed_lines = max_lines;
|
|
|
|
}
|
|
|
|
}
|
2017-01-08 11:18:49 -05:00
|
|
|
|
|
|
|
gboolean listview_get_fixed_num_lines ( listview *lv )
|
|
|
|
{
|
|
|
|
if ( lv ) {
|
|
|
|
return lv->fixed_num_lines;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
void listview_set_fixed_num_lines ( listview *lv )
|
|
|
|
{
|
|
|
|
if ( lv ) {
|
|
|
|
lv->fixed_num_lines = TRUE;
|
|
|
|
}
|
|
|
|
}
|