/***********************************************************************
 * FXRuby -- the Ruby language bindings for the FOX GUI toolkit.
 * Copyright (c) 2001-2003 by J. Lyle Johnson. All Rights Reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser 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 please contact the author by e-mail
 * at "lyle@users.sourceforge.net".
 ***********************************************************************/

/// List styles
enum {
  LIST_EXTENDEDSELECT    = 0,             /// Extended selection mode allows for drag-selection of ranges of items
  LIST_SINGLESELECT      = 0x00100000,    /// Single selection mode allows up to one item to be selected
  LIST_BROWSESELECT      = 0x00200000,    /// Browse selection mode enforces one single item to be selected at all times
  LIST_MULTIPLESELECT    = 0x00300000,    /// Multiple selection mode is used for selection of individual items
  LIST_AUTOSELECT        = 0x00400000,    /// Automatically select under cursor
  LIST_NORMAL            = LIST_EXTENDEDSELECT
  };


class FXIcon;
class FXFont;
class FXList;



/// List item
class FXListItem : public FXObject {
protected:
  FXString  label;
  FXIcon   *icon; 
  void     *data; 
  FXuint    state;
  FXint     x,y;
protected:
  FXListItem();
  virtual void draw(const FXList* list,FXDC& dc,FXint x,FXint y,FXint w,FXint h);
  virtual FXint hitItem(const FXList* list,FXint x,FXint y) const;
protected:
  enum {
    SELECTED  = 1,
    FOCUS     = 2,
    DISABLED  = 4,
    DRAGGABLE = 8,
    ICONOWNED = 16
    };
public:
  %extend {
    FXListItem(const FXString& text,FXIcon* ic=NULL,void* ptr=NULL){
      return new FXRbListItem(text,ic,ptr);
      }
    }
  FXString getText() const;
  FXIcon* getIcon() const;

#ifdef SWIGRUBY
  %extend {
    void setData(VALUE ptr){
      self->setData((void*) ptr);
      }
      
    VALUE getData() const {
      return self->getData() ? (VALUE) self->getData() : Qnil;
      }
  }
#endif

#ifdef SWIGPYTHON
  %extend {
    void setData(PyObject* ptr) {
      FXPySetData(self, ptr);
    }
    PyObject* getData() const {
      return FXPyGetData(self);
    }
  }
#endif

  FXbool hasFocus() const;
  FXbool isSelected() const;
  FXbool isEnabled() const;
  FXbool isDraggable() const;
  FXuint isIconOwned() const;

  virtual ~FXListItem();
  };


DECLARE_FXOBJECT_VIRTUALS(FXListItem)
DECLARE_FXLISTITEM_VIRTUALS(FXListItem)

%typemap(check) FXint LISTITEM_INDEX {
  if ($1 < 0 || $1 >= arg1->getNumItems()) {
    rb_raise(rb_eIndexError, "list item index out of bounds");
  }
}

%apply FXint LISTITEM_INDEX { FXint index };

%alias FXList::appendItem "<<";

/// List Widget
class FXList : public FXScrollArea {
protected:
  FXListItem   **items;             // Item list
  FXint          nitems;            // Number of items
  FXint          anchor;            // Anchor item
  FXint          current;           // Current item
  FXint          extent;            // Extent item
  FXint          cursor;            // Cursor item
  FXFont        *font;              // Font
  FXColor        textColor;         // Text color
  FXColor        selbackColor;      // Selected back color
  FXColor        seltextColor;      // Selected text color
  FXint          listWidth;         // List width
  FXint          listHeight;        // List height
  FXint          visible;           // Number of rows high
  FXString       help;              // Help text
  FXint          grabx;             // Grab point x
  FXint          graby;             // Grab point y
  FXString       lookup;            // Lookup string
  FXTimer       *timer;             // Tip hover timer
  FXTimer       *lookuptimer;       // Lookup timer
  FXbool         state;             // State of item
protected:
  FXList();
  virtual void layout();
  void recompute();
  virtual FXListItem *createItem(const FXString& text,FXIcon* icon,void* ptr);
public:
  long onPaint(FXObject*,FXSelector,void* PTR_EVENT);
  long onEnter(FXObject*,FXSelector,void* PTR_EVENT);
  long onLeave(FXObject*,FXSelector,void* PTR_EVENT);
  long onUngrabbed(FXObject*,FXSelector,void* PTR_EVENT);
  long onKeyPress(FXObject*,FXSelector,void* PTR_EVENT);
  long onKeyRelease(FXObject*,FXSelector,void* PTR_EVENT);
  long onLeftBtnPress(FXObject*,FXSelector,void* PTR_EVENT);
  long onLeftBtnRelease(FXObject*,FXSelector,void* PTR_EVENT);
  long onRightBtnPress(FXObject*,FXSelector,void* PTR_EVENT);
  long onRightBtnRelease(FXObject*,FXSelector,void* PTR_EVENT);
  long onMotion(FXObject*,FXSelector,void* PTR_EVENT);
  long onFocusIn(FXObject*,FXSelector,void* PTR_EVENT);
  long onFocusOut(FXObject*,FXSelector,void* PTR_EVENT);
  long onAutoScroll(FXObject*,FXSelector,void* PTR_EVENT);
  long onClicked(FXObject*,FXSelector,void* PTR_INT);
  long onDoubleClicked(FXObject*,FXSelector,void* PTR_INT);
  long onTripleClicked(FXObject*,FXSelector,void* PTR_INT);
  long onCommand(FXObject*,FXSelector,void* PTR_INT);
  long onQueryTip(FXObject*,FXSelector,void* PTR_IGNORE);
  long onQueryHelp(FXObject*,FXSelector,void* PTR_IGNORE);
  long onTipTimer(FXObject*,FXSelector,void* PTR_IGNORE);
  long onLookupTimer(FXObject*,FXSelector,void* PTR_IGNORE);
  long onCmdSetValue(FXObject*,FXSelector,void* PTR_INT);
  long onCmdGetIntValue(FXObject*,FXSelector,void* PTR_IGNORE); // FIXME
  long onCmdSetIntValue(FXObject*,FXSelector,void* PTR_PINT);
public:
  static FXint ascending(const FXListItem* a,const FXListItem* b);
  static FXint descending(const FXListItem* a,const FXListItem* b);
public:
  enum {
    ID_TIPTIMER=FXScrollArea::ID_LAST,
    ID_LOOKUPTIMER,
    ID_LAST
    };
public:
  %extend {
    /// Construct a list with nvis visible items; the list is initially empty
    FXList(FXComposite *p,FXint nvis,FXObject* tgt=NULL,FXSelector sel=0,FXuint opts=LIST_NORMAL,FXint x=0,FXint y=0,FXint w=0,FXint h=0){
      return new FXRbList(p,nvis,tgt,sel,opts,x,y,w,h);
      }
    }

  /// Return the number of items in the list
  FXint getNumItems() const;

  /// Return number of visible items
  FXint getNumVisible() const;

  /// Change the number of visible items
  void setNumVisible(FXint nvis);
  
  /// Return the item at the given index
  FXListItem *retrieveItem(FXint index) const;
  
#ifdef SWIGRUBY
  %extend {
    /// Replace the item with a [possibly subclassed] item
    FXint replaceItem(FXint index,FXListItem* item,FXbool notify=FALSE){
      // Save pointer to the soon-to-be-destroyed item
      FXListItem* oldItem=self->retrieveItem(index);

      // Do the deed
      if(item->isMemberOf(FXMETACLASS(FXRbListItem)))
        dynamic_cast<FXRbListItem*>(item)->owned=TRUE;
      FXint result=self->replaceItem(index,item,notify);

      // Now zero-out pointers held by still-alive Ruby objects
      FXRbUnregisterRubyObj(oldItem);

      // Done
      return result;
      }

    /// Replace item's text, icon, and user-data pointer
    FXint replaceItem(FXint index,const FXString& text,FXIcon *icon=NULL,void* ITEMDATA=NULL,FXbool notify=FALSE){
      // Save pointer to the soon-to-be-destroyed item
      FXListItem* oldItem=self->retrieveItem(index);

      // Do the deed
      FXint result=self->replaceItem(index,text,icon,ITEMDATA,notify);
      
      // Now zero-out pointers held by still-alive Ruby objects
      FXRbUnregisterRubyObj(oldItem);

      // Done
      return result;
      }
  }

  %extend {
    /// Insert a new [possibly subclassed] item at the given index
    FXint insertItem(FXint index,FXListItem* item,FXbool notify=FALSE){
      if(item->isMemberOf(FXMETACLASS(FXRbListItem)))
        dynamic_cast<FXRbListItem*>(item)->owned=TRUE;
      return self->insertItem(index,item,notify);
      }
  }

  /// Insert item at index with given text, icon, and user-data pointer
  FXint insertItem(FXint index,const FXString& text,FXIcon *icon=NULL,void* ITEMDATA=NULL,FXbool notify=FALSE);

  %extend {
    /// Append a [possibly subclassed] item to the list
    FXint appendItem(FXListItem* item,FXbool notify=FALSE){
      if(item->isMemberOf(FXMETACLASS(FXRbListItem)))
        dynamic_cast<FXRbListItem*>(item)->owned=TRUE;
      return self->appendItem(item,notify);
      }
  }
  
  /// Append new item with given text and optional icon, and user-data pointer
  FXint appendItem(const FXString& text,FXIcon *icon=NULL,void* ITEMDATA=NULL,FXbool notify=FALSE);

  %extend {
    /// Prepend a [possibly subclassed] item to the list
    FXint prependItem(FXListItem* item,FXbool notify=FALSE){
      if(item->isMemberOf(FXMETACLASS(FXRbListItem)))
        dynamic_cast<FXRbListItem*>(item)->owned=TRUE;
      return self->prependItem(item,notify);
      }
  }
  
  /// Prepend new item with given text and optional icon, and user-data pointer
  FXint prependItem(const FXString& text,FXIcon *icon=NULL,void* ITEMDATA=NULL,FXbool notify=FALSE);

#endif
#ifdef SWIGPYTHON
  FXint insertItem(FXint index,FXListItem* item,FXbool notify=FALSE);
  FXint insertItem(FXint index,const FXString& text,FXIcon *icon=NULL,void* ptr=NULL,FXbool notify=FALSE);

  FXint replaceItem(FXint index,FXListItem* item,FXbool notify=FALSE);
  FXint replaceItem(FXint index,const FXString& text,FXIcon *icon=NULL,void* ptr=NULL,FXbool notify=FALSE);

  FXint appendItem(FXListItem* item,FXbool notify=FALSE);
  FXint appendItem(const FXString& text,FXIcon *icon=NULL,void* ptr=NULL,FXbool notify=FALSE);

  FXint prependItem(FXListItem* item,FXbool notify=FALSE);
  FXint prependItem(const FXString& text,FXIcon *icon=NULL,void* ptr=NULL,FXbool notify=FALSE);
#endif

  %extend {
    /// Remove item from list
    void removeItem(FXint index,FXbool notify=FALSE){
      // Save pointer to the soon-to-be-destroyed item
      FXListItem* item=self->retrieveItem(index);

      // Do the deed
      self->removeItem(index,notify);

      // Now zero-out pointers held by still-alive Ruby objects
      FXRbUnregisterRubyObj(item);
      }
  
    /// Remove all items from list
    void clearItems(FXbool notify=FALSE){
      // Save pointers to the soon-to-be-destroyed items
      FXint numItems=self->getNumItems();
      FXListItem** items=0;
      if(FXMALLOC(&items,FXListItem*,numItems)){
        for(FXint i=0; i<numItems; i++){
          items[i]=self->retrieveItem(i);
          }
        }

      // Do the deed
      self->clearItems(notify);

      // Now zero-out pointers held by still-alive Ruby objects
      if(items){
        for(FXint i=0; i<numItems; i++){
          FXRbUnregisterRubyObj(items[i]);
          }
        FXFREE(&items);
        }
      }
  }
  
  /// Return item width
  FXint getItemWidth(FXint index) const;

  /// Return item height
  FXint getItemHeight(FXint index) const;

  /// Return index of item at x,y, if any
  FXint getItemAt(FXint x,FXint y) const;
  
  /// Return item hit code: 0 no hit; 1 hit the icon; 2 hit the text
  FXint hitItem(FXint index,FXint x,FXint y) const;
  
  /** 
  * Search items for item by name, starting from start item; the
  * flags argument controls the search direction, and case sensitivity.
  */
  FXint findItem(const FXString& text,FXint start=-1,FXuint flags=SEARCH_FORWARD|SEARCH_WRAP) const;
  
  /// Scroll to bring item into view
  void makeItemVisible(FXint index);
  
  %extend {
    /// Scroll to make item visible
    void makeItemVisible(FXListItem* item){
      FXint numItems=self->getNumItems();
      for(FXint i=0; i<numItems; i++){
        if(self->retrieveItem(i)==item){
          self->makeItemVisible(i);
	  break;
	  }
	}
      }
  }

  /// Change item text
  void setItemText(FXint index,const FXString& text);
  
  /// Return item text
  FXString getItemText(FXint index) const;
  
  /// Change item icon
  void setItemIcon(FXint index,FXIcon* icon);
  
  /// Return item icon, if any
  FXIcon* getItemIcon(FXint index) const;
  
#ifdef SWIGRUBY
  %extend {
    /// Change item user-data pointer
    void setItemData(FXint index, VALUE ptr){
      self->setItemData(index, (void*) ptr);
      }
  
    /// Return item user-data pointer
    VALUE getItemData(FXint index) const {
      return self->getItemData(index) ? (VALUE) self->getItemData(index) : Qnil;
      }
  } 
#endif
  
  /// Return TRUE if item is selected
  FXbool isItemSelected(FXint index) const;
  
  /// Return TRUE if item is current
  FXbool isItemCurrent(FXint index) const;
  
  /// Return TRUE if item is visible
  FXbool isItemVisible(FXint index) const;
  
  /// Return TRUE if item is enabled
  FXbool isItemEnabled(FXint index) const;
  
  /// Repaint item
  void updateItem(FXint index);
  
  /// Enable item
  FXbool enableItem(FXint index);
  
  /// Disable item
  FXbool disableItem(FXint index);
  
  /// Select item
  FXbool selectItem(FXint index,FXbool notify=FALSE);
  
  /// Deselect item
  FXbool deselectItem(FXint index,FXbool notify=FALSE);
  
  /// Toggle item selection state
  FXbool toggleItem(FXint index,FXbool notify=FALSE);
  
  /// Change current item
  void setCurrentItem(FXint index,FXbool notify=FALSE);
  
  /// Return current item, if any
  FXint getCurrentItem() const;

  /// Change anchor item
  void setAnchorItem(FXint index);
  
  /// Return anchor item, if any
  FXint getAnchorItem() const;

  /// Get item under the cursor, if any
  FXint getCursorItem() const;

  /// Extend selection from anchor item to index
  FXbool extendSelection(FXint index,FXbool notify=FALSE);
  
  /// Deselect all items
  FXbool killSelection(FXbool notify=FALSE);
  
  /// Sort items using current sort function
  void sortItems();
  
  /// Change text font
  void setFont(FXFont* fnt);
  
  /// Return text font
  FXFont* getFont() const;

  /// Return normal text color
  FXColor getTextColor() const;

  /// Change normal text color
  void setTextColor(FXColor clr);
  
  /// Return selected text background
  FXColor getSelBackColor() const;

  /// Change selected text background
  void setSelBackColor(FXColor clr);
  
  /// Return selected text color
  FXColor getSelTextColor() const;

  /// Change selected text color
  void setSelTextColor(FXColor clr);
  
  /// Return list style
  FXuint getListStyle() const;
  
  /// Change list style
  void setListStyle(FXuint style);
  
  /// Set the status line help text for this list
  void setHelpText(const FXString& text);
  
  /// Get the status line help text for this list
  FXString getHelpText() const;

  /// Destructor
  virtual ~FXList();
  };
  
%clear FXint index;

DECLARE_FXOBJECT_VIRTUALS(FXList)
DECLARE_FXID_VIRTUALS(FXList)
DECLARE_FXDRAWABLE_VIRTUALS(FXList)
DECLARE_FXWINDOW_VIRTUALS(FXList)
DECLARE_FXSCROLLAREA_VIRTUALS(FXList)

