/**************************************************************************

    This file is part of Xclass95, a Win95-looking GUI toolkit.
    Copyright (C) 1996, 1997 David Barth, Hector Peraza.

    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.

**************************************************************************/

#include<stdlib.h>
#include<stdio.h>

#include "OXView.h"

//----------------------------------------------------------

OXViewFrame::OXViewFrame(const OXWindow *p, int w, int h, unsigned int options) : 
OXCompositeFrame(p, w, h, options)
{
SetBackgroundColor(_client->GetColorByName("white"));
};


// ----------------------------------------------------------
OXView::OXView(OXWindow *p, int w, int h, long id, unsigned int xMargin, 
                unsigned int yMargin, unsigned int options, unsigned int sboptions) 
       : OXCompositeFrame(p, w, h, options)
{
SetLayoutManager(new OHorizontalLayout(this));
 SetBackgroundColor(_client->GetColorByName("white"));
 _xMargin = xMargin;
 _yMargin = yMargin;
 _scrollVal.x = 1;
 _scrollVal.y = 1;
 _scrollTimer = new OTimer(this, 75);
 _selProperty = XInternAtom(GetDisplay(), "clipboard", False);
 _canvas = new OXViewFrame(this, 10, 10, CHILD_FRAME);
 AddFrame(_canvas, NULL);

if (!(sboptions & NOHSB))
{  _hsb = new OXHScrollBar(this, 10, 10, CHILD_FRAME);
  AddFrame(_hsb, NULL);
  _hsb->Associate(this);
}
else
 _hsb = NULL;

if (!(sboptions &NOVSB)) 
{
  _vsb = new OXVScrollBar(this, 10, 10, CHILD_FRAME);
  AddFrame(_vsb, NULL);
  _vsb->Associate(this);
}
else 
 _vsb= NULL;

 Clear();
 Layout();
 XSelectInput(GetDisplay(), _canvas->GetId(), KeyPressMask | ExposureMask | 
              EnterWindowMask | LeaveWindowMask |
              FocusChangeMask | StructureNotifyMask);


 XGrabButton(GetDisplay(), AnyButton, AnyModifier, _canvas->GetId(), True,
             ButtonPressMask | ButtonReleaseMask | Button1MotionMask,
             GrabModeAsync, GrabModeAsync, None, None);
 _ID = id; 
}

//------------------------------------------------------------
OXView::~OXView()
{
 delete _canvas;
 delete _hsb; delete _vsb;
 delete _scrollTimer;
} 

//------------------------------------------------------------
void OXView::Clear()
{
 _isMarked = False;
 _isSaved = True;
 _scrolling = -1;
 _isMarking = False;
 _markedStart.x = _markedStart.y = 0;
 _markedEnd.x = _markedEnd.y = 0;
 _mousePos.x = _mousePos.y = -1;
 SetVsbPosition(0);
 SetHsbPosition(0);
 _visible.x = _visible.y = 0;
}

//------------------------------------------------------------
void OXView::SetVisibleStart(long newTop, bool direction)
{
  if (direction == HORIZONTAL)
  {
   if (newTop/_scrollVal.x == _visible.x/_scrollVal.x)
    return;
   ScrollCanvas(newTop, HORIZONTAL);
  }
  else
  {
   if (newTop/_scrollVal.y == _visible.y/_scrollVal.y)
    return;
   ScrollCanvas(newTop, VERTICAL);
  } 
}

//------------------------------------------------------------
int OXView::HandleExpose(XExposeEvent *event)
{
  if (event->window == _canvas->GetId())
  {
   DrawRegion(event->x, event->y, event->width, event->height);
  }
  else   
   OXCompositeFrame::HandleExpose(event);
}

//------------------------------------------------------------
int OXView::HandleGraphicsExpose(XGraphicsExposeEvent *event)
{

  if (event->drawable == _canvas->GetId())
  {
   DrawRegion(event->x, event->y, event->width, event->height);
  }
  else
   OXCompositeFrame::HandleGraphicsExpose(event);
}

//------------------------------------------------------------
int OXView::HandleCrossing (XCrossingEvent *event)
{
 if (event->window != _canvas->GetId())
  return True;
 _mousePos.y = ToObjYCoord(_visible.y+event->y);
 if (ToScrYCoord(_mousePos.y+1) >= _canvas->GetHeight())
  _mousePos.y--; 
 _mousePos.x = ToObjXCoord(_visible.x+event->x, _mousePos.y);
 if (_mousePos.x  >= ReturnLineLength(_mousePos.y))
  _mousePos.x--;
 if (((event->state & Button1Mask) != 0) && (_isMarked) &&(_isMarking))
 {
  if (event->type == LeaveNotify)
  {
   if (event->x < 0) 
   {
    _scrolling = 0;
    return False; 
   }
   if (event->x >= _canvas->GetWidth()) 
   {
    _scrolling = 1;
    return False; 
   }
   if (event->y < 0)
   {
    _scrolling = 2;
    return False; 
   } 
   if (event->y >= _canvas->GetHeight())
   {
    _scrolling = 3;
    return False; 
   }
  }
  else
  {
   _scrolling = -1;
   Mark(_mousePos.x, _mousePos.y);
  }
 }
 else
  _isMarking = False;
 return True;
}

//------------------------------------------------------------
int OXView::HandleTimer (OTimer *t)
{
 OPosition size;

 if (t != _scrollTimer)
  return True;
 if (_isMarked) 
 {
  size.y = ToObjYCoord(_visible.y+_canvas->GetHeight())-1;
  size.x = ToObjXCoord(_visible.x+_canvas->GetWidth(), _mousePos.y)-1;
  switch(_scrolling)
  {
   case -1:
    break;
   case 0:
    if (_visible.x == 0)
    {
     _scrolling = -1;
     break;
    }
    else
    {
     SetHsbPosition(_visible.x/_scrollVal.x-1);
     Mark(ToObjXCoord(_visible.x, _mousePos.y)-1,_mousePos.y);     
    }  
    break;
   case 1:
    if (_canvas->GetWidth() >= ToScrXCoord(ReturnLineLength(_mousePos.y), _mousePos.y))
    {
     _scrolling = -1;
     break;
    }
    else
    {
     SetHsbPosition(_visible.x/_scrollVal.x+1);
     Mark(size.x+1,_mousePos.y);
    }  
    break;
   case 2:
    if (_visible.y == 0)
    {
     _scrolling = -1;
     break;
    }
    else
    {
     SetVsbPosition(_visible.y/_scrollVal.y-1);
     Mark(_mousePos.x, _markedStart.y-1);
    }  
    break;
   case 3:
    if (_canvas->GetHeight() >= ToScrYCoord(ReturnLineCount()))
    {
     _scrolling = -1;
     break;
    }
    else
    { 
     SetVsbPosition(_visible.y/_scrollVal.y+1);
     Mark(_mousePos.x, size.y+1);     
    }  
    break;
   default:
    break;
  } 
 }
 delete _scrollTimer;
 _scrollTimer = new OTimer(this, 75); 
}

//------------------------------------------------------------
int OXView::HandleButton(XButtonEvent *event)
{
 if (event->window != _canvas->GetId())
  return True;
 if (event->button == Button1)
 {
  if (event->type == ButtonPress)
  {
   if (_isMarked)
   {
    _isMarked = False;
    UnMark();
   }
   _isMarked = True;
   _isMarking = True;
   _mousePos.y = ToObjYCoord(_visible.y+event->y);
   _mousePos.x = ToObjXCoord(_visible.x+event->x, _mousePos.y);   
   _markedStart.x = _markedEnd.x = _mousePos.x;
   _markedStart.y = _markedEnd.y = _mousePos.y;
  }
  else
  {
   _scrolling = -1;
   if ((_markedStart.x == _markedEnd.x) && (_markedStart.y == _markedEnd.y))
   {
    _isMarked = False;
    SendMessage(_parent, MK_MSG(XC_VIEW,VIEW_ISMARKED), _id, False);
   }
   else
     SendMessage(_parent, MK_MSG(XC_VIEW,VIEW_ISMARKED), _id, True);
   _isMarking = False;
  }
 }
 else
  if (event->type == ButtonPress)
   switch(event->button)
   {
    case Button2:      SendMessage(_parent, MK_MSG(XC_VIEW,VIEW_CLICK2), 
                         _id,(event->y_root << 16) | event->x_root);
     break;
    case Button3:      SendMessage(_parent, MK_MSG(XC_VIEW,VIEW_CLICK3), 
                         _id,(event->y_root << 16) | event->x_root);
   }
return True;
}

//--------------------------------------------------------------
int OXView::HandleMotion(XMotionEvent *event)
{
 if ((ToObjYCoord(_visible.y+event->y) == _mousePos.y) && (ToObjXCoord(_visible.x+event->x, ToObjYCoord(_visible.y+event->y)) == _mousePos.x))
  return True;
 if (_scrolling != -1)
  return True;
 _mousePos.y = ToObjYCoord(_visible.y+event->y);
 if (_mousePos.y >= ReturnLineCount())
  _mousePos.y = ReturnLineCount()-1;
 _mousePos.x = ToObjXCoord(_visible.x+event->x, _mousePos.y);
 if (_mousePos.x > ReturnLineLength(_mousePos.y))
  _mousePos.x = ReturnLineLength(_mousePos.y);
 if (event->window != _canvas->GetId())
  return True;
 if ((_isMarked == False) || (_isMarking == False))
  return True;
 if (event->x < 0) 
  return True; 
 if (event->x >= _canvas->GetWidth()) 
  return True; 
 if (event->y < 0)
  return True; 
 if (event->y >= _canvas->GetHeight())
  return True; 
 Mark(_mousePos.x, _mousePos.y);
 return True;
}


//------------------------------------------------------------
int OXView::ProcessMessage(long msg, long parm1, long parm2) {

  switch(GET_MSG(msg)) {
    case XC_HSCROLL:
      switch(GET_SUBMSG(msg)) {
        case SB_SLIDERTRACK:
        case SB_SLIDERPOS:
          SetVisibleStart(parm1*_scrollVal.x,HORIZONTAL);
          break;
      }
      break;

    case XC_VSCROLL:
      switch(GET_SUBMSG(msg)) {
        case SB_SLIDERTRACK:
        case SB_SLIDERPOS:
          SetVisibleStart(parm1*_scrollVal.y, VERTICAL);
          break;
      }
      break;

    default:
      break;
   
  }

  return True;
}

//------------------------------------------------------------
void OXView::Layout() 
{
   HLayout();
   VLayout(); 
}

//------------------------------------------------------------
void OXView::HLayout() {
  int tcw, tch;
  long cols;
if (_hsb == NULL)
 return;
  tch = _h - (_bw << 1) - _yMargin-1;
  tcw = _w - (_bw << 1) - _xMargin-1;
  if ((_vsb != NULL) &&(_vsb->IsMapped() == True))
  {
   tcw -= _vsb->GetDefaultWidth(); 
  }
  _canvas->SetHeight(tch);
  _canvas->SetWidth(tcw);
  cols = ReturnLongestLineWidth();
  if (cols <= tcw)
   {
     if ((_hsb != NULL) && (_hsb->IsMapped() == True))
     {
      SetVisibleStart(0, HORIZONTAL);
      _hsb->UnmapWindow();
      VLayout();
     }
     _canvas->MoveResize(_bw+_xMargin, _bw+_yMargin, tcw, tch); 
   } 
   else
   {
     if (_hsb != NULL)
     {
      tch -= _hsb->GetDefaultHeight();    
      _hsb->MoveResize(_bw, _h - _hsb->GetDefaultHeight()-_bw, 
                        tcw+1+_bw,_hsb->GetDefaultHeight());
      _hsb->MapWindow();
      _hsb->SetRange(cols/_scrollVal.x, tcw/_scrollVal.x);
     }
    _canvas->MoveResize(_bw+_xMargin, _bw+_yMargin, tcw, tch);
   }
}

//------------------------------------------------------------



//------------------------------------------------------------

void OXView::VLayout() {

int height;
  long lines;
  int tcw, tch;
  tch = _h - (_bw << 1) - _yMargin-1;
  tcw = _w - (_bw << 1) - _xMargin-1;
  if ((_hsb != NULL) && (_hsb->IsMapped() == True))
  {
   tch -= _hsb->GetDefaultHeight();    
  }
  _canvas->SetHeight(tch);
  _canvas->SetWidth(tcw);
  lines = ReturnHeighestColHeight();
   if (lines <= tch) 
   {
    if ((_vsb != NULL) && (_vsb->IsMapped() == True))
    {   
      SetVisibleStart(0, VERTICAL);
     _vsb->UnmapWindow();
      HLayout();
    }
     _canvas->MoveResize(_bw+_xMargin, _bw+_yMargin, tcw, tch); 

   } 
   else 
   {
     if (_vsb != NULL)
     {
      tcw -= _vsb->GetDefaultWidth();
      _vsb->MoveResize(_w - _vsb->GetDefaultWidth() -_bw, _bw,
                         _vsb->GetDefaultWidth(), tch+1+_bw);
      _vsb->MapWindow();
      _vsb->SetRange(lines/_scrollVal.y, tch/_scrollVal.y);
     }
     _canvas->MoveResize(_bw+_xMargin, _bw+_yMargin, tcw, tch);      

   }
}

//------------------------------------------------------------

void OXView::SetSBRange(bool direction)
{

 if (direction == VERTICAL)
 {
  if (_vsb == NULL)
   return;
  if (ReturnHeighestColHeight() <= _canvas->GetHeight()) 
  {
   if (_vsb->IsMapped() == True)
    VLayout();
   else
    return;
  } 
  if (_vsb->IsMapped() == False)
   VLayout();
  _vsb->SetRange(ReturnHeighestColHeight()/_scrollVal.y, 
  _canvas->GetHeight()/_scrollVal.y);
 }
 else
 {  
  if (_hsb == NULL)
   return;
  if (ReturnLongestLineWidth() <= _canvas->GetWidth())
  {
   if (_hsb->IsMapped() == True)
    HLayout();
   else
    return;
  }
  if (_hsb->IsMapped() == False)
   HLayout();
  _hsb->SetRange(ReturnLongestLineWidth()/_scrollVal.x, _canvas->GetWidth()/_scrollVal.x);
 }   
}   

void OXView::SetHsbPosition(long newPos)
{
 if (_hsb != NULL)
  _hsb->SetPosition(newPos);
 else
  SetVisibleStart(newPos * _scrollVal.x, HORIZONTAL);
}
void OXView::SetVsbPosition(long newPos)
{
 if (_vsb != NULL)
  _vsb->SetPosition(newPos);
 else
  SetVisibleStart(newPos * _scrollVal.y, VERTICAL);
}

//------------------------------------------------------------

void OXView::DrawBorder() {

  switch (_options & (SUNKEN_FRAME | RAISED_FRAME | DOUBLE_BORDER)) {
  case SUNKEN_FRAME | DOUBLE_BORDER:
    XDrawLine (GetDisplay(), _id, _shadowGC, 0, 0, _w-2, 0);
    XDrawLine (GetDisplay(), _id, _shadowGC, 0, 0, 0, _h-2);
    XDrawLine (GetDisplay(), _id, _blackGC, 1, 1, _w-3, 1);
    XDrawLine (GetDisplay(), _id, _blackGC, 1, 1, 1, _h-3);

    XDrawLine (GetDisplay(), _id, _hilightGC, 0, _h-1, _w-1, _h-1);
    XDrawLine (GetDisplay(), _id, _hilightGC, _w-1, _h-1, _w-1, 0);
    XDrawLine (GetDisplay(), _id, _bckgndGC,  1, _h-2, _w-2, _h-2);
    XDrawLine (GetDisplay(), _id, _bckgndGC,  _w-2, 1, _w-2, _h-2);
    break;

  default:
    OXFrame::DrawBorder();
    break;
  }
}

//------------------------------------------------------------
void OXView::ScrollCanvas(long new_top, bool direction) {

  XPoint points[4];
  int xsrc, ysrc, xdest, ydest;
  int cpyheight, cpywidth;

 if (direction == VERTICAL)
 {
  points[0].x = points[3].x = 0;
  points[1].x = points[2].x = _canvas->GetWidth();
  xsrc = xdest = 0;
  cpywidth = 0;
  if (new_top < _visible.y) {
    ysrc = 0;
    ydest = _visible.y - new_top;
    cpyheight = ydest;
    if (ydest > _canvas->GetHeight()) ydest = _canvas->GetHeight();
    points[1].y = points[0].y = 0;
    points[3].y = points[2].y = ydest ; // -1;
  } else {
    ydest = 0;
    ysrc = new_top - _visible.y;
    cpyheight= ysrc;
    if (ysrc > _canvas->GetHeight()) ysrc = _canvas->GetHeight();
    points[1].y = points[0].y = _canvas->GetHeight()-ysrc; // +1;
    points[3].y = points[2].y = _canvas->GetHeight();
    } 
   _visible.y = new_top;
  if (_visible.y < 0)
   _visible.y = 0;
 }
 else
 {
  points[0].y = points[1].y = 0;
  points[2].y = points[3].y = _canvas->GetHeight();
  ysrc = ydest = 0;
  cpyheight = 0;  
  if (new_top < _visible.x) {   
    xsrc = 0;
    xdest = _visible.x - new_top;
    cpywidth = xdest;  
    if (xdest < 0) xdest = _canvas->GetWidth();
    points[0].x = points[3].x = 0;
    points[1].x = points[2].x = xdest ; // -1;
  } else {
    xdest = 0;
    xsrc =  new_top - _visible.x; 
    cpywidth = xsrc;
    if (xsrc > _canvas->GetWidth()) xsrc = _canvas->GetWidth();
    points[0].x = points[3].x = _canvas->GetWidth() -xsrc; // +1;
    points[1].x = points[2].x = _canvas->GetWidth();  
  }
   _visible.x = new_top;
   if(_visible.x < 0)
    _visible.x = 0;
 }   
  XCopyArea(GetDisplay(), _canvas->GetId(), _canvas->GetId(), _whiteGC, 
          xsrc, ysrc,_canvas->GetWidth()-cpywidth,
            _canvas->GetHeight()-cpyheight, xdest, ydest);
 XClearArea(GetDisplay(), _canvas->GetId(), points[0].x, points[0].y,
            points[2].x - points[0].x, points[2].y - points[0].y,False/*True*/);
  DrawRegion(points[0].x, points[0].y,
            points[2].x - points[0].x, points[2].y - points[0].y);

}

