/*
     PUI Auxiliary Widget Library
     Derived from PLIB, the Portable Game Library by Steve Baker.

     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Library General Public
     License as published by the Free Software Foundation; either
     version 2 of the License, or (at your option) any later version.
 
     This library is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     Library General Public License for more details.
 
     You should have received a copy of the GNU Library General Public
     License along with this library; if not, write to the Free Software
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 
     For further information visit http://plib.sourceforge.net

     $Id: puAux.h 2147 2009-01-29 16:53:09Z fayjf $
*/

#ifndef _PUI_AUX_WIDGETS_H_
#define _PUI_AUX_WIDGETS_H_ 1

#include "pu.h"

// Widget Class Bit Patterns
// PUI classes take up 0x00000001 through 0x00020000

#define PUCLASS_FILESELECTOR        0x00040000 /* Because FilePicker is obsolete */
#define PUCLASS_BISLIDER            0x00080000
#define PUCLASS_TRISLIDER           0x00100000
#define PUCLASS_VERTMENU            0x00200000
#define PUCLASS_LARGEINPUT          0x00400000
#define PUCLASS_COMBOBOX            0x00800000
#define PUCLASS_SELECTBOX           0x01000000
#define PUCLASS_SPINBOX             0x02000000
#define PUCLASS_SCROLLBAR           0x04000000
#define PUCLASS_BISLIDERWITHENDS    0x08000000
#define PUCLASS_SLIDERWITHINPUT     0x10000000
#define PUCLASS_COMPASS             0x20000000
#define PUCLASS_CHOOSER             0x40000000
#define PUCLASS_LIST                0x80000000


// Widget Declarations
class puaSpinBox          ;
class puaFileSelector     ;
class puaBiSlider         ;
class puaTriSlider        ;
class puaVerticalMenu     ;
class puaLargeInput       ;
class puaComboBox         ;
class puaSelectBox        ;
class puaScrollBar        ;
class puaBiSliderWithEnds ;
class puaCompass          ;
class puaSliderWithInput  ;
class puaChooser          ;
class puaList             ;


// A File selector widget
class puaFileSelector : public puDialogBox
{
  UL_TYPE_DATA

protected:
  char** files ;
  char*  dflag ;
  int num_files   ;
  int arrow_count ;

  char *startDir ;

  void find_files ( void ) ;
  static void handle_select ( puObject* ) ;
  static void input_entered ( puObject* ) ;

  puFrame   *frame         ;
  puListBox *list_box      ;
  puSlider  *slider        ;
  puOneShot *cancel_button ;
  puOneShot *ok_button     ;
  puInput   *input         ;
  puArrowButton *up_arrow       ;
  puArrowButton *down_arrow     ;
  puArrowButton *fastup_arrow   ;
  puArrowButton *fastdown_arrow ;

  void puaFileSelectorInit ( int x, int y, int w, int h,
                             int arrows, const char *dir, const char *title ) ;

public:

  puaFileSelector ( int x, int y, int w, int h, int arrows,
                    const char *dir, const char *title = "Pick a file" ) :
      puDialogBox ( x, y )
  {
    puaFileSelectorInit ( x, y, w, h, arrows, dir, title ) ;
  }

  puaFileSelector ( int x, int y, int w, int h,
                    const char *dir, const char *title = "Pick a file" ) :
      puDialogBox ( x, y )
  {
    puaFileSelectorInit ( x, y, w, h, 1, dir, title ) ;
  }

  puaFileSelector ( int x, int y, int arrows,
                    const char *dir, const char *title = "Pick a file" ) :
      puDialogBox ( x, y )
  {
    puaFileSelectorInit ( x, y, 220, 170, arrows, dir, title ) ;
  }

  puaFileSelector ( int x, int y,
                    const char *dir, const char *title = "Pick a file" ) :
      puDialogBox ( x, y )
  {
    puaFileSelectorInit ( x, y, 220, 170, 1, dir, title ) ;
  }

  ~puaFileSelector () ;

  /* Not for application use!! */
  puInput *__getInput ( void ) const { return input ; }
  char *__getStartDir ( void ) const { return (char *) startDir ; }

  void setInitialValue ( const char *fname ) ;
  void setSize ( int w, int h ) ;
} ;


// An input box that takes an arbitrary number of lines of input
class puaLargeInput : public puInputBase, public puGroup
{
  UL_TYPE_DATA

protected:
  int num_lines ;               // Number of lines of text in the box
  int lines_in_window ;         // Number of lines showing in the window
  int top_line_in_window ;      // Number of the first line in the window
  int max_width ;               // Width of longest line of text in box, in pixels
  int slider_width ;
  int line_height ;             // Text height + interline gap
  int vgap ;                    // Gap on top and bottom
  int hgap ;                    // Gap on the left and right side
  int input_width ;             // Width of display area (widget minus gaps & slider)
  int input_height ;            // Height of display area

  puFrame *frame ;
  puFrame *plug ;               // Little square in the bottom right corner

  puaScrollBar *bottom_slider ; // Horizontal slider at bottom of window
  puaScrollBar *right_slider ;  // Vertical slider at right of window

  int arrow_count ;             // Number of up/down arrows above and below the right slider

  void normalizeCursors ( void ) ;
  void removeSelectRegion ( void ) ;
  void updateGeometry ( void ) ;

  char *wrapText ( int target_width = 0, int *numlines = 0, int *maxwidth = 0 ) ;
  void getTextProperties(int *numlines, int *maxwidth) ;
  char *getText () { return bottom_slider ? getStringValue () : getDisplayedText () ; }

public:
  puaLargeInput ( int x, int y, int w, int h, int arrows, int sl_width, int wrap_text = FALSE ) ;
  ~puaLargeInput ()
  {
    if ( puActiveWidget() == this )
      puDeactivateWidget () ;
  }

  void setSize ( int w, int h ) ;
  void setLegendFont ( puFont f ) { puGroup::setLegendFont ( f ) ; updateGeometry () ; }

  int getNumLines ( void ) const { return num_lines ; }
  int getLinesInWindow ( void ) const { return lines_in_window ; }
  void setTopLineInWindow ( int val ) { top_line_in_window = (val<0) ? 0 : ( (val>num_lines-2) ? num_lines-2 : val ) ;  }
  void setSliderPosition ( float fraction ) ;

  void draw     ( int dx, int dy ) ;
  int  checkHit ( int button, int updown, int x, int y ) ;
  void doHit    ( int button, int updown, int x, int y ) ;
  int  checkKey ( int key, int updown ) ;

  void setSelectRegion ( int s, int e ) ;
  void selectEntireLine ( void ) ;

  void invokeDownCallback ( void )
  {
    rejectInput () ;
    normalizeCursors () ;
    if ( down_cb != NULL ) (*down_cb)(this) ;
  }

  void  setValue ( const char *s ) ;
  void  addNewLine ( const char *l ) ;
  void  addText ( const char *l ) ;
  void  appendText ( const char *l ) ;
  void  removeText ( int start, int end ) ;
  char *getDisplayedText ( void )
  {
    return ( displayed_text == NULL ? getStringValue () : displayed_text ) ;
  }
} ;


// A box that contains a set of alternatives that drop down when the user clicks on the down-arrow
// Defined constants telling how the callback got triggered.  This is needed for
// cases in which the user has deleted the entry in the input box and wants that
// entry deleted from the list of items.
#define PUACOMBOBOX_CALLBACK_NONE      0
#define PUACOMBOBOX_CALLBACK_INPUT     1
#define PUACOMBOBOX_CALLBACK_ARROW     2
class puaComboBox : public puGroup
{
  UL_TYPE_DATA

protected:
  char ** list  ;
  int num_items ;

  int curr_item ;

  puInput *input ;
  puArrowButton *arrow_btn ;
  puPopupMenu *popup_menu  ;

  int callback_source ;

  static void input_cb ( puObject *inp   ) ;
  static void input_active_cb ( puObject *inp   ) ;
  static void input_down_cb ( puObject *inp   ) ;
  static void handle_arrow ( puObject *arrow  ) ;
  static void handle_popup ( puObject *popupm ) ;

  void update_widgets ( void ) ;
  void update_current_item ( void ) ;
  void setCallbackSource ( int s )  {  callback_source = s ;  }

public:
  /* Not for application use ! */
  puPopupMenu * __getPopupMenu ( void ) const { return popup_menu ; }

  void newList ( char ** _list ) ;
  int  getNumItems ( void ) const { return num_items ; }
  char *getNewEntry ( void ) const { return input->getStringValue () ; }

  int  getCurrentItem ( void ) ;
  void setCurrentItem ( int item )
  {
    if ( ( item >= 0 ) && ( item < num_items ) )
    {
      curr_item = item ;
      update_widgets () ;

      callback_source = PUACOMBOBOX_CALLBACK_ARROW ;
      invokeCallback () ;
    }
  }

  void setCurrentItem ( const char *item_ptr ) ;

  void setPosition ( int x, int y )
  {
    puGroup::setPosition ( x, y ) ;

    /* Ensure that popup menu will show up at the right place */
    newList ( list ) ;
  }

  void setSize ( int w, int h ) ;

  void setValue ( float f ) { puValue::setValue ( f ) ;  input->setValue ( f ) ; }
  void setValue ( int i ) { puValue::setValue ( i ) ;  input->setValue ( i ) ; }
  void setValue ( const char *s ) { puValue::setValue ( s ) ;  input->setValue ( s ) ; }
  void setValue ( puValue *pv ) { puValue::setValue ( pv ) ;  input->setValue ( pv ) ; }

  void draw ( int dx, int dy ) ;
  int  checkHit ( int button, int updown, int x, int y ) ;
  int  checkKey ( int key, int updown ) ;

  int  getCallbackSource ( void ) const  {  return callback_source ;  }

  virtual void setColourScheme ( float r, float g, float b, float a = 1.0f ) ;
  virtual void setColour ( int which, float r, float g, float b, float a = 1.0f ) ;

  puaComboBox ( int minx, int miny, int maxx, int maxy,
                char **list, int editable = TRUE ) ;

  ~puaComboBox ()
  {
    int i ;
    for ( i = 0; i < num_items; i++ )
      delete [] list[i] ;

    delete [] list ;
  }
} ;


// Like a menu bar, but the selections are one above the other
class puaVerticalMenu : public puGroup
{
  UL_TYPE_DATA

protected:
public:
  puaVerticalMenu ( int x = -1, int y = -1 ) :

  puGroup ( x < 0 ? puGetWindowWidth() -
                     ( puGetDefaultLegendFont().getStringWidth ( " " )
                       + PUSTR_TGAP + PUSTR_BGAP ) : x,

          y < 0 ? puGetWindowHeight() -
                   ( puGetDefaultLegendFont().getStringHeight ()
                     + PUSTR_TGAP + PUSTR_BGAP ) : y)
  {
    type |= PUCLASS_VERTMENU ;
    floating = TRUE ;  // DEPRECATED! -- we need to replace this code.
    if ( y < 0 ) { setVStatus( TRUE ) ; } /* It is now supposed to stick to the top left - JCJ*/
  }

  void add_submenu ( const char *str, char *items[], puCallback _cb[],
                     void *_user_data[] = NULL ) ;
  void close ( void ) ;
} ;


// Not sure what this one does ...
class puaSelectBox : public puGroup
{
  UL_TYPE_DATA

protected:
  char ** list  ;
  int num_items ;

  int curr_item ;

  puInput *input ;
  puArrowButton *down_arrow ;
  puArrowButton *up_arrow   ;

  static void handle_arrow ( puObject *arrow ) ;

  void update_widgets ( void ) ;

public:
  void newList ( char ** _list ) ;
  int  getNumItems ( void ) const { return num_items ; }

  int  getCurrentItem ( void ) const { return curr_item ; }
  void setCurrentItem ( int item )
  {
    if ( ( item >= 0 ) && ( item < num_items ) )
    {
      curr_item = item ;
      update_widgets () ;

      invokeCallback () ;
    }
  }

  void setSize ( int w, int h ) ;
  void draw ( int dx, int dy ) ;
  int  checkKey ( int key, int updown ) ;

  virtual void setColourScheme ( float r, float g, float b, float a = 1.0f ) ;
  virtual void setColour ( int which, float r, float g, float b, float a = 1.0f ) ;

  puaSelectBox ( int minx, int miny, int maxx, int maxy,
                 char **list ) ;
} ;


// A slider with up- and down-arrows on its ends
class puaScrollBar : public puSlider
{
  UL_TYPE_DATA

protected:
  int arrow_count ;
  int active_arrow ;
  float line_step_size ;
  enum { NONE = 0, FASTDOWN = 1, DOWN = 2, UP = 4, FASTUP = 8 };

public:
  void doHit ( int button, int updown, int x, int y ) ;
  void draw  ( int dx, int dy ) ;
  puaScrollBar ( int minx, int miny, int sz, int arrows, int vertical = FALSE ) :
      puSlider ( minx, miny, sz, vertical )
  {
    type |= PUCLASS_SCROLLBAR ;
    arrow_count = arrows ;
    active_arrow = NONE ;
    line_step_size = 0.0f ;
  }

  /* Alternate constructor which lets you explicitly set width */

  puaScrollBar ( int minx, int miny, int sz, int arrows, int vertical, int width ) :
      puSlider ( minx, miny, sz, vertical, width )
  {
    type |= PUCLASS_SCROLLBAR ;
    arrow_count = arrows ;
    active_arrow = NONE ;
    line_step_size = 0.0f ;
  }

  void setMaxValue ( float f )
  {
    maximum_value = f ;
    slider_fraction = 1.0f / ( getMaxValue() - getMinValue() + 1.0f ) ;
    puPostRefresh () ;
  }


  void setMinValue ( float i )
  {
    minimum_value = i ;
    slider_fraction = 1.0f / ( getMaxValue() - getMinValue() + 1.0f ) ;
    puPostRefresh () ;
  }

  void setLineStepSize ( float s ) { line_step_size = s ; }
  float getLineStepSize ( void ) const { return line_step_size ; }
} ;



// A puSlider with two slide boxes instead of one
class puaBiSlider : public puSlider
{
  UL_TYPE_DATA

protected:
  float current_max ;
  float current_min ;

  int active_button ;  // Zero for none, one for min, two for max
public:
  void doHit ( int button, int updown, int x, int y ) ;
  void draw  ( int dx, int dy ) ;
  puaBiSlider ( int minx, int miny, int sz, int vertical = FALSE ) :
     puSlider ( minx, miny, sz, vertical )
  {
    type |= PUCLASS_BISLIDER ;
    setMaxValue ( 1.0f ) ;
    setMinValue ( 0.0f ) ;
    setStepSize ( 1.0f ) ;
    current_max = 1.0f ;
    current_min = 0.0f ;
    active_button = 0 ;
  }

  /* Alternate constructor which lets you explicitly set width */

  puaBiSlider ( int minx, int miny, int sz, int vertical, int width ) :
     puSlider ( minx, miny, sz, vertical, width )
  {
    type |= PUCLASS_BISLIDER ;
    setMaxValue ( 1.0f ) ;
    setMinValue ( 0.0f ) ;
    setStepSize ( 1.0f ) ;
    current_max = 1.0f ;
    current_min = 0.0f ;
    active_button = 0 ;
  }

  void setMaxValue ( float i )
  {
    maximum_value = i ;
    slider_fraction = 1.0f / ( getMaxValue() - getMinValue() + 1.0f ) ;
    puPostRefresh () ;
  }


  void setMinValue ( float i )
  {
    minimum_value = i ;
    slider_fraction = 1.0f / ( getMaxValue() - getMinValue() + 1.0f ) ;
    puPostRefresh () ;
  }

  void setCurrentMax ( int i ) { current_max = (float) i ; puPostRefresh () ; } /* DEPRECATED */
  void setCurrentMax ( float f ) { current_max = f ; puPostRefresh () ; }
  float getCurrentMax ( void ) const { return current_max ; }

  void setCurrentMin ( int i ) { current_min = (float) i ; puPostRefresh () ; } /* DEPRECATED */
  void setCurrentMin ( float f ) { current_min = f ; puPostRefresh () ; }
  float getCurrentMin ( void ) const { return current_min ; }

  void setActiveButton ( int i ) { active_button = i ; }
  int getActiveButton ( void ) const { return active_button ; }
} ;


// A puSlider with three slide boxes

class puaTriSlider : public puaBiSlider
{
  UL_TYPE_DATA

protected:
  // "active_button" is now zero for none, one for min, two for middle, three for max
  int freeze_ends ;  // true to make end sliders unmovable
public:
  void doHit ( int button, int updown, int x, int y ) ;
  void draw  ( int dx, int dy ) ;
  puaTriSlider ( int minx, int miny, int sz, int vertical = FALSE ) :
     puaBiSlider ( minx, miny, sz, vertical )
  {
    type |= PUCLASS_TRISLIDER ;
    freeze_ends = TRUE ;
  }

  /* Alternate constructor which lets you explicitly set width */

  puaTriSlider ( int minx, int miny, int sz, int vertical, int width ) :
     puaBiSlider ( minx, miny, sz, vertical, width )
  {
    type |= PUCLASS_TRISLIDER ;
    freeze_ends = TRUE ;
  }

  int getFreezeEnds ( void ) const { return freeze_ends ; }
  void setFreezeEnds ( int val )   { freeze_ends = val ; puPostRefresh () ; }
} ;


// A vertical puBiSlider with a puInput box above it showing the current maximum
// value and a puInput box below it showing the current minimum value

class puaBiSliderWithEnds : public puGroup
{
  UL_TYPE_DATA

protected:
  puaBiSlider *slider ;
  puInput *max_box ;
  puInput *min_box  ;

  static void handle_slider ( puObject *obj ) ;
  static void handle_max ( puObject *obj  ) ;
  static void handle_min ( puObject *obj ) ;
  static void input_down_callback ( puObject *obj ) ;

  void update_widgets ( void ) ;

public:
  // For internal use only:
  void __setMax ( float f ) { max_box->setValue ( f ) ; }
  void __setMin ( float f ) { min_box->setValue ( f ) ; }

  // For public use:

  void draw ( int dx, int dy ) ;
  int  checkHit ( int button, int updown, int x, int y )
  {
    return puGroup::checkHit ( button, updown, x, y ) ;
  }

  int  checkKey ( int key, int updown ) ;

  puaBiSliderWithEnds ( int minx, int miny, int maxx, int maxy ) ;

  void setSize ( int w, int h ) ;

  void setMaxValue ( float f ) { slider->setMaxValue ( f ) ; }
  float getMaxValue ( void ) const { return slider->getMaxValue () ; }
  void setMinValue ( float f ) { slider->setMinValue ( f ) ; }
  float getMinValue ( void ) const { return slider->getMinValue () ; }

  void setCurrentMax ( float f )
  {
    slider->setCurrentMax ( f ) ;
    max_box->setValue ( f ) ;
  }

  float getCurrentMax ( void ) const { return slider->getCurrentMax () ; }

  void setCurrentMin ( float f )
  {
    slider->setCurrentMin ( f ) ;
    min_box->setValue ( f ) ;
  }

  float getCurrentMin ( void ) const { return slider->getCurrentMin () ; }

  void setActiveButton ( int i ) { slider->setActiveButton ( i ) ; }
  int getActiveButton ( void ) const { return slider->getActiveButton () ; }

  char *getValidData ( void ) const { return max_box->getValidData () ; }
  void setValidData ( char *data ) { max_box->setValidData ( data ) ; min_box->setValidData ( data ) ; }
  void addValidData ( char *data ) { max_box->addValidData ( data ) ; min_box->addValidData ( data ) ; }

  void setCBMode ( int m ) { slider->setCBMode ( m ) ; }
  int getCBMode ( void ) const { return slider->getCBMode () ; }

  void setDelta ( float f ) { slider->setDelta ( f ) ; }
  float getDelta ( void ) const { return slider->getDelta () ; }

  float getStepSize ( void ) const { return slider->getStepSize () ; }
  void setStepSize ( float f )     { slider->setStepSize ( f )     ; }
} ;


// A vertical puSlider with a puInput box above or below it showing its value

class puaSliderWithInput : public puGroup
{
  UL_TYPE_DATA

protected:
  puSlider *slider ;
  puInput *input_box ;
  int input_position ;

  static void handle_slider ( puObject *obj ) ;
  static void handle_input ( puObject *obj ) ;
  static void input_down_callback ( puObject *obj ) ;

  void update_widgets ( void ) ;

public:
  // For internal use only:
  void __setInputBox ( float f ) { input_box->setValue ( f ) ; }

  // For public use:

  void draw ( int dx, int dy ) ;
  int  checkHit ( int button, int updown, int x, int y )
  {
    return puGroup::checkHit ( button, updown, x, y ) ;
  }

  int  checkKey ( int key, int updown ) ;

  puaSliderWithInput ( int minx, int miny, int maxx, int maxy, int above = 0 ) ;

  void setSize ( int w, int h ) ;

  void setMaxValue ( float f ) { slider->setMaxValue ( f ) ; }
  float getMaxValue ( void ) const { return slider->getMaxValue () ; }
  void setMinValue ( float f ) { slider->setMinValue ( f ) ; }
  float getMinValue ( void ) const { return slider->getMinValue () ; }

  void setValue ( int i ) { slider->setValue ( i ) ;  input_box->setValue ( i ) ; }
  void setValue ( float f ) { slider->setValue ( f ) ;  input_box->setValue ( f ) ; }
  virtual void setValue ( const char *s ) { slider->setValue ( s ) ; }
  virtual void setValue ( bool b ) { slider->setValue ( b ) ; }

  int   getIntegerValue ( void ) { return slider->getIntegerValue () ; }
  float getFloatValue ( void )   { return slider->getFloatValue ()   ; }
  char  getCharValue ( void )    { return slider->getCharValue ()    ; }
  char *getStringValue ( void )  { return slider->getStringValue ()  ; }
  bool  getBooleanValue ( void ) { return slider->getBooleanValue () ; }

  char *getValidData ( void ) const { return input_box->getValidData () ; }
  void setValidData ( char *data ) { input_box->setValidData ( data ) ; }
  void addValidData ( char *data ) { input_box->addValidData ( data ) ; }

  void setCBMode ( int m ) { slider->setCBMode ( m ) ; }
  int getCBMode ( void ) const { return slider->getCBMode () ; }

  void setDelta ( float f ) { slider->setDelta ( f ) ; }
  float getDelta ( void ) const { return slider->getDelta () ; }

  float getStepSize ( void ) const { return slider->getStepSize () ; }
  void setStepSize ( float f )     { slider->setStepSize ( f )     ; }
} ;

class puaSpinBox : public puRange, public puGroup
{
  UL_TYPE_DATA

protected :
  puInput *input_box ;
  puArrowButton *up_arrow ;
  puArrowButton *down_arrow ;

  int arrow_position ;

public :
  /* Whether the arrows are on the LEFT of the input box (0) or RIGHT (1 DEFAULT) */
  int getArrowPosition ( void ) const { return arrow_position ; }
  /* Offered as a proportion of the input box height. Default = 0.5 */
  void setArrowHeight ( float height )
  {
    puBox ibox = *(input_box->getABox()) ;
    int size = int(height * ( ibox.max[1] - ibox.min[1] )) ;
    up_arrow->setSize ( size, size ) ;
    down_arrow->setSize ( size, size ) ;
    int xpos = getArrowPosition () ? ibox.max[0] : ibox.min[0] - size ;
    int ymid = ( ibox.max[1] + ibox.min[1] ) / 2 ;
    if ( getArrowPosition () == 0 )  /* Arrows are on the left, adjust the x-position of the input box */
    {
      ibox.min[0] -= xpos ;
      input_box->setPosition ( ibox.min[0], ibox.min[1] ) ;
      abox.min[0] += xpos ;
      xpos = 0 ;
    }

    if ( height > 0.5f )  /* Adjust the input box to be up from the bottom */
    {
      input_box->setPosition ( ibox.min[0], ibox.min[1] + size - ymid ) ;
      abox.min[1] += ymid - size ;
      ymid = size ;
    }
    else  /* Input box is at the bottom of the group area */
    {
      input_box->setPosition ( ibox.min[0], 0 ) ;
      abox.min[1] += ibox.min[1] ;
    }

    up_arrow->setPosition ( xpos, ymid ) ;
    down_arrow->setPosition ( xpos, ymid - size ) ;
    recalc_bbox() ;
  }

  float getArrowHeight ( void ) const
  {
    int awid, ahgt, iwid, ihgt ;
    input_box->getSize ( &iwid, &ihgt ) ;
    up_arrow->getSize ( &awid, &ahgt ) ;
    return float(ahgt) / float(ihgt) ;
  }

  puaSpinBox ( int minx, int miny, int maxx, int maxy, int arrow_pos = 1 ) ;

  void setValue ( float f ) { puValue::setValue ( f ) ;  input_box->setValue ( f ) ; }
  void setValue ( int i ) { puValue::setValue ( i ) ;  input_box->setValue ( i ) ; }
  void setValue ( const char *s ) { puValue::setValue ( s ) ;  input_box->setValue ( s ) ; }
  void setValue ( puValue *pv ) { puValue::setValue ( pv ) ;  input_box->setValue ( pv ) ; }
} ;


/*
 * Widget that looks like a 3-d coordinate system with quarter-circles in the coordinate planes.
 * It is used in 3-d modeling to translate and rotate the scene.  The coordinates rotate with
 * the scene but do not translate.  Defined values are:
 *  0 - Nothing active
 *  1 - Dot at origin active:  reset (usually)
 *  2 - X-axis dot active:  rotate about y- and z-axes
 *  3 - Y-axis dot active:  rotate about z- and x-axes
 *  4 - Z-axis dot active:  rotate about x- and y-axes
 *  5 - X-axis bar active:  translate along x-axis
 *  6 - Y-axis bar active:  translate along y-axis
 *  7 - Z-axis bar active:  translate along z-axis
 *  8 - XY-plane arc active:  rotate about z-axis
 *  9 - YZ-plane arc active:  rotate about x-axis
 * 10 - ZX-plane arc active:  rotate about y-axis
 * 11 - X-axis, Y-axis, and XY-arc active:  translate in xy-plane
 * 12 - Y-axis, Z-axis, and YZ-arc active:  translate in yz-plane
 * 13 - Z-axis, X-axis, and ZX-arc active:  translate in zx-plane
 */
#define PUACOMPASS_INACTIVE          0
#define PUACOMPASS_RESET             1
#define PUACOMPASS_ROTATE_Y_Z        2
#define PUACOMPASS_ROTATE_Z_X        3
#define PUACOMPASS_ROTATE_X_Y        4
#define PUACOMPASS_TRANSLATE_X       5
#define PUACOMPASS_TRANSLATE_Y       6
#define PUACOMPASS_TRANSLATE_Z       7
#define PUACOMPASS_ROTATE_Z          8
#define PUACOMPASS_ROTATE_X          9
#define PUACOMPASS_ROTATE_Y         10
#define PUACOMPASS_TRANSLATE_X_Y    11
#define PUACOMPASS_TRANSLATE_Y_Z    12
#define PUACOMPASS_TRANSLATE_Z_X    13

class puaCompass : public puObject
{
  UL_TYPE_DATA

protected :
  sgQuat rotation ;
  sgVec3 translation ;
  float point_size ;
  int button_state ;
  int trigger_button ;
  float mouse_x, mouse_y, mouse_z ;
  float translation_sensitivity ;

  float xint, yint, zint ;
  float prev_angle ;
  sgQuat prev_rotation ;

public :
  puaCompass ( int minx, int miny, int maxx, int maxy ) : puObject ( minx, miny, maxx, maxy )
  {
    setValue ( PUACOMPASS_INACTIVE ) ;

//    sgSetQuat ( rotation, 1.0f, 0.0f, 0.0f, 0.0f ) ;
//    sgSetQuat ( rotation, 0.707107f, 0.707107f, 0.0f, 0.0f ) ;
//    sgSetQuat ( rotation, 0.5f, -0.5f, 0.5f, 0.5f ) ;
//    sgSetQuat ( rotation, 0.866025f, -0.166667f, 0.166667f, 0.166667f ) ;
    sgSetQuat ( rotation, 0.866025f, -0.408248f, 0.288675f, 0.0f ) ;
    sgSetVec3 ( translation, 0.0f, 0.0f, 0.0f ) ;

    point_size = 10.0f ;

    button_state = PU_UP ;
    trigger_button = active_mouse_button ;
    mouse_x = mouse_y = mouse_z = 0.0f ;
    translation_sensitivity = 1.0f ;

    xint = yint = zint = 0.0f ;
    prev_angle = 0.0f ;
    sgCopyQuat ( prev_rotation, rotation ) ;
  }

  void draw ( int dx, int dy ) ;
  void doHit ( int button, int updown, int x, int y ) ;

  // Accessors and mutators
  void getRotation ( sgQuat q ) const  {  memcpy ( q, rotation, 4 * sizeof(sgFloat) ) ;  }
  void setRotation ( sgQuat q )  {  memcpy ( rotation, q, 4 * sizeof(sgFloat) ) ;  }
  void setRotation ( sgFloat t, sgFloat x, sgFloat y, sgFloat z )
  {
    sgFloat sinth = sgSin ( t / 2.0f ) ;
    sgFloat norm = sgSqrt ( x * x + y * y + z * z ) ;
    if ( norm == 0.0 ) norm = 1.0 ;
    rotation[SG_W] = sgCos ( t / 2.0f ) ;
    rotation[SG_X] = sinth * x / norm ;
    rotation[SG_Y] = sinth * y / norm ;
    rotation[SG_Z] = sinth * z / norm ;
  }

  void getTranslation ( sgVec3 t ) const  {  memcpy ( t, translation, 3 * sizeof(sgFloat) ) ;  }
  void setTranslation ( sgVec3 t )  {  memcpy ( translation, t, 3 * sizeof(sgFloat) ) ;  }
  void setTranslation ( sgFloat x, sgFloat y, sgFloat z )  {  translation[SG_X] = x ;  translation[SG_Y] = y ;  translation[SG_Z] = z ;  }

  float getPointSize () const  {  return point_size ;  }
  void setPointSize ( float p )  {  point_size = p ;  }

  int getTriggerButton () const  {  return trigger_button ;  }
  void setTriggerButton ( int b )  {  trigger_button = b ;  }

  float getTranslationSensitivity () const  {  return translation_sensitivity ;  }
  void setTranslationSensitivity ( float t )  {  translation_sensitivity = t ;  }
} ;


class puaChooser
{
  UL_TYPE_DATA

  puButton    *chooser_button ;
  puPopupMenu *popup_menu ;

  int x1, y1, x2, y2 ;

  static void static_popup_cb ( puObject * ) ;
  static void static_menu_cb  ( puObject * ) ;

public:

  virtual ~puaChooser ()
  {
    delete chooser_button ;
    delete popup_menu     ;
  }

  puaChooser ( int _x1, int _y1, int _x2, int _y2, char *legend ) ;

  void add_item ( char *str, puCallback _cb, void *_user_data = NULL ) ;
  void close () ;

  void popup_cb () ;

  void menuCleanup  ( const char *s ) ;

  void hide   () { chooser_button -> hide   () ; popup_menu -> hide   () ; }
  void reveal () { chooser_button -> reveal () ; popup_menu -> hide   () ; }

  static void menuCleanup  ( puObject * ) ;
} ;


/**
 * A scrolling list for PUI.
 *
 * Believe it or not, PUI does not have one of these.
 *
 *  This widget consists of a puListBox, a slider and two
 *  arrow buttons. This makes the ListBox scrollable,
 *  very handy if the box is too small to show all items
 *  at once.
 *
 *  (Original code taken from FlightGear 0.9.6 sources,
 *   modified by Jan Reucker)
 */
class puaList : public puGroup
{
  UL_TYPE_DATA

  char ** _contents;
  puFrame * _frame;
  puSlider * _slider;
  puArrowButton * _up_arrow;
  puArrowButton * _down_arrow;
  int _style;
  int _sw;       // slider width
  int _width, _height;

protected:
  virtual void init (int w, int h, short transparent);
  puListBox * _list_box;

public:
  puaList (int x, int y, int w, int h, int sl_width = 20);
  puaList (int x, int y, int w, int h, char ** contents, int sl_width = 20);
  puaList (int x, int y, int w, int h, short transparent, int sl_width = 20);
  puaList (int x, int y, int w, int h, short transparent, char ** contents, int sl_width = 20);
  virtual ~puaList ();

  virtual void newList (char ** contents);

  virtual char * getStringValue ();
  virtual int    getIntegerValue ();
  virtual void   getValue (char **ps);
  virtual void   getValue (int  *i);

  virtual void   setColourScheme (float r, float g, float b, float a);
  virtual void   setColour (int which, float r, float g, float b, float a);
  virtual void   setSize (int w, int h);

  int  getNumVisible ( void ) const { return _list_box->getNumVisible(); }
  int  getNumItems   ( void ) const { return _list_box->getNumItems(); }
  int  getTopItem    ( void ) const { return _list_box->getTopItem(); }
  void setTopItem    ( int item_index );
};

#endif

