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

    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 <limits.h>
#include <stdio.h>
#include <X11/cursorfont.h>

#include "OXTextView.h"

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

OXTextView::_OXTextView()
{
    XGCValues gcv;
    int mask = GCFont | GCForeground | GCBackground;
    _font = XLoadQueryFont(GetDisplay(),"-adobe-courier-medium-r-*-*-12-*-*-*-*-*-iso8859-1");   
    unsigned long back = _client->GetColorByName("white");
    SetBackgroundColor(back);
    gcv.foreground = _client->GetColorByName("black");
    gcv.background = back;
    gcv.font = _font->fid;

    _gc = XCreateGC(GetDisplay(), _canvas->GetId(), mask, &gcv);
    
    gcv.foreground = back;
    gcv.background = _client->GetColorByName("navy");

    _gc_marked = XCreateGC(GetDisplay(), _canvas->GetId(), mask, &gcv);

    _text = new OText();
    Clear();
    _clipboard = new OText();
    _scrollVal.y = _font->ascent + _font->descent;
    _scrollVal.x = XTextWidth(_font, "w",1);
}

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

OXTextView::OXTextView(OXWindow *parent, int x, int y, long id, unsigned int sboptions) :
  OXView(parent, x, y, id, 3, 3, SUNKEN_FRAME | DOUBLE_BORDER, sboptions) { 
_OXTextView();
}

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

OXTextView::OXTextView(OXWindow *parent, int x, int y, long id, OText *text,unsigned int sboptions) :
  OXView(parent, x, y, id, 3, 3,SUNKEN_FRAME | DOUBLE_BORDER, sboptions) {
 _OXTextView();
 OLongPosition pos, srcStart, srcEnd;
 pos.x = pos.y = 0;
 srcStart.x = srcStart.y = 0;
 srcEnd.y = text->RowCount()-1;
 srcEnd.x = text->GetLineLength(srcEnd.y)-1;
 _text->InsText(pos, text, srcStart, srcEnd);
}

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

OXTextView::OXTextView(OXWindow *parent, int x, int y, long id, char *string, unsigned int sboptions) :
  OXView(parent, x, y, id, 3, 3,SUNKEN_FRAME | DOUBLE_BORDER, sboptions) {
 OLongPosition pos;
 _OXTextView();
 pos.x = pos.y = 0;
 _text->InsText(pos, string);
}

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

OXTextView::~OXTextView() 
{
  delete _text;
  delete _clipboard;
}

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

void OXTextView::SetText(OText *text)
{
 Clear();
 delete _text;
 _text = text;
  Layout();
  DrawRegion(0,0, _canvas->GetWidth(), _canvas->GetHeight());
}

void OXTextView::AddText(OText *text)
{
 _text->AddText(text);
  Layout();
  DrawRegion(0,0, _canvas->GetWidth(), _canvas->GetHeight());
}
void OXTextView::AddLine(char *string)
{
 _text->InsLine(_text->RowCount(),string);
  Layout();
  DrawRegion(0,0, _canvas->GetWidth(), _canvas->GetHeight());
}

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

long OXTextView::ReturnLongestLineWidth()
{long count = 0;
 long  rows = _text->RowCount();
   long longest=0;
   while (count < rows)
   {
    if (ToScrXCoord(_text->GetLineLength(count), count)+ _visible.x > longest)
     longest =ToScrXCoord(_text->GetLineLength(count), count)+_visible.x;
    count++; 
   }
  return longest;
}

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

bool OXTextView::Search(char *string, bool direction, bool caseSensitive)
{
 OLongPosition pos, pos2;
 pos2.x = pos2.y = 0;
 if (_isMarked)
 {
  pos2.x = _markedEnd.x+1;
  pos2.y = _markedEnd.y;
 }
 if (_text->Search(&pos, pos2, string, direction, caseSensitive) == False)
  return False;
 UnMark();
 _isMarked = True;
 _markedStart.y = _markedEnd.y = pos.y;
 _markedStart.x = pos.x;
 _markedEnd.x = _markedStart.x + strlen(string)-1;
 _markedEnd.x++;
 _markedEnd.x--;
 pos.y = ToObjYCoord(_visible.y);
 if ((_markedStart.y < pos.y)
        || (ToScrYCoord(_markedStart.y) >= _canvas->GetHeight()))
  pos.y = _markedStart.y;
 pos.x = ToObjXCoord(_visible.x, pos.y);
 if ((_markedStart.x < pos.x)
        || (ToScrXCoord(_markedStart.x, pos.y) >= _canvas->GetWidth()))
  pos.x = _markedStart.x;

 SetVsbPosition((ToScrYCoord(pos.y)+_visible.y)/_scrollVal.y);
 SetHsbPosition((ToScrXCoord(pos.x, pos.y)+_visible.x)/_scrollVal.x);
 DrawRegion(0,ToScrYCoord(_markedStart.y),
       
_canvas->GetWidth(),ToScrYCoord(_markedEnd.y+1)-ToScrYCoord(_markedEnd.y));
 return True;
}         


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

long OXTextView::ToScrYCoord(long yCoord)
{ 
 if (yCoord * (_font->ascent + _font->descent) <= 0)
  return 0;
 if (yCoord > _text->RowCount())
  return _text->RowCount() * (_font->ascent + _font->descent) ;
 return yCoord * (_font->ascent + _font->descent) - _visible.y;
}

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

long OXTextView::ToScrXCoord(long xCoord, long line)
{
 OLongPosition pos;
 char *buffer;
 pos.x = 0;
 pos.y = line;
 long with = _text->GetLineLength(line);
 if ((xCoord <= 0) || (pos.y < 0) || (with <= 0))
  return 0;
 if (with+1 < xCoord)
  return with;
 buffer =_text->GetLine(pos, xCoord);
 with = XTextWidth(_font, buffer, xCoord) - _visible.x;
 delete buffer;
 return with; 
}

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

long OXTextView::ToObjYCoord(long yCoord)
{ 
 return  yCoord / (_font->ascent + _font->descent);
}

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

long OXTextView::ToObjXCoord(long xCoord, long line)
{
 OLongPosition pos;
 char *buffer, *travelBuffer;
 char charBuffer;
 long viscoord;
 if ((line < 0) || (line >= _text->RowCount()))
  return 0;
 long len = _text->GetLineLength(line);
 pos.x = 0;
 pos.y = line;
 if ((len <= 0) || (xCoord < 0))
  return 0;
 viscoord =  xCoord; 
 buffer = _text->GetLine(pos, len);
 travelBuffer = buffer;
 charBuffer = *travelBuffer++;
 while ((viscoord - XTextWidth(_font, &charBuffer,1) >= 0) && (pos.x < len))
 {
  viscoord -= XTextWidth(_font, &charBuffer,1);
  pos.x++;
  charBuffer = *travelBuffer++;
 }

 delete buffer; 
 return pos.x;
} 

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

void OXTextView::Clear() 
{
    OXView::Clear();
    delete _text;
    _text = new OText();
//    _text->Clear();
    SendMessage(_parent, MK_MSG(XC_VIEW,VIEW_ISMARKED), _ID, False);
    XClearWindow(GetDisplay(), _canvas->GetId());
    SendMessage(_parent, MK_MSG(XC_VIEW,VIEW_DATACHANGE), _ID,0); 
    Layout();
}

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

bool OXTextView::LoadFile(char *filename, long startpos, long length) 
{
 FILE *fp;
  if ((fp = fopen(filename, "r")) == NULL) return False;
  fclose(fp);
  Clear();
  _text->Load(filename, startpos, length);
  Layout();
  DrawRegion(0,0, _canvas->GetWidth(), _canvas->GetHeight());
  return True;
}

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

bool OXTextView::Copy()
{
 OLongPosition insPos;  

 if (_isMarked == False)
  return False;  
 delete _clipboard;
 _clipboard = new OText();
 insPos.y = insPos.x = 0;
 _clipboard->InsText(insPos, _text, _markedStart, _markedEnd);
 XSetSelectionOwner(GetDisplay(), XA_PRIMARY, _id, CurrentTime);
 return True; 
}

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

bool OXTextView::SelectAll()
{
 if ((_text->RowCount() == 1) && (_text->GetLineLength(0) == 0))
  return False;
 _isMarked = True;
 _markedStart.y = 0;
 _markedStart.x = 0;
 _markedEnd.y = _text->RowCount()-1;
 _markedEnd.x = _text->GetLineLength(_markedEnd.y)-1;
 if (_markedEnd.x < 0)
  _markedEnd.x = 0;
 DrawRegion(0, 0, _canvas->GetWidth(), _canvas->GetHeight());
 return True;
} 

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

void OXTextView::DrawRegion(int x, int y, int w, int h) 
{
 long xoffset;
 OLongPosition pos;
 long len, len1, len2;
 int line_count = _text->RowCount();
 XRectangle rect;
 char *buffer;
 rect.x = x;
 rect.y = y;
 pos.y = ToObjYCoord(_visible.y+h);
 rect.height = h +ToScrYCoord(pos.y+1) - ToScrYCoord(pos.y);
 pos.x = ToObjXCoord(_visible.x+w, pos.y);
 rect.width = w + ToScrXCoord(pos.x+1,pos.y) - ToScrXCoord(pos.x, pos.y); 
 int yloc = rect.y + _scrollVal.y; 
 pos.y =  ToObjYCoord(_visible.y+rect.y);       
 while ((pos.y < line_count) 
        && (yloc - _scrollVal.y<= _canvas->GetHeight())
        && (yloc - _scrollVal.y <= rect.y+rect.height))
 {
  pos.x = ToObjXCoord(_visible.x+rect.x, pos.y);
  xoffset = ToScrXCoord(pos.x, pos.y);
  len = _text->GetLineLength(pos.y)-pos.x;
    XClearArea(GetDisplay(), _canvas->GetId(), 
                x, ToScrYCoord(pos.y), 
                rect.width, ToScrYCoord(pos.y+1)-ToScrYCoord(pos.y),False);
  if (len > 0)
  {
   if (len > ToObjXCoord(_visible.x+rect.x + rect.width, pos.y)-pos.x)
    len = ToObjXCoord(_visible.x+xoffset + (rect.x-xoffset) +rect.width, pos.y) - pos.x+1;     
   if (pos.x == 0)
    xoffset = -_visible.x;
   if (pos.y >= ToObjYCoord(_visible.y))
   {     
    buffer = _text->GetLine(pos, len);
    if ((_isMarked == False) 
          || (pos.y < _markedStart.y) || (pos.y > _markedEnd.y) 
          || ((pos.y == _markedStart.y) 
                && (_markedStart.x >= pos.x+len)
                && (_markedStart.y != _markedEnd.y))
          || ((pos.y == _markedEnd.y)
                && (_markedEnd.x < pos.x)
                && (_markedStart.y != _markedEnd.y))
          || ((_markedStart.y == _markedEnd.y)
                && ((_markedEnd.x < pos.x) || 
                    (_markedStart.x > pos.x+len))))
{
     XDrawImageString(GetDisplay(), _canvas->GetId(), _gc, 
                         xoffset, ToScrYCoord(pos.y+1)  - _font->descent, buffer, len);
}
    else
    { 
     if ((pos.y > _markedStart.y) && (pos.y < _markedEnd.y))
     {
      len1= 0;
      len2= len;
     }
     else
     {
      if (_markedStart.y == _markedEnd.y)
      {
       if ((_markedStart.x >= pos.x) 
                && (_markedStart.x <= pos.x+len))
        len1 = _markedStart.x - pos.x;
       else
        len1 = 0;
       if ((_markedEnd.x >= pos.x) 
                && (_markedEnd.x <= pos.x+len))
        len2 = _markedEnd.x+1 - pos.x - len1;
       else
        len2 = len - len1;
      }
      else
      {
       if (pos.y == _markedStart.y)
       {
        if (_markedStart.x < pos.x)
        {
         len1 = 0;
         len2 = len;
        }
        else
        {
         len1 = _markedStart.x - pos.x;
         len2 = len - len1;
        }
       }
       else
       {
        if (_markedEnd.x > pos.x+len)
        {
         len1 = 0;
         len2 = len;
        }
        else
        {
         len1 = 0 ;
         len2 = _markedEnd.x+1 - pos.x;
        }  
       }
      }
     }
     XDrawImageString(GetDisplay(), _canvas->GetId(), _gc,
                        ToScrXCoord(pos.x, pos.y),
                        ToScrYCoord(pos.y+1) - _font->descent,
                       buffer, len1);
     XDrawImageString(GetDisplay(), _canvas->GetId(), _gc_marked,
                        ToScrXCoord(pos.x+len1, pos.y),
                        ToScrYCoord(pos.y+1) - _font->descent,
                       buffer+len1, len2);
     XDrawImageString(GetDisplay(), _canvas->GetId(), _gc,
                        ToScrXCoord(pos.x+len1+len2, pos.y),
                       ToScrYCoord(pos.y+1) - _font->descent,
                       buffer+len1+len2, len-(len1+len2));
    }
    delete buffer;
   }
  }
  pos.y++;
  yloc+=ToScrYCoord(pos.y) -ToScrYCoord(pos.y-1);
 }
}

//------------------------------------------
  
int OXTextView::HandleSelectionRequest (XSelectionRequestEvent *event)
{
 XSelectionEvent reply;
 char *buffer, *temp_buffer;
 long len, prev_len, temp_len;
 long count;
 OLongPosition pos;
 reply.type = SelectionNotify;
 reply.selection = event->selection;
 reply.target = event->target;
 reply.property = event->property;
 reply.time = event->time;
 reply.requestor = event->requestor;
 len = 0;
 for (count = 0; count < _clipboard->RowCount();count++)
  len+= _clipboard->GetLineLength(count)+1;
 
 pos.y = pos.x = 0;
 buffer = new char[len];
 prev_len = temp_len = 0;
 for (pos.y = 0; pos.y < _clipboard->RowCount();pos.y++)
 {
  temp_len = _clipboard->GetLineLength(pos.y);
  temp_buffer = _clipboard->GetLine(pos, temp_len);
  strncpy(buffer+prev_len, temp_buffer, temp_len);
  buffer[prev_len+temp_len] = 10;
  delete temp_buffer;
  prev_len += temp_len+1;
 }
 buffer[len] = '\0';
 XChangeProperty(GetDisplay(), event->requestor, event->property,
  event->target, 8, PropModeReplace, (unsigned char*) buffer, len-1);

 delete buffer;
  
 XSendEvent(GetDisplay(), event->requestor, True,0, (XEvent*) &reply);
 return True;
}

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

void OXTextView::Mark(long xPos, long yPos)
{
 OLongPosition posStart, posEnd, pos;
 pos.y = yPos;
 pos.x = xPos;
 long yWidth;
 if (pos.y > _text->RowCount()-1)
  pos.y = _text->RowCount()-1;
 if (pos.x > _text->GetLineLength(pos.y))
  pos.x = _text->GetLineLength(pos.y);
 if (pos.y < _markedStart.y)
 {
  posEnd.y = _markedStart.y;
  if ((marked_from_y == 1) || (marked_from_x == 1))
  {
   posEnd.y = _markedEnd.y;
   _markedEnd.x = _markedStart.x;
   _markedEnd.y = _markedStart.y;
  }
  posStart.y = pos.y;
  _markedStart.y = pos.y;
  _markedStart.x = pos.x;
  marked_from_y = 0;
  marked_from_x = 0;
 }
 else
  if (pos.y > _markedEnd.y)
  {
   posStart.y = _markedEnd.y;
   if ((marked_from_y == 0) || (marked_from_x == 0))
     {
      if (_markedStart.y != _markedEnd.y)
      {
       posStart.y = _markedStart.y;
       _markedStart.x = _markedEnd.x;
       _markedStart.y = _markedEnd.y;
     }
    }
   _markedEnd.y = pos.y;
   _markedEnd.x = pos.x -1;
   marked_from_y = 1;
   marked_from_x = 1;

   posEnd.y = _markedEnd.y;
  }
  else
  {
   if ((pos.x <=  _markedStart.x) 
        && (pos.y == _markedStart.y))
   {
     posEnd.y = _markedStart.y;
     if ((marked_from_y == 1) || (marked_from_x == 1))
     {
       posEnd.y = _markedEnd.y;
       _markedEnd.x = _markedStart.x;  
       _markedEnd.y = _markedStart.y;
     
     }
     _markedStart.x = pos.x;
     marked_from_y = 0;
     marked_from_x = 0;
     posStart.y = _markedStart.y;
   }
   else
   {  
    if ((pos.x > _markedEnd.x) && (pos.y == _markedEnd.y))
    {
     posStart.y = _markedEnd.y;
     if ((marked_from_y == 0) || (marked_from_x == 0))
     {
      posStart.y = _markedStart.y;
      _markedStart.x = _markedEnd.x;
      _markedStart.y = _markedEnd.y;
     }
     _markedEnd.x = pos.x-1;
     marked_from_y = 1;
     marked_from_x = 1;
     posEnd.y = _markedEnd.y;
    }
    else 
    { 
     if ((marked_from_y == 0) || (marked_from_x == 0))
     {
      posStart.y = _markedStart.y;
      _markedStart.y = pos.y;
      _markedStart.x = pos.x;
      posEnd.y = _markedStart.y;
      marked_from_x = 0;
      if ((_markedStart.y == _markedEnd.y) 
          && (_markedStart.x > _markedEnd.x))
      {
       _markedStart.x = _markedEnd.x;
       _markedEnd.x = pos.x-1;
       marked_from_x  = 1;
      }  
     } 
     else
      if ((marked_from_x == 1) || (marked_from_y == 1))
      {
       posStart.y = pos.y;
       posEnd.y = _markedEnd.y;
       _markedEnd.y = pos.y;
       _markedEnd.x = pos.x -1;
       marked_from_y = 1;
       marked_from_x = 1;

      if (_markedEnd.x == -1)
      {
       _markedEnd.y = pos.y-1;
       _markedEnd.x = _text->GetLineLength(_markedEnd.y)-1;
       if (_markedEnd.x < 0)
        _markedEnd.x = 0;
      }
      marked_from_x = 1;
      if ((_markedStart.y == _markedEnd.y)
         && (_markedStart.x > _markedEnd.x))
      {
       _markedEnd.x = _markedStart.x;
       _markedStart.x = pos.x;
       marked_from_x  = 0;
      }
     }
    }
   } 
  }
  if (_markedEnd.x == _text->GetLineLength(_markedEnd.y))
   _markedEnd.x--;

   if (_markedEnd.x == -1)
   {
    if (_markedEnd.y > 0)
     _markedEnd.y--;
    _markedEnd.x = _text->GetLineLength(_markedEnd.y)-1;
    if (_markedEnd.x < 0)
     _markedEnd.x = 0;
   }
  DrawRegion(0, ToScrYCoord(posStart.y),_canvas->GetWidth(),
             ToScrYCoord(posEnd.y+1)-ToScrYCoord(posStart.y));
  return;
}

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

void OXTextView::UnMark()
{
 _isMarked = False;
 XClearWindow(GetDisplay(), _canvas->GetId());
 DrawRegion(0,0,_canvas->GetWidth(), _canvas->GetHeight());
}

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

void OXTextView::AdjustWidth()
{
 long line;
 long size;
 line = _text->GetLongestLine();
 if (line <= 0)
  return;
 size = ToScrXCoord(_text->GetLineLength(line), line) + _visible.x;
 if (_vsb->IsMapped())
  size+= _vsb->GetDefaultWidth();
 size += (_bw << 1) + _xMargin+1;
 Resize(size, _h);
}
