543 lines
12 KiB
C++
543 lines
12 KiB
C++
|
/****************************************************************************
|
||
|
|
||
|
GLUI User Interface Toolkit
|
||
|
---------------------------
|
||
|
|
||
|
glui_list.cpp - GLUI_List control class
|
||
|
|
||
|
|
||
|
--------------------------------------------------
|
||
|
|
||
|
Copyright (c) 2004 John Kew
|
||
|
|
||
|
This program is freely distributable without licensing fees and is
|
||
|
provided without guarantee or warrantee expressed or implied. This
|
||
|
program is -not- in the public domain.
|
||
|
|
||
|
*****************************************************************************/
|
||
|
|
||
|
|
||
|
|
||
|
#include "glui_internal_control.h"
|
||
|
#include <cmath>
|
||
|
#include <sys/timeb.h>
|
||
|
|
||
|
/****************************** GLUI_List::GLUI_List() **********/
|
||
|
|
||
|
GLUI_List::GLUI_List( GLUI_Node *parent, bool scroll,
|
||
|
int id, GLUI_CB callback
|
||
|
/*,GLUI_Control *object
|
||
|
GLUI_InterObject_CB obj_cb*/)
|
||
|
{
|
||
|
common_construct(parent, NULL, scroll, id, callback/*, object, obj_cb*/);
|
||
|
}
|
||
|
|
||
|
/****************************** GLUI_List::GLUI_List() **********/
|
||
|
|
||
|
GLUI_List::GLUI_List( GLUI_Node *parent,
|
||
|
GLUI_String& live_var, bool scroll,
|
||
|
int id,
|
||
|
GLUI_CB callback
|
||
|
/* ,GLUI_Control *object
|
||
|
,GLUI_InterObject_CB obj_cb*/ )
|
||
|
{
|
||
|
common_construct(parent, &live_var, scroll, id, callback/*, object, obj_cb*/);
|
||
|
}
|
||
|
|
||
|
/****************************** GLUI_List::common_construct() **********/
|
||
|
|
||
|
void GLUI_List::common_construct(
|
||
|
GLUI_Node *parent,
|
||
|
GLUI_String* data, bool scroll,
|
||
|
int id,
|
||
|
GLUI_CB callback
|
||
|
/*,GLUI_Control *object
|
||
|
, GLUI_InterObject_CB obj_cb*/)
|
||
|
{
|
||
|
common_init();
|
||
|
GLUI_Node *list_panel = parent;
|
||
|
|
||
|
if (scroll) {
|
||
|
GLUI_Panel *p = new GLUI_Panel(parent,"",GLUI_PANEL_NONE);
|
||
|
p->x_off = 1;
|
||
|
list_panel = p;
|
||
|
}
|
||
|
this->ptr_val = data;
|
||
|
if (data) {
|
||
|
this->live_type = GLUI_LIVE_STRING;
|
||
|
}
|
||
|
this->user_id = id;
|
||
|
this->callback = callback;
|
||
|
this->name = "list";
|
||
|
list_panel->add_control( this );
|
||
|
if (scroll)
|
||
|
{
|
||
|
new GLUI_Column(list_panel, false);
|
||
|
scrollbar =
|
||
|
new GLUI_Scrollbar(list_panel,
|
||
|
"scrollbar",
|
||
|
GLUI_SCROLL_VERTICAL,
|
||
|
GLUI_SCROLL_INT);
|
||
|
scrollbar->set_object_callback(GLUI_List::scrollbar_callback, this);
|
||
|
scrollbar->set_alignment(GLUI_ALIGN_LEFT);
|
||
|
// scrollbar->can_activate = false; //kills ability to mouse drag too
|
||
|
}
|
||
|
init_live();
|
||
|
}
|
||
|
|
||
|
/****************************** GLUI_List::mouse_down_handler() **********/
|
||
|
int GLUI_List::mouse_down_handler( int local_x, int local_y )
|
||
|
{
|
||
|
int tmp_line;
|
||
|
unsigned long int ms;
|
||
|
timeb time;
|
||
|
ftime(&time);
|
||
|
ms = time.millitm + (time.time)*1000;
|
||
|
|
||
|
tmp_line = find_line( local_x-x_abs, local_y-y_abs-5 );
|
||
|
if ( tmp_line == -1 ) {
|
||
|
if ( glui )
|
||
|
glui->deactivate_current_control( );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (tmp_line < num_lines) {
|
||
|
curr_line = tmp_line;
|
||
|
if (scrollbar)
|
||
|
scrollbar->set_int_val(curr_line);
|
||
|
this->execute_callback();
|
||
|
if (associated_object != NULL)
|
||
|
if (cb_click_type == GLUI_SINGLE_CLICK) {
|
||
|
if (obj_cb) {
|
||
|
// obj_cb(associated_object, user_id);
|
||
|
obj_cb(this);
|
||
|
}
|
||
|
} else {
|
||
|
if (last_line == curr_line && (ms - last_click_time) < 300) {
|
||
|
//obj_cb(associated_object, user_id);
|
||
|
obj_cb(this);
|
||
|
} else {
|
||
|
last_click_time = ms;
|
||
|
last_line = curr_line;
|
||
|
}
|
||
|
}
|
||
|
if ( can_draw())
|
||
|
update_and_draw_text();
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/******************************** GLUI_List::mouse_up_handler() **********/
|
||
|
|
||
|
int GLUI_List::mouse_up_handler( int local_x, int local_y, bool inside )
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************** GLUI_List::mouse_held_down_handler() ******/
|
||
|
|
||
|
int GLUI_List::mouse_held_down_handler( int local_x, int local_y,
|
||
|
bool new_inside)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
/****************************** GLUI_List::key_handler() **********/
|
||
|
|
||
|
int GLUI_List::key_handler( unsigned char key,int modifiers )
|
||
|
{
|
||
|
|
||
|
|
||
|
draw_text_only = false; /** Well, hack is not yet working **/
|
||
|
update_and_draw_text();
|
||
|
draw_text_only = false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/****************************** GLUI_List::activate() **********/
|
||
|
|
||
|
void GLUI_List::activate( int how )
|
||
|
{
|
||
|
// if ( debug )
|
||
|
// dump( stdout, "-> ACTIVATE" );
|
||
|
active = true;
|
||
|
|
||
|
if ( how == GLUI_ACTIVATE_MOUSE )
|
||
|
return; /* Don't select everything if activated with mouse */
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/****************************** GLUI_List::deactivate() **********/
|
||
|
|
||
|
void GLUI_List::deactivate( void )
|
||
|
{
|
||
|
active = false;
|
||
|
redraw();
|
||
|
}
|
||
|
|
||
|
/****************************** GLUI_List::draw() **********/
|
||
|
|
||
|
void GLUI_List::draw( int x, int y )
|
||
|
{
|
||
|
int line = 0;
|
||
|
int box_width;
|
||
|
GLUI_List_Item *item;
|
||
|
|
||
|
GLUI_DRAWINGSENTINAL_IDIOM
|
||
|
|
||
|
/* Bevelled Border */
|
||
|
glBegin( GL_LINES );
|
||
|
glColor3f( .5, .5, .5 );
|
||
|
glVertex2i( 0, 0 ); glVertex2i( w, 0 );
|
||
|
glVertex2i( 0, 0 ); glVertex2i( 0, h );
|
||
|
|
||
|
glColor3f( 1., 1., 1. );
|
||
|
glVertex2i( 0, h ); glVertex2i( w, h );
|
||
|
glVertex2i( w, h ); glVertex2i( w, 0 );
|
||
|
|
||
|
if ( enabled )
|
||
|
glColor3f( 0., 0., 0. );
|
||
|
else
|
||
|
glColor3f( .25, .25, .25 );
|
||
|
glVertex2i( 1, 1 ); glVertex2i( w-1, 1 );
|
||
|
glVertex2i( 1, 1 ); glVertex2i( 1, h-1 );
|
||
|
|
||
|
glColor3f( .75, .75, .75 );
|
||
|
glVertex2i( 1, h-1 ); glVertex2i( w-1, h-1 );
|
||
|
glVertex2i( w-1, h-1 ); glVertex2i( w-1, 1 );
|
||
|
glEnd();
|
||
|
|
||
|
/* Draw Background if enabled*/
|
||
|
if (enabled) {
|
||
|
glColor3f( 1., 1., 1. );
|
||
|
glDisable( GL_CULL_FACE );
|
||
|
glBegin( GL_QUADS );
|
||
|
glVertex2i( 2, 2 ); glVertex2i( w-2, 2 );
|
||
|
glVertex2i( w-2, h-2 ); glVertex2i(2, h-2 );
|
||
|
glEnd();
|
||
|
} else {
|
||
|
glColor3f( .8, .8, .8 );
|
||
|
glDisable( GL_CULL_FACE );
|
||
|
glBegin( GL_QUADS );
|
||
|
glVertex2i( 2, 2 ); glVertex2i( w-2, 2 );
|
||
|
glVertex2i( w-2, h-2 ); glVertex2i(2, h-2 );
|
||
|
glEnd();
|
||
|
}
|
||
|
|
||
|
/* Figure out how wide the box is */
|
||
|
box_width = get_box_width();
|
||
|
|
||
|
/* Figure out which lines are visible*/
|
||
|
|
||
|
visible_lines = (int)(h-20)/15;
|
||
|
|
||
|
item = (GLUI_List_Item *) items_list.first_child();
|
||
|
|
||
|
line = 0;
|
||
|
while (item) {
|
||
|
if (line < start_line) {
|
||
|
line++;
|
||
|
item = (GLUI_List_Item *) item->next();
|
||
|
continue;
|
||
|
}
|
||
|
if (line >= start_line && line <= (start_line+visible_lines)) {
|
||
|
if (curr_line == line)
|
||
|
draw_text(item->text.c_str(),1,0,(line - start_line)*15);
|
||
|
else
|
||
|
draw_text(item->text.c_str(),0,0,(line - start_line)*15);
|
||
|
}
|
||
|
line++;
|
||
|
item = (GLUI_List_Item *) item->next();
|
||
|
}
|
||
|
|
||
|
if (scrollbar) {
|
||
|
scrollbar->set_int_limits(MAX(0,num_lines-visible_lines), 0);
|
||
|
glPushMatrix();
|
||
|
glTranslatef(scrollbar->x_abs-x_abs, scrollbar->y_abs-y_abs,0.0);
|
||
|
scrollbar->draw_scroll();
|
||
|
glPopMatrix();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/********************************* GLUI_List::draw_text() ****************/
|
||
|
|
||
|
void GLUI_List::draw_text(const char *t, int selected, int x, int y )
|
||
|
{
|
||
|
int text_x, i, x_pos;
|
||
|
int box_width;
|
||
|
|
||
|
GLUI_DRAWINGSENTINAL_IDIOM
|
||
|
|
||
|
/** Find where to draw the text **/
|
||
|
|
||
|
text_x = 2 + GLUI_LIST_BOXINNERMARGINX;
|
||
|
|
||
|
/** Draw selection area dark **/
|
||
|
if ( enabled && selected ) {
|
||
|
glColor3f( 0.0f, 0.0f, .6f );
|
||
|
glBegin( GL_QUADS );
|
||
|
glVertex2i(text_x, y+5 ); glVertex2i( w-text_x, y+5 );
|
||
|
glVertex2i(w-text_x, y+19 ); glVertex2i(text_x, y+19 );
|
||
|
glEnd();
|
||
|
}
|
||
|
box_width = get_box_width();
|
||
|
|
||
|
if ( !selected || !enabled ) { /* No current selection */
|
||
|
x_pos = text_x; /* or control disabled */
|
||
|
if ( enabled )
|
||
|
glColor3b( 0, 0, 0 );
|
||
|
else
|
||
|
glColor3b( 32, 32, 32 );
|
||
|
|
||
|
glRasterPos2i( text_x, y+15);
|
||
|
i = 0;
|
||
|
while( t[i] != '\0' && substring_width(t,0,i) < box_width) {
|
||
|
glutBitmapCharacter( get_font(), t[i] );
|
||
|
x_pos += char_width( t[i] );
|
||
|
i++;
|
||
|
}
|
||
|
}
|
||
|
else { /* There is a selection */
|
||
|
i = 0;
|
||
|
x_pos = text_x;
|
||
|
glColor3f( 1., 1., 1. );
|
||
|
glRasterPos2i( text_x, y+15);
|
||
|
while( t[i] != '\0' && substring_width(t,0,i) < box_width) {
|
||
|
glutBitmapCharacter( get_font(), t[i] );
|
||
|
x_pos += char_width( t[i] );
|
||
|
i++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
int GLUI_List::find_line(int x, int y) {
|
||
|
return start_line + ((int)(y/15));
|
||
|
}
|
||
|
|
||
|
int GLUI_List::get_box_width() {
|
||
|
return MAX( this->w
|
||
|
- 6 /* 2 * the two-line box border */
|
||
|
- 2 * GLUI_LIST_BOXINNERMARGINX, 0 );
|
||
|
|
||
|
}
|
||
|
|
||
|
/******************************** GLUI_List::substring_width() *********/
|
||
|
int GLUI_List::substring_width( const char *t, int start, int end )
|
||
|
{
|
||
|
int i, width;
|
||
|
|
||
|
width = 0;
|
||
|
|
||
|
for( i=start; i<=end; i++ )
|
||
|
width += char_width( t[i] );
|
||
|
|
||
|
return width;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************** GLUI_List::update_and_draw_text() ********/
|
||
|
|
||
|
void GLUI_List::update_and_draw_text( void )
|
||
|
{
|
||
|
if ( NOT can_draw() )
|
||
|
return;
|
||
|
|
||
|
//update_substring_bounds();
|
||
|
/* printf( "ss: %d/%d\n", substring_start, substring_end ); */
|
||
|
|
||
|
redraw();
|
||
|
}
|
||
|
|
||
|
|
||
|
/********************************* GLUI_List::special_handler() **********/
|
||
|
|
||
|
int GLUI_List::special_handler( int key,int modifiers )
|
||
|
{
|
||
|
if ( NOT glui )
|
||
|
return false;
|
||
|
|
||
|
if ( key == GLUT_KEY_DOWN ) {
|
||
|
if (curr_line < num_lines) {
|
||
|
curr_line++;
|
||
|
if (curr_line > start_line+visible_lines)
|
||
|
start_line++;
|
||
|
}
|
||
|
} else if ( key == GLUT_KEY_UP ) {
|
||
|
if (curr_line > 0) {
|
||
|
curr_line--;
|
||
|
if (curr_line < start_line)
|
||
|
start_line--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (scrollbar)
|
||
|
scrollbar->set_int_val(curr_line);
|
||
|
redraw();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/************************************ GLUI_List::update_size() **********/
|
||
|
|
||
|
void GLUI_List::update_size( void )
|
||
|
{
|
||
|
if ( NOT glui )
|
||
|
return;
|
||
|
|
||
|
if ( w < GLUI_LIST_MIN_TEXT_WIDTH )
|
||
|
w = GLUI_LIST_MIN_TEXT_WIDTH;
|
||
|
}
|
||
|
|
||
|
/**************************************** GLUI_Listbox::add_item() **********/
|
||
|
|
||
|
int GLUI_List::add_item( int id, const char *new_text )
|
||
|
{
|
||
|
GLUI_List_Item *new_node = new GLUI_List_Item;
|
||
|
GLUI_List_Item *head;
|
||
|
|
||
|
new_node->text = new_text;
|
||
|
new_node->id = id;
|
||
|
|
||
|
head = (GLUI_List_Item*) items_list.first_child();
|
||
|
new_node->link_this_to_parent_last( &items_list );
|
||
|
|
||
|
if ( head == NULL ) {
|
||
|
/*** This is first item added ***/
|
||
|
|
||
|
int_val = id+1; /** Different than id **/
|
||
|
// do_selection( id );
|
||
|
last_live_int = id;
|
||
|
|
||
|
if( glui )
|
||
|
glui->post_update_main_gfx();
|
||
|
}
|
||
|
num_lines++;
|
||
|
if (scrollbar)
|
||
|
scrollbar->set_int_limits(MAX(num_lines-visible_lines,0), 0);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/************************************** GLUI_Listbox::delete_() **********/
|
||
|
|
||
|
int GLUI_List::delete_all()
|
||
|
{
|
||
|
GLUI_List_Item *item;
|
||
|
|
||
|
item = (GLUI_List_Item *) items_list.first_child();
|
||
|
while( item ) {
|
||
|
item->unlink();
|
||
|
delete item;
|
||
|
item = (GLUI_List_Item *) items_list.first_child();
|
||
|
}
|
||
|
|
||
|
num_lines = 0;
|
||
|
curr_line = 0;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/************************************** GLUI_Listbox::delete_item() **********/
|
||
|
|
||
|
int GLUI_List::delete_item( const char *text )
|
||
|
{
|
||
|
GLUI_List_Item *node = get_item_ptr( text );
|
||
|
|
||
|
if ( node ) {
|
||
|
node->unlink();
|
||
|
delete node;
|
||
|
num_lines--;
|
||
|
return true;
|
||
|
}
|
||
|
else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/************************************** GLUI_Listbox::delete_item() **********/
|
||
|
|
||
|
int GLUI_List::delete_item( int id )
|
||
|
{
|
||
|
GLUI_List_Item *node = get_item_ptr( id );
|
||
|
|
||
|
if ( node ) {
|
||
|
node->unlink();
|
||
|
delete node;
|
||
|
num_lines--;
|
||
|
return true;
|
||
|
}
|
||
|
else {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/************************************ GLUI_Listbox::get_item_ptr() **********/
|
||
|
|
||
|
GLUI_List_Item *GLUI_List::get_item_ptr( const char *text )
|
||
|
{
|
||
|
GLUI_List_Item *item;
|
||
|
|
||
|
item = (GLUI_List_Item *) items_list.first_child();
|
||
|
while( item ) {
|
||
|
if ( item->text == text )
|
||
|
return item;
|
||
|
|
||
|
item = (GLUI_List_Item *) item->next();
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
/************************************ GLUI_Listbox::get_item_ptr() **********/
|
||
|
|
||
|
GLUI_List_Item *GLUI_List::get_item_ptr( int id )
|
||
|
{
|
||
|
GLUI_List_Item *item;
|
||
|
|
||
|
item = (GLUI_List_Item *) items_list.first_child();
|
||
|
while( item ) {
|
||
|
if ( item->id == id )
|
||
|
return item;
|
||
|
|
||
|
item = (GLUI_List_Item *) item->next();
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/**************************************** GLUI_List::mouse_over() ********/
|
||
|
|
||
|
int GLUI_List::mouse_over( int state, int x, int y )
|
||
|
{
|
||
|
glutSetCursor( GLUT_CURSOR_LEFT_ARROW );
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void GLUI_List::scrollbar_callback(GLUI_Control *my_scrollbar) {
|
||
|
GLUI_Scrollbar *sb = my_scrollbar->dynamicCastGLUI_Scrollbar();
|
||
|
if (!sb) return;
|
||
|
GLUI_List* me = (GLUI_List*) sb->associated_object;
|
||
|
if (me->scrollbar == NULL)
|
||
|
return;
|
||
|
int new_start_line = sb->get_int_val(); // TODO!!
|
||
|
me->start_line = new_start_line;
|
||
|
|
||
|
if ( me->can_draw() )
|
||
|
me->update_and_draw_text();
|
||
|
}
|