/********************************************************************************
*                                                                               *
*                    R o l l  O u t  P a n e  W i d g e t                       *
*                                                                               *
*********************************************************************************
* Roll Out Pane Widget Copyright (C) 2000-2003 by Petri Hodju.                  *
*                Revised edition (C)      2004 by Bill Baxter.                  *
*********************************************************************************
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.            *
*********************************************************************************
* $Id: FXRollOut.cpp,v 1.5 2004/02/15 22:03:14 baxter Exp $
********************************************************************************/

#include "fx.h"
#include "fxkeys.h"
#include "FXRollOut.h"

/*
 Notes:
  - Get rid of hardcoded scrolling speed and scrollbar width

  - Internal widget hierarchy is as follows:
      FXRollOut
          FXRollOutView
              FXRollOutContent
                  FXRollOutPane
                      FXRollOutView
                          FXRollOutContent
                              userWidget
                              userWidget
                              userWidget
                  FXRollOutPane
                      FXRollOutView
                          FXRollOutContent
                              userWidget
                              userWidget
                              userWidget
            ...
  - FXRollOutFrame is made by FXRollOut as child of App.
*/

//#define BUTTON_HEIGHT   20
#define SCROLL_SPEED    20
#define SCROLLBAR_WIDTH 4

// Frame styles
#define FRAME_MASK        (FRAME_SUNKEN|FRAME_RAISED|FRAME_THICK)

//////////////////////////////////////////////////////////////////////////////////////////
// frame drawing code - dirty hack. Copy-Pasted from FXFrame

FXRollOutFrame::FXRollOutFrame(FXApp *App){
  backColor=App->getBaseColor();
  baseColor=App->getBaseColor();
  hiliteColor=App->getHiliteColor();
  shadowColor=App->getShadowColor();
  borderColor=App->getBorderColor();
}

FXRollOutFrame::~FXRollOutFrame(){
  FXTRACE((100,"FXRollOutFrame::~FXRollOutFrame %08x\n",this));
}

void FXRollOutFrame::drawBorderRectangle(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h){
  dc.setForeground(borderColor);
  dc.drawRectangle(x,y,w,h);
}

void FXRollOutFrame::drawRaisedRectangle(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h){
  dc.setForeground(shadowColor);
  dc.fillRectangle(x,y+h-1,w,1);
  dc.fillRectangle(x+w-1,y,1,h);
  dc.setForeground(hiliteColor);
  dc.fillRectangle(x,y,w,1);
  dc.fillRectangle(x,y,1,h);
}

void FXRollOutFrame::drawSunkenRectangle(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h){
  dc.setForeground(shadowColor);
  dc.fillRectangle(x,y,w,1);
  dc.fillRectangle(x,y,1,h);
  dc.setForeground(hiliteColor);
  dc.fillRectangle(x,y+h-1,w,1);
  dc.fillRectangle(x+w-1,y,1,h);
}

void FXRollOutFrame::drawRidgeRectangle(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h){
  dc.setForeground(hiliteColor);
  dc.fillRectangle(x,y,w,1);
  dc.fillRectangle(x,y,1,h);
  dc.fillRectangle(x+1,y+h-2,w-2,1);
  dc.fillRectangle(x+w-2,y+1,1,h-2);
  dc.setForeground(shadowColor);
  dc.fillRectangle(x+1,y+1,w-3,1);
  dc.fillRectangle(x+1,y+1,1,h-3);
  dc.fillRectangle(x,y+h-1,w,1);
  dc.fillRectangle(x+w-1,y,1,h);
}

void FXRollOutFrame::drawGrooveRectangle(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h){
  dc.setForeground(shadowColor);
  dc.fillRectangle(x,y,w,1);
  dc.fillRectangle(x,y,1,h);
  dc.fillRectangle(x+1,y+h-2,w-2,1);
  dc.fillRectangle(x+w-2,y+1,1,h-2);
  dc.setForeground(hiliteColor);
  dc.fillRectangle(x+1,y+1,w-2,1);
  dc.fillRectangle(x+1,y+1,1,h-2);
  dc.fillRectangle(x+1,y+h-1,w,1);
  dc.fillRectangle(x+w-1,y+1,1,h);
}

void FXRollOutFrame::drawDoubleRaisedRectangle(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h){
  dc.setForeground(hiliteColor);
  dc.fillRectangle(x,y,w-1,1);
  dc.fillRectangle(x,y,1,h-1);
  dc.setForeground(baseColor);
  dc.fillRectangle(x+1,y+1,w-2,1);
  dc.fillRectangle(x+1,y+1,1,h-2);
  dc.setForeground(shadowColor);
  dc.fillRectangle(x+1,y+h-2,w-2,1);
  dc.fillRectangle(x+w-2,y+1,1,h-1);
  dc.setForeground(borderColor);
  dc.fillRectangle(x,y+h-1,w,1);
  dc.fillRectangle(x+w-1,y,1,h);
}

void FXRollOutFrame::drawDoubleSunkenRectangle(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h){
  dc.setForeground(shadowColor);
  dc.fillRectangle(x,y,w-1,1);
  dc.fillRectangle(x,y,1,h-1);
  dc.setForeground(borderColor);
  dc.fillRectangle(x+1,y+1,w-3,1);
  dc.fillRectangle(x+1,y+1,1,h-3);
  dc.setForeground(hiliteColor);
  dc.fillRectangle(x,y+h-1,w,1);
  dc.fillRectangle(x+w-1,y,1,h);
  dc.setForeground(baseColor);
  dc.fillRectangle(x+1,y+h-2,w-2,1);
  dc.fillRectangle(x+w-2,y+1,1,h-2);
}

// Draw dashed focus rectangle
void FXRollOutFrame::drawFocusRectangle(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h){
  dc.setFillStyle(FILL_STIPPLED);
  dc.setStipple(STIPPLE_GRAY);
  dc.setForeground(borderColor);
  dc.drawRectangle(x,y,w-1,h-1);
  dc.setFillStyle(FILL_SOLID);
  dc.setStipple(STIPPLE_NONE);
}

// Draw border
void FXRollOutFrame::drawFrame(FXDCWindow& dc,FXuint options,FXint x,FXint y,FXint w,FXint h){
  switch(options&FRAME_MASK){
  case FRAME_LINE: drawBorderRectangle(dc,x,y,w,h); break;
  case FRAME_SUNKEN: drawSunkenRectangle(dc,x,y,w,h); break;
  case FRAME_RAISED: drawRaisedRectangle(dc,x,y,w,h); break;
  case FRAME_GROOVE: drawGrooveRectangle(dc,x,y,w,h); break;
  case FRAME_RIDGE: drawRidgeRectangle(dc,x,y,w,h); break;
  case FRAME_SUNKEN|FRAME_THICK: drawDoubleSunkenRectangle(dc,x,y,w,h); break;
  case FRAME_RAISED|FRAME_THICK: drawDoubleRaisedRectangle(dc,x,y,w,h); break;
  }
}

//////////////////////////////////////////////////////////////////////////////////////////
// FXRollOutContent

// Message Map
FXDEFMAP(FXRollOutContent) FXRollOutContentMap[]={
  FXMAPFUNC(SEL_PAINT,0,FXRollOutContent::onPaint)
};

// Object implementation
FXIMPLEMENT(FXRollOutContent,FXComposite,FXRollOutContentMap,ARRAYNUMBER(FXRollOutContentMap))
  
  // For deserialization
  FXRollOutContent::FXRollOutContent(){
}

// Constructor for child window creation
FXRollOutContent::FXRollOutContent(FXComposite *p,FXWindow *tgt,FXuint opts,
				   FXint x,FXint y,FXint w,FXint h,
				   FXint pl,FXint pr,FXint pt,FXint pb,
				   FXint hs,FXint vs):FXComposite(p,opts,x,y,w,h){
  FXTRACE((100,"FXRollOutContent::FXRollOutContent %08x\n",this));
  // Send messages to FXRollOut widget (tgt == FXRollOut)
  setTarget(tgt);
  padleft=pl;
  padright=pr;
  padtop=pt;
  padbottom=pb;
  hspacing=hs;
  vspacing=vs;
  border=0;
  flags|=FLAG_SHOWN|FLAG_ENABLED;
}

FXint FXRollOutContent::getDefaultWidth(){
  return content_width;
}

FXint FXRollOutContent::getDefaultHeight(){
  return content_height;
}

void FXRollOutContent::create(){
  FXComposite::create();
  FXWindow *child;
  FXint t;
  
  // Calculate the initial size of the content. Use FXVerticalFrame layout concept.
  adjust();
  
  // Calculate the width of the content
  FXint w=FXComposite::getDefaultWidth();
  for(child=getFirst(); child; child=child->getNext()){
    t=child->getX()+child->getDefaultWidth();
    if(w<t) w=t;
  }
  // Calculate the height of the content
  FXint h=FXComposite::getDefaultHeight();
  for(child=getFirst(); child; child=child->getNext()){
    t=child->getY()+child->getDefaultHeight();
    if(h<t) h=t;
  }
  
  // Store this information for later use
  content_width=w;
  content_height=h+1; // +1 == cosmetics...

  // If the view type we are contained in is LIST_VIEW type add one to height.. cosmetics..
  if(((FXRollOutView *)getParent())->getViewType()==LIST_VIEW)
    h++;
  
  // Resize the content to initial size
  resize(w,h);
}

// Dummy layout. Might have impact on scrolling performance if left out.
void FXRollOutContent::layout(){
  flags&=~FLAG_DIRTY;
}

void FXRollOutContent::adjust(FXint _w, FXint _h){
  FXint left,right,top,bottom;
  FXint mw=0,mh=0;
  FXint remain,extra_space,total_space,t;
  FXint x,y,w,h;
  FXint numc=0;
  FXint sumexpand=0;
  FXint numexpand=0;
  FXint e=0;
  FXuint hints;
  FXWindow* child;
  
  // Placement rectangle; right/bottom non-inclusive
  left=border+padleft;
  right=width-border-padright;
  top=border+padtop;
  bottom=height-border-padbottom;
  remain=bottom-top;
  
  // Get maximum child size
  if(options&PACK_UNIFORM_WIDTH) mw=((_w)?_w:maxChildWidth())-padleft-padright;
  if(options&PACK_UNIFORM_HEIGHT) mh=((_h)?_h:maxChildHeight())-padtop-padbottom;
  
  // Find number of paddable children and total height
  for(child=getFirst(); child; child=child->getNext()){
    if(child->shown()){
      hints=child->getLayoutHints();
      if(!((hints&LAYOUT_BOTTOM)&&(hints&LAYOUT_CENTER_Y))){    // LAYOUT_FIX_Y
        if(hints&LAYOUT_FIX_HEIGHT) h=child->getHeight();
        else if(options&PACK_UNIFORM_HEIGHT) h=mh;
        else h=child->getDefaultHeight();
        FXASSERT(h>=0);
        if((hints&LAYOUT_CENTER_Y) || ((hints&LAYOUT_FILL_Y) && !(hints&LAYOUT_FIX_HEIGHT))){
          sumexpand+=h;
          numexpand+=1;
        }
        else{
          remain-=h;
        }
        numc++;
      }
    }
  }
  
  // Child spacing
  if(numc>1) remain-=vspacing*(numc-1);
  
  // Do the layout
  for(child=getFirst(); child; child=child->getNext()){
    if(child->shown()){
      hints=child->getLayoutHints();
      
      // Determine child width
      if(hints&LAYOUT_FIX_WIDTH) w=child->getWidth();
      else if(options&PACK_UNIFORM_WIDTH) w=mw;
      else if(hints&LAYOUT_FILL_X) w=right-left;
      else w=child->getDefaultWidth();
      
      // Determine child x-position
      if((hints&LAYOUT_RIGHT)&&(hints&LAYOUT_CENTER_X)) x=child->getX();
      else if(hints&LAYOUT_CENTER_X) x=left+(right-left-w)/2;
      else if(hints&LAYOUT_RIGHT) x=right-w;
      else x=left;
      
      // Layout child in Y
      y=child->getY();
      if(hints&LAYOUT_FIX_HEIGHT) h=child->getHeight();
      else if(options&PACK_UNIFORM_HEIGHT) h=mh;
      else h=child->getDefaultHeight();
      if(!((hints&LAYOUT_BOTTOM)&&(hints&LAYOUT_CENTER_Y))){    // LAYOUT_FIX_Y
        extra_space=0;
        total_space=0;
        if((hints&LAYOUT_FILL_Y) && !(hints&LAYOUT_FIX_HEIGHT)){
          if(sumexpand>0){                            // Divide space proportionally to height
            t=h*remain;
            FXASSERT(sumexpand>0);
            h=t/sumexpand;
            e+=t%sumexpand;
            if(e>=sumexpand){h++;e-=sumexpand;}
          }
          else{                                       // Divide the space equally
            FXASSERT(numexpand>0);
            h=remain/numexpand;
            e+=remain%numexpand;
            if(e>=numexpand){h++;e-=numexpand;}
          }
        }
        else if(hints&LAYOUT_CENTER_Y){
          if(sumexpand>0){                            // Divide space proportionally to height
            t=h*remain;
            FXASSERT(sumexpand>0);
            total_space=t/sumexpand-h;
            e+=t%sumexpand;
            if(e>=sumexpand){total_space++;e-=sumexpand;}
          }
          else{                                       // Divide the space equally
            FXASSERT(numexpand>0);
            total_space=remain/numexpand-h;
            e+=remain%numexpand;
            if(e>=numexpand){total_space++;e-=numexpand;}
          }
          extra_space=total_space/2;
        }
        if(hints&LAYOUT_BOTTOM){
          y=bottom-h-extra_space;
          bottom=bottom-h-hspacing-total_space;
        }
        else{/*hints&LAYOUT_TOP*/
          y=top+extra_space;
          top=top+h+vspacing+total_space;
        }
      }
      child->position(x,y,w,h);
    }
  }
}

void FXRollOutContent::enable(){
  // Re-route this to actual FXRollOutPane widget
  getParent()->getParent()->enable();
}

void FXRollOutContent::disable(){
  // Re-route this to actual FXRollOutPane widget
  getParent()->getParent()->disable();
}

// Destructor
FXRollOutContent::~FXRollOutContent(){
  FXTRACE((100,"FXRollOutContent::~FXRollOutContent %08x\n",this));
}

///////////////////////////////////////
// FXRollOutContent Message Handlers

long FXRollOutContent::onPaint(FXObject*,FXSelector,void *ptr){ 
  FXEvent *ev=(FXEvent *)ptr;  
  FXDCWindow dc(this);
  dc.setForeground(getApp()->getBaseColor());
  dc.fillRectangle(ev->rect.x,ev->rect.y,ev->rect.w,ev->rect.h);
  return 1; 
}

//////////////////////////////////////////////////////////////////////////////////////////
// FXRollOutView

// Message Map
FXDEFMAP(FXRollOutView) FXRollOutViewMap[]={
  FXMAPFUNC(SEL_PAINT,0,FXRollOutView::onPaint)
};

// Object implementation
FXIMPLEMENT(FXRollOutView,FXComposite,FXRollOutViewMap,ARRAYNUMBER(FXRollOutViewMap))
  
  // For deserialization
  FXRollOutView::FXRollOutView(){
}

// Constructor for child window creation
FXRollOutView::FXRollOutView(FXComposite *p,FXWindow *tgt,FXuint opts,FXint x,FXint y,
                             FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb,
                             FXint hs,FXint vs):FXComposite(p,opts,x,y,w,h){
  FXTRACE((100,"FXRollOutView::FXRollOutView %08x\n",this));
  content=new FXRollOutContent(this,tgt,opts&PACK_UNIFORM_WIDTH,0,0,w,h,pl,pr,pt,pb,hs,vs);
  setTarget(tgt);
  flags|=FLAG_SHOWN|FLAG_ENABLED;
}

// Scroll content
void FXRollOutView::scrollContent(FXint dx,FXint dy){
  content->move(content->getX()+dx,content->getY()+dy);
}

FXint FXRollOutView::getDefaultWidth(){
  return content->getDefaultWidth();
}

FXint FXRollOutView::getDefaultHeight(){
  return content->getDefaultHeight();
}

// Create
void FXRollOutView::create(){
  FXComposite::create();
  resize(getContentWidth(),0);
}

// Dummy layout. Might have impact on scrolling performance if left out.
void FXRollOutView::layout(){
  flags&=~FLAG_DIRTY;
}

// Destructor
FXRollOutView::~FXRollOutView(){
  FXTRACE((100,"FXRollOutView::~FXRollOutView %08x\n",this));
  delete content;
}

///////////////////////////////////////
// FXRollOutView Message Handlers

long FXRollOutView::onPaint(FXObject*,FXSelector,void *ptr){
  FXEvent *ev=(FXEvent *)ptr;  
  FXDCWindow dc(this);
  dc.setForeground(getApp()->getBaseColor());
  dc.fillRectangle(ev->rect.x,ev->rect.y,ev->rect.w,ev->rect.h);
  return 1;
}

//////////////////////////////////////////////////////////////////////////////////////////
// FXRollOutPaneToggle

class FXRollOutPaneToggle : public FXToggleButton
{
  FXDECLARE(FXRollOutPaneToggle)
public:
protected:
  FXRollOutPaneToggle(){};
  void press(FXbool dn);
private:
  FXRollOutPaneToggle(const FXRollOutPaneToggle&);
  FXRollOutPaneToggle& operator=(const FXRollOutPaneToggle&);
public:
  // Message Handlers
  long onPaint(FXObject*,FXSelector,void *);
public:
  /// Construct toggle button with two text labels, and two icons, one for each state
  FXRollOutPaneToggle(FXComposite* p,const FXString& text1,const FXString& text2,
    FXIcon* icon1=NULL,FXIcon* icon2=NULL,
    FXObject* tgt=NULL,FXSelector sel=0,FXuint opts=TOGGLEBUTTON_NORMAL,
    FXint x=0,FXint y=0,FXint w=0,FXint h=0,
    FXint pl=DEFAULT_PAD,FXint pr=DEFAULT_PAD,FXint pt=DEFAULT_PAD,FXint pb=DEFAULT_PAD)
    : FXToggleButton(p,text1,text2,icon1,icon2,tgt,sel,opts,x,y,w,h,pl,pr,pt,pb) {}

  /// Destructor
  virtual ~FXRollOutPaneToggle() {}
};
// Message Map
FXDEFMAP(FXRollOutPaneToggle) FXRollOutPaneButtonMap[]={
  FXMAPFUNC(SEL_PAINT,0,FXRollOutPaneToggle::onPaint),
};
// Object implementation
FXIMPLEMENT(FXRollOutPaneToggle,FXToggleButton,FXRollOutPaneButtonMap,ARRAYNUMBER(FXRollOutPaneButtonMap))
//FXIMPLEMENT(FXRollOutPaneToggle,FXToggleButton,0,0)
// Object methods

long FXRollOutPaneToggle::onPaint(FXObject*o,FXSelector s,void *ptr)
{
  return FXToggleButton::onPaint(o,s,ptr);
}


//////////////////////////////////////////////////////////////////////////////////////////
// FXRollOutPane

// Message Map
FXDEFMAP(FXRollOutPane) FXRollOutPaneMap[]={
  FXMAPFUNC(SEL_PAINT,0,FXRollOutPane::onPaint),
  FXMAPFUNC(SEL_TIMEOUT,FXRollOutPane::ID_PANE_TIMEOUT,FXRollOutPane::onTimeout),
  FXMAPFUNC(SEL_COMMAND,FXRollOutPane::ID_PANE_BUTTON,FXRollOutPane::onButton),
  FXMAPFUNC(SEL_FOCUS_UP,0,FXRollOutPane::onFocusUp)
};

// Object implementation
FXIMPLEMENT(FXRollOutPane,FXComposite,FXRollOutPaneMap,ARRAYNUMBER(FXRollOutPaneMap))
  
  // For deserialization
  FXRollOutPane::FXRollOutPane(){
}

// Constructor for child window creation
FXRollOutPane::FXRollOutPane(FXComposite *p,
                             const FXString& text,const FXString& text2,
                             FXIcon *ic, FXIcon *ic2, 
                             FXWindow *tgt,FXuint opts, FXuint button_opts,
                             FXint x,FXint y,FXint w,FXint h)
  :FXComposite(p,opts,x,y,w,h)
{
  FXTRACE((100,"FXRollOutPane::FXRollOutPane %08x\n",this));
  // Prepare style for expand/collapse button
  // Add toolbar style if present
  border=(opts&FRAME_THICK)?2:(opts&(FRAME_SUNKEN|FRAME_RAISED))?1:0;  
  lrborder = border+2;
  button=new FXRollOutPaneToggle(
    this,text,text2,ic,ic2,this,ID_PANE_BUTTON,button_opts,
    lrborder+1,border+1,w-2*(lrborder+1));
  view=new FXRollOutView(this,tgt,opts&PACK_UNIFORM_WIDTH,0,0,w-2*lrborder,0,2,2,2,2,2,2);
  view->setViewType(PANE_VIEW);
  frame=new FXRollOutFrame(getApp());
  //resize(w,BUTTON_HEIGHT+2*border+2);
  timer=NULL;
  scrolling=0;
  setTarget(tgt);
  flags|=FLAG_SHOWN|FLAG_ENABLED;
}

FXint FXRollOutPane::getDefaultWidth(){
  return 2*(border+1)+FXMAX(button->getDefaultWidth(),view->getDefaultWidth());
}

FXint FXRollOutPane::getDefaultHeight(){
  return button->getDefaultHeight()+border+1;
}

void FXRollOutPane::create(){
  FXComposite::create();
//  int BUTTON_HEIGHT=2*BUTTON_PAD+getApp()->getNormalFont()->getFontHeight();
  if (options&ROLLOUT_OPEN) {
    FXuint saveopt = options;
    options &= ~(SMOOTH_ROLLOUT);
    button->setState(true);
    mode = ID_PANE_OPENING;
    onButton(this,0,0);
    options = saveopt;
  }
  FXint btnh = button->getDefaultHeight();
  resize(2*(border+1)+view->getDefaultWidth(),btnh+2*border+2);
  view->move(lrborder,border+btnh+1);
  button->resize(view->getDefaultWidth()-2*(lrborder+1),btnh);
}

// Dummy layout. Might have impact on scrolling performance if left out.
void FXRollOutPane::layout(){
  flags&=~FLAG_DIRTY;
}

// Adjust all panes to desired width
void FXRollOutPane::adjust(FXint w, FXint h){
  resize(w,getHeight());
  view->resize(w-2*lrborder,view->getHeight());
  view->getContent()->resize(w-2*lrborder,view->getContent()->getHeight());
  view->getContent()->adjust(w-2*lrborder,h);
//  int BUTTON_HEIGHT=2*BUTTON_PAD+getApp()->getNormalFont()->getFontHeight();
  button->resize(w-2*(lrborder+1),button->getDefaultHeight());
}
void FXRollOutPane::enable(){
  // If user wants to enable this pane -> enable the expand/collapse button
  button->enable();
}
void FXRollOutPane::rollout() {
  button->setState(true);
}
void FXRollOutPane::rollup() {
  button->setState(false);
}

void FXRollOutPane::disable(){
  FXint ch=view->getContentHeight();
  // If we do not have any content -> do nothing
  if(ch>0){
    switch(button->getState()){
    case TRUE:
      button->setState(FALSE);
      mode=ID_PANE_CLOSING;
      timer=getApp()->addTimeout(this,FXRollOutPane::ID_PANE_TIMEOUT,5);
      break;
    }
  }
  button->disable();
}

// Destructor
FXRollOutPane::~FXRollOutPane(){
  FXTRACE((100,"FXRollOut::~FXRollOut %08x\n",this));
  delete view;
  delete button;
  delete frame;
}

/////////////////////////////////// ////
// Message Handlers

long FXRollOutPane::onPaint(FXObject*,FXSelector,void *ptr){
  FXEvent *ev=(FXEvent *)ptr;
  const int hclosed = 10;
  FXDCWindow dc(this);
  dc.setForeground(getApp()->getBaseColor());
  dc.fillRectangle(ev->rect.x,ev->rect.y,ev->rect.w,ev->rect.h);
  FXint ftop = border+1+((button->getDefaultHeight()-hclosed)>>1);
  FXint fh   = height-ftop;
  if (!button->getState()) {  // collapsed!
    fh = hclosed;
  }
  frame->drawFrame(dc,options,0,ftop,width,fh);

  return 1;
}

long FXRollOutPane::onTimeout(FXObject*,FXSelector,void *){
  FXWindow *c=0;
  FXRollOutContent *con=0;
  
  // clear the timer
  timer=NULL;

  switch(mode){
  case ID_PANE_OPENING:
    // We are expanding
    scrolling|=PANE_EXPANDING;
    // Mark the height we are about to scroll
    chc=cch=view->getContentHeight()+border+1;
    // Move all descendant panes first downwards
    con=(FXRollOutContent *)getParent();
    for(c=con->getLast();c&&c!=this;c=c->getPrev())
      c->move(c->getX(),c->getY()+cch);
    // Resize parent, view and ourself
    con->resize(con->getWidth(),con->getHeight()+cch);
    view->resize(view->getWidth(),view->getHeight()+view->getContentHeight());
    resize(getWidth(),getHeight()+cch);
    // After that expand this pane smoothly
    if(options&SMOOTH_ROLLOUT&&getApp()->getAnimSpeed()!=0){
      mode=ID_PANE_EXPAND;
      view->getContent()->move(view->getContent()->getX(),-cch);
      timer=getApp()->addTimeout(this,FXRollOutPane::ID_PANE_TIMEOUT,5);
    }
    // Or just expand it in one go
    else{
      view->getContent()->move(view->getContent()->getX(),0);
      // Send update request to owner pane list
      if(target)
        {
          target->handle(this,FXSEL(SEL_UPDATE,FXRollOut::ID_PANEUPDATE),0);
          target->handle(this,FXSEL(SEL_COMMAND,FXRollOut::ID_EXPAND),0);
        }
      scrolling&=~PANE_EXPANDING;
    }
    break;
  case ID_PANE_EXPAND:
    // Expand by SCROLL_SPEED until chc < SCROLL_SPEED
    if(chc>SCROLL_SPEED){
      // Restart the timer
      timer=getApp()->addTimeout(this,FXRollOutPane::ID_PANE_TIMEOUT,5);
      // Move the content
      view->getContent()->move(view->getContent()->getX(),-chc);
      chc-=SCROLL_SPEED;
    }
    else{
      // Do the last move
      view->getContent()->move(view->getContent()->getX(),0);
      // Send update request to owner pane list
      if(target)
        {
          target->handle(this,FXSEL(SEL_UPDATE,FXRollOut::ID_PANEUPDATE),0);
          target->handle(this,FXSEL(SEL_COMMAND,FXRollOut::ID_EXPAND),0);
        }
      scrolling&=~PANE_EXPANDING;
    }
    break;
  case ID_PANE_CLOSING:
    // We are collapsing
    scrolling|=PANE_COLLAPSING;
    // Mark the height we are about to scroll
    chc=cch=view->getContentHeight()+border+1;
    mode=ID_PANE_COLLAPSE;
    timer=getApp()->addTimeout(this,FXRollOutPane::ID_PANE_TIMEOUT,5);
    break;
  case ID_PANE_COLLAPSE:
    // Collapse this pane
    // Do it smoothly
    if(options&SMOOTH_ROLLOUT&&getApp()->getAnimSpeed()!=0){
      // Collapse by SCROLL_SPEED until chc < SCROLL_SPEED
      if(chc>SCROLL_SPEED){
        // Restart the timer
        timer=getApp()->addTimeout(this,FXRollOutPane::ID_PANE_TIMEOUT,5);
        // Move the content
        view->getContent()->move(view->getContent()->getX(),chc-cch);
        chc-=SCROLL_SPEED;
        // Flush
        getApp()->flush();
        return 1;
      }
      else{
        // Do the last move
        view->getContent()->move(view->getContent()->getX(),-cch);
        // And resize view and ourself
        view->resize(view->getWidth(),0);
        resize(getWidth(),getHeight()-cch);
      }
    }
    // Or just collapse it in one go
    else{
      view->getContent()->move(view->getContent()->getX(),-cch);
      view->resize(view->getWidth(),0);
      resize(getWidth(),getHeight()-cch);
      //resize(getWidth(),button->getDefaultHeight());
    }
    con=(FXRollOutContent *)getParent();
    // And now move all descendant panes upwards
    for(c=con->getLast();c&&c!=this;c=c->getPrev())
      c->move(c->getX(),c->getY()-cch);
    // And resize the parent as well
    con->resize(con->getWidth(),con->getHeight()-cch);
    // Send update request to owner pane list
    if(target)
      {
        target->handle(this,FXSEL(SEL_UPDATE,FXRollOut::ID_PANEUPDATE),0);
        target->handle(this,FXSEL(SEL_COMMAND,FXRollOut::ID_COLLAPSE),0);
      }
    scrolling&=~PANE_COLLAPSING;
    break;
  }
  // Flush
  getApp()->flush();
  return 1 ;
}

long FXRollOutPane::onButton(FXObject *snd,FXSelector,void *){
  FXTRACE((150,"%s::onButton (%s)\n",getClassName(),snd->getClassName()));
  
  // Sanity check. If we are already expanding or collapsing, do nothing
  if(scrolling&(PANE_EXPANDING|PANE_COLLAPSING)){
    return 1;
  }
  
  // If we do not have any content -> do nothing
  switch(button->getState()){
  case TRUE: // The button is down - open pane
    // Move all descendant panes first
    mode=ID_PANE_OPENING;
    timer=getApp()->addTimeout(this,FXRollOutPane::ID_PANE_TIMEOUT,5);
    break;
  case FALSE: // The button is up - close pane
    mode=ID_PANE_CLOSING;
    timer=getApp()->addTimeout(this,FXRollOutPane::ID_PANE_TIMEOUT,5);
    break;
  }
  return 1;
}

long FXRollOutPane::onFocusUp(FXObject* obj,FXSelector sel,void *ptr){ 
  return button->handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr);
}

//////////////////////////////////////////////////////////////////////////////////////////
// FXRollOut

// Message Map
FXDEFMAP(FXRollOut) FXRollOutMap[]={
  FXMAPFUNC(SEL_PAINT,0,FXRollOut::onPaint),
  FXMAPFUNC(SEL_UPDATE,FXRollOut::ID_PANEUPDATE,FXRollOut::onUpdate),
  FXMAPFUNC(SEL_CONFIGURE,0,FXRollOut::onConfigure),
  FXMAPFUNC(SEL_MOTION,0,FXRollOut::onMouseMove),
  FXMAPTYPE(SEL_COMMAND,FXRollOut::onCommand),
  FXMAPFUNC(SEL_MOUSEWHEEL,0,FXRollOut::onMouseWheel),
  FXMAPFUNC(SEL_LEFTBUTTONPRESS,0,FXRollOut::onLeftBtnPress),
  FXMAPFUNC(SEL_LEFTBUTTONRELEASE,0,FXRollOut::onLeftBtnRelease),
  FXMAPFUNC(SEL_RIGHTBUTTONPRESS,0,FXRollOut::onRightBtnPress),
  FXMAPFUNC(SEL_RIGHTBUTTONRELEASE,0,FXRollOut::onRightBtnRelease),
  FXMAPFUNC(SEL_KEYPRESS,0,FXRollOut::onKeyPress),
  FXMAPFUNC(SEL_KEYRELEASE,0,FXRollOut::onKeyRelease),
  FXMAPFUNC(SEL_FOCUS_SELF,0,FXRollOut::onFocusSelf),
  FXMAPFUNC(SEL_FOCUSIN,0,FXRollOut::onFocusIn),
  FXMAPFUNC(SEL_FOCUSOUT,0,FXRollOut::onFocusOut),
  FXMAPFUNC(SEL_FOCUS_UP,0,FXRollOut::onFocusUp),
  FXMAPFUNC(SEL_FOCUS_DOWN,0,FXRollOut::onFocusDown),
  FXMAPFUNC(SEL_FOCUS_LEFT,0,FXRollOut::onFocusUp),
  FXMAPFUNC(SEL_FOCUS_RIGHT,0,FXRollOut::onFocusDown),
  FXMAPFUNC(SEL_FOCUS_NEXT,0,FXRollOut::onFocusDown),
  FXMAPFUNC(SEL_FOCUS_PREV,0,FXRollOut::onFocusUp)
};

// Object implementation
FXIMPLEMENT(FXRollOut,FXComposite,FXRollOutMap,ARRAYNUMBER(FXRollOutMap))
  
  // For deserialization
FXRollOut::FXRollOut(){
}

// Constructor for child window creation
FXRollOut::FXRollOut(FXComposite* p,const FXString& text,FXuint opts,
                     FXint x,FXint y,FXint w,FXint h,
                     FXint pl,FXint pr,FXint pt,FXint pb):FXComposite(p,opts,x,y,w,h){
  FXTRACE((100,"FXRollOut::FXRollOut %08x\n",this));
  border=(opts&FRAME_THICK)?2:(opts&(FRAME_SUNKEN|FRAME_RAISED))?1:0;
  view=new FXRollOutView(this,this,opts&PACK_UNIFORM_WIDTH,border+pl,border+pt,
                         w-(2*border+pl+pr)-(SCROLLBAR_WIDTH+1),h-(2*border+pt+pb),1,1,1,1,1,1);
  view->setViewType(LIST_VIEW);
  frame=new FXRollOutFrame(getApp());
  title=text;
  font=getApp()->getNormalFont();
  fonthfix=scrolling=0;
  nodrag=new FXCursor(getApp(),CURSOR_ARROW);
  drag=new FXCursor(getApp(),CURSOR_UPDOWN);
  current=nodrag;
  flags|=FLAG_SHOWN|FLAG_ENABLED;
  inHandler = false;
  padtop=pt;
  padbottom=pb;
  padleft=pl;
  padright=pr;
}

// Create a new pane and return the FXComposite on which the widgets should be constructed
FXRollOutContent *FXRollOut::createPane(const FXString& text,FXIcon *ic,FXuint opts,FXuint btn_opts){
  return createPane(text,0,ic,0,opts,btn_opts);
}

FXRollOutContent *FXRollOut::createPane(const FXString& text1,const FXString& text2,
                                        FXIcon *ic1, FXIcon *ic2,FXuint opts,FXuint btn_opts){
  FXComposite *con=view->getContent();
  FXint y=0;
  FXWindow *c=0;
  for(c=con->getFirst();c;c=c->getNext())
    y=c->getY()+c->getHeight();
  FXRollOutPane *newPane=new FXRollOutPane(con,text1,text2,ic1,ic2,this,opts,btn_opts,1,y+1,con->getWidth()-2);
  return newPane->getContent();
}

inline FXint hasHandler(const FXMetaClass *mclass,FXSelector sel)
{
  // Stop search short of FXWindow level.  FXWindow has dummy 
  // handlers installed for everything to forward to target, but it doesn't
  // actually do anything itself.
  // Any subclass of FXWindow which installs a handler, probably does it
  // in order to respond to interaction.
  FXASSERT(mclass && mclass!=&FXWindow::metaClass);
  if (mclass->search(sel)) return 1;
  if (mclass->getBaseClass()==&FXWindow::metaClass) return 0;
  return hasHandler(mclass->getBaseClass(),sel);
}


void FXRollOut::setCursor(FXCursor *cursor,FXWindow *wnd){
  FXWindow *c=0;
  
  if(!wnd)
    wnd=view->getContent();
  
  // Call recursively for all children
  for(c=wnd->getFirst();c;c=c->getNext())
    setCursor(cursor,c);
  
  // Determine if widget handles motion and button presses.
  // Really the only way to know for sure is to send the messages
  // and see how the widget responds but this gives the right answer most
  // of the time.
  // The checks for enabled and focusable just give us quick early reject
  // to avoid the recursive search through handler maps.
  if (wnd->isEnabled() && 
      !(wnd->canFocus() ||
        hasHandler(wnd->getMetaClass(),FXSEL(SEL_LEFTBUTTONPRESS,0)) ||
        hasHandler(wnd->getMetaClass(),FXSEL(SEL_MOTION,0)) ||
        hasHandler(wnd->getMetaClass(),FXSEL(SEL_RIGHTBUTTONPRESS,0))))
  {
    wnd->setDefaultCursor(cursor);
    wnd->setDragCursor(cursor);
    FXTRACE((150,"%s::setCursor(%s)\n",getClassName(),wnd->getClassName()));
  }
}

void FXRollOut::addPane(FXWindow *wnd, FXWindow *tgt){
  FXWindow *c=0;
  
  if(!tgt)
    tgt=this;
  
  // Call recursively for all children
  for(c=wnd->getFirst();c;c=c->getNext())
    addPane(c,tgt);
  
  if(!wnd->canFocus()){
    wnd->setTarget(tgt);
  }
}

void FXRollOut::checkViewPosition(FXRollOutPane *pane, FXint action)
{
  FXint cy=0, ch=0, py=0, ph=0, vh=0, sy1=0, sy2=0, sy3=0;
  FXint scroll_y=0;
  
  cy=view->getContent()->getY();
  ch=view->getContent()->getHeight();
  vh=view->getHeight();

  if(pane) 
    {
      py=pane->getY();
      ph=pane->getHeight();
    }
  
  sy1=cy+ch-vh;
  sy2=cy+py+ph-vh;
  sy3=cy+py;
  
  // Select cursor according to content and view height
  if(view->getHeight()<view->getContent()->getHeight())
    {
      if(current==nodrag)
        {
          setCursor(drag);
          setDragCursor(drag);
          view->setDefaultCursor(drag);
          view->setDragCursor(drag);
          current=drag;
        }
    }
  else
    {
      if(current==drag)
        {
          setCursor(nodrag);
          setDragCursor(nodrag);
          view->setDefaultCursor(nodrag);
          view->setDragCursor(nodrag);
          current=nodrag;
        }
    }

  switch(action)
    {
    case ID_COLLAPSE:
      scroll_y=(sy1<0&&cy<0)?-sy1:0;
      if(scroll_y)
        {
          scrollContent(0, scroll_y);
          return;
        }
      else if(pane)
        {
          scroll_y=(sy3<0)?-sy3+1:0;      
          if(!scroll_y)
            scroll_y=(sy2>0)?-sy2-1:0;
          scrollContent(0, scroll_y);
          return;
        }
      break;
    case ID_EXPAND:
      if(sy2<0&&!(sy3<0))
        return;
      scroll_y=(sy3<0)?-sy3+1:0;
      if(!scroll_y)
        scroll_y=(sy3>sy2)?-sy2-1:-sy3+1;
      scrollContent(0, scroll_y);
      return;      
      break;
    }
}

void FXRollOut::updateScrollbar()
{
  FXint cy=view->getContent()->getY();
  FXint ch=view->getContent()->getHeight();
  FXint sh=getHeight();
  FXint vh=view->getHeight();
  
  if(ch-vh>0)
    {
      sbh=(sh*vh)/ch;
      sby=(-cy*(sh-sbh))/(ch-vh);
    }
  else
    {
      sby=sbh=0;
    }
}

FXint FXRollOut::dragFromView(FXint dy)
{
  FXint cy=view->getContent()->getY();
  FXint ch=view->getContent()->getHeight();
  FXint sh=getHeight();
  FXint vh=view->getHeight();
  
  return (ch>vh)?-cy*(sh-sbh)/(ch-vh):0;
}

FXint FXRollOut::dragFromScrollbar(FXint dy)
{
  FXint cy=view->getContent()->getY();
  FXint ch=view->getContent()->getHeight();
  FXint sh=getHeight();
  FXint vh=view->getHeight();
  
  FXint mvm=ch-vh;
  FXint mbm=sh*(ch-vh);
  
  return(cy+((ch*dy-cy*vh)/vh));
}

void FXRollOut::drawScrollbar()
{
  FXDCWindow dc(this);
  FXint sh=getHeight();
  
  dc.setForeground(getApp()->getHiliteColor());
  dc.fillRectangle(width-SCROLLBAR_WIDTH,(fonthfix+border)/2,width,sby);
  dc.setForeground(getApp()->getShadowColor());
  dc.fillRectangle(width-SCROLLBAR_WIDTH,(fonthfix+border)/2+sby,width,sbh);
  dc.setForeground(getApp()->getHiliteColor());
  dc.fillRectangle(width-SCROLLBAR_WIDTH,(fonthfix+border)/2+sby+sbh,width,sh-sby-sbh);
}

FXint FXRollOut::restrictX(FXint dx)
{
  FXint cx=view->getContent()->getX();
  FXint cw=view->getContentWidth();
  FXint vw=view->getWidth();
  FXint udx=cx+cw-vw;
  
  return dx;
  
  if(dx>0)
    return (cx+dx>0)?-cx:dx;
  else
    return (udx<0)?0:(udx+dx<0)?-udx:dx;
}

FXint FXRollOut::restrictY(FXint dy)
{
  FXint cy=view->getContent()->getY();
  FXint ch=view->getContent()->getHeight();
  FXint vh=view->getHeight();
  FXint udy=cy+ch-vh;

  if(dy>0)
    return (cy+dy>0)?-cy:dy;
  else
    return (udy<0)?0:(udy+dy<0)?-udy:dy;
}

// Scroll content
void FXRollOut::scrollContent(FXint dx,FXint dy){
  view->scrollContent(restrictX(dx),restrictY(dy));
}

FXint FXRollOut::getDefaultWidth(){
  return view->getDefaultWidth()+2*border+padleft+padright+(SCROLLBAR_WIDTH+1)+1;
}

FXint FXRollOut::getDefaultHeight(){
  return view->getDefaultHeight()+2*border+padtop+padbottom+view->getY();
}

void FXRollOut::create(){
  FXComposite::create();
  FXint w = 0;
  FXWindow *child = 0;
  
  if(title!=""){
    fonthfix=getApp()->getNormalFont()->getFontHeight();
    view->position(view->getX(),view->getY()+fonthfix,view->getWidth(),view->getHeight()-fonthfix);
  }
  
  FXRollOutContent *con = view->getContent();
  
  // What is the maximum width of childs
  w = con->maxChildWidth();
  
  // And adjust all of them with that value
  for(child=con->getFirst(); child; child=child->getNext()){
    ((FXRollOutPane *)child)->adjust(w);
  }
  
  nodrag->create();
  drag->create();
}

// Dummy layout. Might have impact on scrolling performance if left out.
void FXRollOut::layout(){
  flags&=~FLAG_DIRTY;
}

// Destructor
FXRollOut::~FXRollOut(){
  FXTRACE((100,"FXRollOut::~FXRollOut %08x\n",this));
  delete view;
  delete frame;
  delete nodrag;
  delete drag;
}

///////////////////////////////////////
// Message Handlers

long FXRollOut::onPaint(FXObject*,FXSelector,void *ptr){
  FXEvent *ev=(FXEvent *)ptr;
  
  FXDCWindow dc(this);

  FXint tw,yy,xx,hh;

  tw=0;
  xx=0;
  yy=0;
  hh=height;

  // Paint background
  dc.setForeground(getApp()->getBaseColor());
  dc.fillRectangle(ev->rect.x,ev->rect.y,ev->rect.w-(SCROLLBAR_WIDTH+1),ev->rect.h);
  dc.fillRectangle(0,0,width,view->getY());
  dc.fillRectangle(width-(SCROLLBAR_WIDTH+1),(fonthfix+border)/2,1,height-(fonthfix+border)/2);

  // Draw label if there is one
  if(!title.empty()){
    tw=font->getTextWidth(title.text(),title.length());
    yy=2+font->getFontAscent()/2;
    hh=height-yy;
  }
    
  // We should really just draw what's exposed!
  switch(options&FRAME_MASK) {
  case FRAME_LINE: frame->drawBorderRectangle(dc,0,yy,width-(SCROLLBAR_WIDTH+1),hh); break;
  case FRAME_SUNKEN: frame->drawSunkenRectangle(dc,0,yy,width-(SCROLLBAR_WIDTH+1),hh); break;
  case FRAME_RAISED: frame->drawRaisedRectangle(dc,0,yy,width-(SCROLLBAR_WIDTH+1),hh); break;
  case FRAME_GROOVE: frame->drawGrooveRectangle(dc,0,yy,width-(SCROLLBAR_WIDTH+1),hh); break;
  case FRAME_RIDGE: frame->drawRidgeRectangle(dc,0,yy,width-(SCROLLBAR_WIDTH+1),hh); break;
  case FRAME_SUNKEN|FRAME_THICK: frame->drawDoubleSunkenRectangle(dc,0,yy,width-(SCROLLBAR_WIDTH+1),hh); break;
  case FRAME_RAISED|FRAME_THICK: frame->drawDoubleRaisedRectangle(dc,0,yy,width-(SCROLLBAR_WIDTH+1),hh); break;
  }
  
  // Draw label
  if(!title.empty()){
    if(options&GROUPBOX_TITLE_RIGHT) xx=width-tw-16;
    else if(options&GROUPBOX_TITLE_CENTER) xx=(width-tw)/2-4;
    else xx=8;
    dc.setForeground(backColor);
    dc.setFont(font);
    dc.fillRectangle(xx,yy,tw+8,2);
    dc.setForeground(getApp()->getForeColor());
    dc.drawText(xx+4,font->getFontAscent(),title.text(),title.length());
  }
  
  // Set drag cursor and draw scrollbar if necessary
  if(view->getHeight()<view->getContent()->getHeight())
    {
      drawScrollbar();
    }
  else
    {
      dc.setForeground(getApp()->getBaseColor());
      dc.fillRectangle(width-SCROLLBAR_WIDTH,(fonthfix+border)/2,width,height-(fonthfix+border)/2);
    }
  return 1;
}

long FXRollOut::onUpdate(FXObject*,FXSelector,void *ptr){
  update();
  return 1;
}
  
long FXRollOut::onConfigure(FXObject *snd,FXSelector sel,void *ptr){
  FXComposite::onConfigure(snd,sel,ptr);
  FXEvent *ev=(FXEvent *)ptr;
  FXTRACE((250,"%s::onConfigure\n",getClassName()));
  if(snd==getApp()){
    view->resize(
      ev->rect.w-(2*border+padleft+padright)-(SCROLLBAR_WIDTH+1),
      ev->rect.h-(2*border+padtop+padbottom)-fonthfix);
    checkViewPosition(0, ID_COLLAPSE);
    updateScrollbar();
  }
  return 1;
}

long FXRollOut::onMouseWheel(FXObject*,FXSelector,void* ptr){
  FXEvent* ev=(FXEvent*)ptr;
  FXint line=1;
  FXint jump=1;
  FXint page=8;
  FXint mov;
  if(isEnabled()){
    if(!(ev->state&(LEFTBUTTONMASK|MIDDLEBUTTONMASK|RIGHTBUTTONMASK))){
      if(ev->state&ALTMASK) jump=line;                      // Fine scrolling
      else if(ev->state&CONTROLMASK) jump=page;             // Coarse scrolling
      else jump=FXMIN(page,getApp()->getWheelLines()*line); // Normal scrolling
      mov=ev->code*jump/120;                                // Move scroll position
      scrollContent(0,mov);
      return 1;
    }
  }
  return 0;
}

long FXRollOut::onLeftBtnPress(FXObject *snd,FXSelector sel,void *ptr){
  FXTRACE((150,"%s::onLeftBtn (%s)\n",getClassName(),snd->getClassName()));
  FXint x,y,rx,ry;
  rx=((FXEvent *)ptr)->root_x;
  mouse_y=ry=((FXEvent *)ptr)->root_y;
  translateCoordinatesFrom(x,y,getApp()->getRootWindow(),((FXEvent *)ptr)->root_x,((FXEvent *)ptr)->root_y);
  if(view->getHeight()<view->getContent()->getHeight())
    {
      if(!(x<view->getX()+view->getWidth()))
        {
          setDragCursor(nodrag);
          scrolling|=PANE_SCROLLING|PANE_SCROLLBARGRAB;
          grab();
        }
      else if(x<view->getX()+view->getWidth())
        {
          scrolling|=PANE_SCROLLING;
          grab();
          setDragCursor(drag);
        }
    }
  return 1;
}

long FXRollOut::onLeftBtnRelease(FXObject *snd,FXSelector sel,void *ptr){
  FXTRACE((150,"%s::onLeftBtn (%s)\n",getClassName(),snd->getClassName()));
  if(grabbed())
    {
      ungrab();
      scrolling&=~(PANE_SCROLLING|PANE_SCROLLBARGRAB);
    }
  return 1;
}

long FXRollOut::onRightBtnPress(FXObject *snd,FXSelector sel,void *ptr){
  FXTRACE((150,"%s::onRightBtn (%s)\n",getClassName(),snd->getClassName()));
  FXint x,y,rx,ry;
  rx=((FXEvent *)ptr)->root_x;
  mouse_y=ry=((FXEvent *)ptr)->root_y;
  translateCoordinatesFrom(x,y,getApp()->getRootWindow(),((FXEvent *)ptr)->root_x,((FXEvent *)ptr)->root_y);
  if(view->getHeight()<view->getContent()->getHeight())
    {
      if(x<view->getX()+view->getWidth())
        {
          scrolling|=PANE_SCROLLING;
          grab();
          setDragCursor(drag);
        }
    }
  return 1;
}

long FXRollOut::onRightBtnRelease(FXObject *snd,FXSelector sel,void *ptr){
  FXTRACE((150,"%s::onRightBtn (%s)\n",getClassName(),snd->getClassName()));
  if(grabbed())
    {
      ungrab();
      scrolling&=~(PANE_SCROLLING|PANE_SCROLLBARGRAB);
    }
  return 1;
}

long FXRollOut::onMouseMove(FXObject *snd,FXSelector sel,void *ptr){
  FXEvent *ev=(FXEvent *)ptr;
  FXint sy=0;
  FXint vm;
  
  if(scrolling&PANE_SCROLLING){
    if(scrolling&PANE_SCROLLBARGRAB)
      {
        vm=dragFromScrollbar(ev->root_y-mouse_y);
        scrollContent(0,-vm);
      }
    else
      {
        dragFromView(ev->root_y-mouse_y);
        scrollContent(0,ev->root_y-mouse_y);    
      }
    updateScrollbar();
    drawScrollbar();
    mouse_y=ev->root_y;
  }
  return 1;
}

long FXRollOut::onFocusSelf(FXObject *snd,FXSelector sel,void *ptr){
  FXTRACE((150,"%s::onFocusSelf (%s)\n",getClassName(),snd->getClassName()));
  // Pass focus to view
  view->content->handle(snd,FXSEL(SEL_FOCUS_SELF,0),ptr);
  flags|=FLAG_FOCUSED;
  return 1;
}

long FXRollOut::onFocusIn(FXObject *snd,FXSelector sel,void *ptr){
  //FXComposite::onFocusIn(snd,sel,ptr);
  FXTRACE((150,"%s::onFocusIn (%s)\n",getClassName(),snd->getClassName()));
  flags|=FLAG_FOCUSED;
  return 1;
}

long FXRollOut::onFocusOut(FXObject *snd,FXSelector sel,void *ptr){
  //FXComposite::onFocusOut(snd,sel,ptr);
  FXTRACE((150,"%s::onFocusOut (%s)\n",getClassName(),snd->getClassName()));
  flags&=~FLAG_FOCUSED;
  return 1;
}

long FXRollOut::onFocusUp(FXObject *snd,FXSelector sel,void *ptr){
  FXTRACE((150,"%s::onFocusUp (%s)\n",getClassName(),snd->getClassName()));
  FXWindow *child;
  FXWindow *foc = view->getContent();

  while(foc->getFocus())
    foc = foc->getFocus();
  
  if(!(child=foc->getPrev()))
    child = foc->getParent();
  
  while(child){
    if(child->shown()){
      if(child->isEnabled() && child->canFocus()){
        child->handle(this,MKUINT(0,SEL_FOCUS_SELF),ptr);
        return 1;
      }
      if(child->isComposite() && child->handle(this,sel,ptr)) return 1;
    }
    if(child->getPrev())
      child = child->getPrev();
    else
      child = child->getParent();
  }
  return 0;
}

long FXRollOut::onFocusDown(FXObject *snd,FXSelector sel,void *ptr){
  FXTRACE((150,"%s::onFocusDown (%s)\n",getClassName(),snd->getClassName()));
  FXWindow *child;
  FXWindow *foc = view->getContent();
  while(foc->getFocus())
    foc = foc->getFocus();
  if(foc)
    while(!(child=foc->getNext()))
      foc=foc->getParent();
  else
    child=getFirst();
  while(child){
    if(child->shown()){
      if(child->isEnabled() && child->canFocus()){
        child->handle(this,MKUINT(0,SEL_FOCUS_SELF),ptr);
        return 1;
        }
      if(child->isComposite() && child->handle(this,sel,ptr)) return 1;
      }
    child=child->getNext();
    }
  return 0;
}

long FXRollOut::onKeyPress(FXObject *snd,FXSelector sel,void *ptr){
  FXTRACE((150,"%s::onKeyPress (%s)\n",getClassName(),snd->getClassName()));

  // prevent recursion in focus bounce
  if (inHandler)  // we've been here before and noone handled message
    return 0;

  // Bounce to focus widget
  inHandler=1;
  if(getFocus() && getFocus()->handle(snd,sel,ptr)) { 
    inHandler=0;
    return 1;
  }
  inHandler=0;

  // Perform the default keyboard processing
  switch(((FXEvent*)ptr)->code)
    {
    case KEY_Tab:
      if(((FXEvent*)ptr)->state&SHIFTMASK) goto prv;
    case KEY_Next:
      return handle(this,MKUINT(0,SEL_FOCUS_NEXT),ptr);
    case KEY_Prior:
    case KEY_ISO_Left_Tab:
prv:  return handle(this,MKUINT(0,SEL_FOCUS_PREV),ptr);
    case KEY_Up:
    case KEY_KP_Up:
      return handle(this,MKUINT(0,SEL_FOCUS_UP),ptr);
    case KEY_Down:
    case KEY_KP_Down:
      return handle(this,MKUINT(0,SEL_FOCUS_DOWN),ptr);
    case KEY_Left:
    case KEY_KP_Left:
      return handle(this,MKUINT(0,SEL_FOCUS_LEFT),ptr);
    case KEY_Right:
    case KEY_KP_Right:
      return handle(this,MKUINT(0,SEL_FOCUS_RIGHT),ptr);
    }
  return 0;
}

long FXRollOut::onKeyRelease(FXObject *snd,FXSelector sel,void *ptr){
  //FXComposite::onKeyRelease(snd,sel,ptr);
  FXTRACE((150,"%s::onKeyRelease (%s)\n",getClassName(),snd->getClassName()));
  // Bounce to focus widget
  if (inHandler)
    return 0;
  
  inHandler=1;
  if(getFocus() && getFocus()->handle(snd,sel,ptr)) {
    inHandler=0;
    return 1;
  }
  inHandler=0;

  return 0;
}

long FXRollOut::onCommand(FXObject *snd,FXSelector sel,void *ptr){
  //FXComposite::onCommand(snd,sel,ptr);
  FXTRACE((150,"%s::onCommand (%s)\n",getClassName(),snd->getClassName()));
  
  checkViewPosition((FXRollOutPane *)snd, FXSELID(sel));
  updateScrollbar();
  
  return 1; 
}

