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

    This file is part of notepad95, a text editor for fvwm95.
    Copyright (C) 1997, Harald Radke.                   

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

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

#include <limits.h>
#include <stdio.h>
#include <X11/keysym.h>
#include "OXTextFrame.h"
#include <X11/cursorfont.h>
#include<X11/Xatom.h>


OXTextFrame::OXTextFrame(OXWindow *parent, int x, int y,
                         unsigned int options, unsigned long back) :
  OXFrame(parent, x, y, options, back) {
    XGCValues gcv;
    int mask = GCFont | GCForeground | GCBackground;
    Cursor cursor;
    int shape = XC_xterm;       
    font = XLoadQueryFont(GetDisplay(),
//           "-adobe-courier-medium-r-*-*-12-*-*-*-*-*-iso8859-1");
           "8x13");

    _th = font->ascent + font->descent;
    _tw = 8;
    SetBackgroundColor(back);

    gcv.foreground = _client->GetColorByName("black");
    gcv.background = back;
    gcv.font = font->fid;

    _gc = XCreateGC(GetDisplay(), _id, mask, &gcv);

    gcv.function = GXand;
    _gc_crs1 = XCreateGC(GetDisplay(), _id, mask, &gcv);


    gcv.foreground = back;
    gcv.background = _client->GetColorByName("black");
    gcv.font = font->fid;

    gcv.function = GXxor;
    _gc_crs0 = XCreateGC(GetDisplay(), _id, mask, &gcv);
    
    gcv.function = GXcopy;
    gcv.foreground = back;
    gcv.background = _client->GetColorByName("navy");
    gcv.font = font->fid;

    _gc_marked = XCreateGC(GetDisplay(), _id, mask, &gcv);

    text = new OText();
    Clear();
    clipboard = new OText();

    XSelectInput(GetDisplay(), _id, KeyPressMask | ExposureMask | 
                  FocusChangeMask | StructureNotifyMask
                  /* | Button1MotionMask */);

    XGrabButton(GetDisplay(), AnyButton, AnyModifier, _id, True,
                ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
                GrabModeAsync, GrabModeAsync, None, None);

    cursor = XCreateFontCursor(GetDisplay(), shape);
    XDefineCursor(GetDisplay(), _id, cursor);

    sel_prop = XInternAtom(GetDisplay(), "SELECTION", False);
    pasteok = False; 
}

OXTextFrame::~OXTextFrame() {
  delete text;
  delete clipboard;
  delete timer;
  delete csr_timer;     
}

void OXTextFrame::Clear() {

  delete text;
  text = new OText();

  current.row = current.col = 0;
  visible.row = visible.col = 0;
  is_marked = False;
  marking = False;
  marked_start.row = marked_end.row = 0;
  marked_start.col = marked_end.col = 0;

  SendMessage(_parent, MK_MSG(XC_TEXTEDIT,TXT_ISMARKED), False,0);

  cursorstate = 1;
  scrolling = -1;

  XClearWindow(GetDisplay(), _id);

  delete timer;
  delete csr_timer;
  timer = new OTimer(this, 500);
  csr_timer = new OTimer(this, 75);

  SendMessage(_parent, MK_MSG(XC_TEXTEDIT,TXT_POSCHANGE), current.row, 
              current.col); 
}

bool OXTextFrame::LoadFile(char *filename) {
  FILE *fp;

  if ((fp = fopen(filename, "r")) == NULL) return False;
  fclose(fp);

  Clear();
  text->Load(filename);
  DrawRegion(MARGIN, MARGIN, _w-MARGIN-(_w-MARGIN)%_tw, 
                             _h-MARGIN-(_h-MARGIN)%_th);
  return True;
}

bool OXTextFrame::SaveFile(char *filename) {
  return text->Save(filename);
}

bool OXTextFrame::IsSaved() {
 return text->IsSaved();
}


bool OXTextFrame::Cut() {
  Coord pos;

  pos.row = visible.row;
  pos.col = visible.col;

  if (Copy() == False) return False;

  is_marked = False;
  text->DelText(marked_start, marked_end);
  XClearWindow(GetDisplay(), _id);
  DrawRegion(MARGIN,MARGIN, _w-MARGIN-(_w-MARGIN)%_tw,
                            _h-MARGIN-(_h-MARGIN)%_th);

  if (marked_start.row < visible.row)
    pos.row = marked_start.row;

  if (marked_start.col < visible.col)
    pos.col = marked_start.col;

  HSBChange(pos.row, pos.col);
  VSBChange(pos.row, pos.col);

  SetCurrent(marked_start);
  SendMessage(_parent, MK_MSG(XC_TEXTEDIT,TXT_ISMARKED), False,0);

  return True;
}

bool OXTextFrame::Copy() {
  Coord ins_pos;  

  if (is_marked == False) return False;  
  delete clipboard;
  clipboard = new OText();
  ins_pos.row = ins_pos.col = 0;
  clipboard->InsText(ins_pos, text, marked_start, marked_end);
  XSetSelectionOwner(GetDisplay(), XA_PRIMARY, _id, CurrentTime);

  return True; 
}

bool OXTextFrame::Paste() {
  Coord pos, start_src, end_src;

  XConvertSelection(GetDisplay(), XA_PRIMARY, XA_STRING, sel_prop,
                    _id, CurrentTime);

  return True;
}

bool OXTextFrame::Delete() {
  Coord pos;

  pos.row = visible.row;
  pos.col = visible.col;

  if (is_marked == False) return False;

  is_marked = False;
  SendMessage(_parent, MK_MSG(XC_TEXTEDIT,TXT_ISMARKED), False,0);
  text->DelText(marked_start, marked_end);     

  XClearWindow(GetDisplay(), _id);
  DrawRegion(MARGIN,MARGIN, _w-MARGIN-(_w-MARGIN)%_tw,
                            _h-MARGIN-(_h-MARGIN)%_th);

  if (marked_start.row < visible.row)
    pos.row = marked_start.row;

  if (marked_start.col < visible.col)
    pos.col = marked_start.col;

  HSBChange(pos.row, pos.col);
  VSBChange(pos.row, pos.col);

  SetCurrent(marked_start);
  return True;
}

bool OXTextFrame::SelectAll() {
  if ((text->RowCount() == 1) && (text->GetLineLength(0) == 0))
    return False;
  is_marked = True;
  marked_start.row = 0;
  marked_start.col = 0;
  marked_end.row = text->RowCount()-1;
  marked_end.col = text->GetLineLength(marked_end.row)-1;
  DrawRegion(MARGIN, MARGIN, _w-MARGIN-(_w-MARGIN)%_tw,
                             _h-MARGIN-(_h-MARGIN)%_th);
  return True;
}

bool OXTextFrame::LineBreak() {
 return True;
}

bool OXTextFrame::Search(char *string, bool direction, bool caseSensitive)
{
 Coord pos;
 if (text->Search(&pos, current, string, direction, caseSensitive) == False)
  return False;
 is_marked = False;
 DrawRegion(MARGIN,(marked_start.row-visible.row)*_th+MARGIN,
       _w,(marked_end.row-marked_start.row+1)*_th);

 is_marked = True;
 marked_start.row = marked_end.row = pos.row;
 marked_start.col = pos.col;
 marked_end.col = marked_start.col + strlen(string)-1;

 marked_end.col++;
 if (direction)
  SetCurrent(marked_end);
 else
  SetCurrent(marked_start);
 marked_end.col--;
 pos.row = visible.row;
 pos.col = visible.col;
 if ((current.row < visible.row) 
        || (current.row > visible.row+(_h-MARGIN)/_th -1))
  pos.row = marked_start.row;  
 
 if ((current.col < visible.col) 
        || (current.col > visible.col+(_w-MARGIN)/_tw -1))
  pos.col = marked_start.col;

 SBPosChange(pos.row, pos.col);  

 DrawRegion(MARGIN,(marked_start.row-visible.row)*_th+MARGIN,
       _w,(marked_end.row-marked_start.row+1)*_th);

 return True;
}
 
bool OXTextFrame::Goto(long line)
{
 if (line-1 >= text->RowCount())
  return False;
 SBPosChange(line-1,0);
 SetCurrent(visible);
 return True;
}


void OXTextFrame::DrawRegion(int x, int y, int w, int h) 
{
  Coord pos;
  long len, len1, len2;
  int line_count = text->RowCount();
  XRectangle rect;
  char *buffer;
  rect.x = x;
  rect.y = y;
  rect.width = w+_tw;
  rect.height = h+_th;
  int yloc = rect.y; 
  pos.row = visible.row + (rect.y-MARGIN)/_th;      
  pos.col = visible.col + (rect.x-MARGIN)/_tw;      

   while ((pos.row < line_count) && (yloc <= _h-_th-(_h-MARGIN)%_th) && 
(yloc <= rect.y+rect.height))
  {
    len = text->GetLineLength(pos.row)-pos.col;
     if (len*_tw > rect.width)
      len = rect.width/_tw; 

    if ((len > 0) && (pos.row >= visible.row))
    {
     buffer = new char[len];
     text->GetLine(pos, buffer, len);
     if ((is_marked == False) ||
         (pos.row < marked_start.row) || (pos.row > marked_end.row)
          || ((pos.row == marked_start.row) 
                && (marked_start.col >= pos.col+len)
                && (marked_start.row != marked_end.row))
          || ((pos.row == marked_end.row)
                && (marked_end.col < pos.col)
                && (marked_start.row != marked_end.row))
          || ((marked_start.row == marked_end.row)
                && ((marked_end.col < pos.col) || 
                    (marked_start.col > pos.col+len))))
     XDrawImageString(GetDisplay(), _id, _gc, 
                        MARGIN+(pos.col-visible.col)*_tw, 
                        MARGIN+(pos.row-visible.row+1)*_th - font->descent,
                       buffer, len);
     else
     { 
      if ((pos.row > marked_start.row) && (pos.row < marked_end.row))
      {
       len1= 0;
       len2= len;
      }
      else
      {
       if (marked_start.row == marked_end.row)
       {
        if ((marked_start.col >= pos.col) 
                && (marked_start.col <= pos.col+len))
        len1 = marked_start.col - pos.col;
       else
        len1 = 0;
       if ((marked_end.col >= pos.col) 
                && (marked_end.col <= pos.col+len))
        len2 = marked_end.col+1 - pos.col - len1;
       else
        len2 = len - len1;
       }
       else
       {
        if (pos.row == marked_start.row)
        {
         if (marked_start.col < pos.col)
         {
          len1 = len;
          len2 = 0;
         }
         else
         {
          len1 = marked_start.col - pos.col;
          len2 = len - len1;
         }
        }
        else
        {
         if (marked_end.col > pos.col+len)
         {
          len1 = 0;
          len2 = len;
         }
         else
         {
          len1 = 0 ;
          len2 = marked_end.col+1 - pos.col;
         }  
        }
       }
      }
      XDrawImageString(GetDisplay(), _id, _gc,
                        MARGIN+(pos.col-visible.col)*_tw,
                        MARGIN+(pos.row-visible.row+1)*_th - font->descent,
                       buffer, len1);
      XDrawImageString(GetDisplay(), _id, _gc_marked,
                        MARGIN+(pos.col-visible.col)*_tw+len1*_tw,
                        MARGIN+(pos.row-visible.row+1)*_th - font->descent,
                       buffer+len1, len2);
      XDrawImageString(GetDisplay(), _id, _gc,
                       MARGIN+(pos.col-visible.col)*_tw+(len1+len2)*_tw,
                        MARGIN+(pos.row-visible.row+1)*_th - font->descent,
                       buffer+len1+len2, len-(len1+len2));

     }
       delete buffer;
    }
    pos.row++;
    yloc+=_th;
  }
}

void OXTextFrame::SetCurrent(Coord new_coord)
{
  delete timer;
  DrawCursor(2);
  current.row = new_coord.row;
  current.col = new_coord.col;
  DrawCursor(1);
  timer = new OTimer(this, 500);        
  SendMessage(_parent, MK_MSG(XC_TEXTEDIT,TXT_POSCHANGE), current.row, 
        current.col); 
}

void OXTextFrame::DrawCursor(int mode)
{

 char count = -1;
 char cursor = ' ';
 if ((current.row >= visible.row) && (current.row <= 
visible.row+(_h-MARGIN)/_th-1)
    && (current.col >= visible.col) && (current.col <= visible.col+(_w-MARGIN)/_tw-1))
 {
  if (current.row < text->RowCount())   
   count = text->GetChar(current);
  if (count == -1)
   cursor =' ';
  else 
   cursor = count;
  if (mode == 2)
  {
   if ((is_marked == 1) && (count != -1))
   {
    if (((current.row > marked_start.row) && (current.row < marked_end.row))
        || ((current.row == marked_start.row) && (current.col >= marked_start.col)
             && (current.row < marked_end.row))
        || ((current.row == marked_end.row) && (current.col <= marked_end.col)
             && (current.row > marked_start.row))
        || ((current.row == marked_start.row) && (current.row == marked_end.row) &&
            (current.col >= marked_start.col) && (current.col <= marked_end.col)
            && (marked_start.col != marked_end.col)))
    { 

     XDrawImageString(GetDisplay(), _id, _gc_marked, MARGIN+(current.col-visible.col)*_tw,
                  (current.row - visible.row+1)*_th+MARGIN - font->descent,
                   &cursor, 1);
    }
    else
   {
    XDrawImageString(GetDisplay(), _id, _gc, MARGIN+(current.col-visible.col)*_tw,
                  (current.row - visible.row+1)*_th+MARGIN - font->descent,
                   &cursor, 1);
 
   }   

   }
   else
   {
    XDrawImageString(GetDisplay(), _id, _gc, MARGIN+(current.col-visible.col)*_tw,
                  (current.row - visible.row+1)*_th+MARGIN - font->descent,
                   &cursor, 1);
 
   }   
  }
  else 
   if (mode == 1)
     XDrawLine(GetDisplay(), _id, _gc_crs1,(current.col-visible.col)*_tw+MARGIN,
                                  (current.row-visible.row)*_th+MARGIN,
                                  (current.col-visible.col)*_tw+MARGIN,
                                 (current.row-visible.row+1)*_th+MARGIN-1);
     
 }
} 
 
void OXTextFrame::DrawLine(int row, int mode) {
 GC gc;
 if (mode == 0) gc = _gc;
  else gc=_gc_marked;
 Coord pos;
 long len = text->GetLineLength(row)-visible.col;
 XClearArea(GetDisplay(), _id, MARGIN, MARGIN+(row-visible.row)*_th,
            _w - MARGIN, _th, False);
 if (len < 1)
  return;
 char *buffer = new char[len];
 pos.row = row;
 pos.col = visible.col;
 text->GetLine(pos, buffer, len);
 XDrawImageString(GetDisplay(), _id, gc, MARGIN, 
                  (row-visible.row+1)*_th+MARGIN - font->descent,
                        buffer, len);

}


void OXTextFrame::SetTopLine(int new_top) {
  if (visible.row == new_top) return;
  ScrollWindow(new_top, True);
}

void OXTextFrame::SetTopCol(int new_top) {
  if (visible.col == new_top) return;
   ScrollWindow(new_top, False);

}
void OXTextFrame::AdjustPos()
{
 Coord pos;

 pos.row = current.row;
 pos.col = current.col;
 if (pos.row < visible.row)
  pos.row = visible.row;
 else
  if (pos.row > visible.row + (_h-MARGIN)/_th -1)
   pos.row = visible.row + (_h-MARGIN)/_th -1;

 if (pos.col < visible.col)
  pos.col = visible.col;
 else
  if (pos.col > visible.col + (_w-MARGIN)/_tw -1)
   pos.col = visible.col + (_w-MARGIN)/_tw -1;
 if ((pos.row != current.row) || (pos.col != current.col))
  SetCurrent(pos); 
}


void OXTextFrame::ScrollWindow(int new_top, bool direction) {
  XPoint points[4];
  int xsrc, ysrc, xdest, ydest;
  int cpyheight, cpywidth;

 if (direction == True)
 {
  points[0].x = points[3].x = MARGIN;
  points[1].x = points[2].x = _w;
  xsrc = xdest = MARGIN;
  cpywidth = MARGIN;
  if (new_top < visible.row) {
    ysrc = MARGIN;
    ydest = (visible.row - new_top) * _th+MARGIN;
    cpyheight = ydest;
    if (ydest > _h) ydest = _h;
    points[1].y = points[0].y = MARGIN;
    points[3].y = points[2].y = ydest ; // -1;
  } else {
    ydest = MARGIN;
    ysrc = (new_top - visible.row) * _th+MARGIN;
    cpyheight= ysrc;
    if (ysrc > _h-(_h-MARGIN)%_th) ysrc = _h-(_h-MARGIN)%_th;
    points[1].y = points[0].y = _h-ysrc+MARGIN - (_h-MARGIN)%_th; // +1;
    points[3].y = points[2].y = _h- (_h-MARGIN)%_th;
  }
  visible.row = new_top;
 }
 else
 {
   points[0].y = points[1].y = MARGIN;
  points[2].y = points[3].y = _h;
  ysrc = ydest = MARGIN;
  cpyheight = MARGIN;  
  if (new_top < visible.col) {   
    xsrc = MARGIN;
    xdest = (visible.col - new_top) * _tw+MARGIN; 
    cpywidth = xdest;  
    if (xdest < 0) xdest = _w;
    points[0].x = points[3].x = MARGIN;
    points[1].x = points[2].x = xdest ; // -1;
  } else {
    xdest = MARGIN;
    xsrc = (new_top - visible.col) * _tw+MARGIN;
    cpywidth = xsrc;
    if (xsrc > _w-(_w-MARGIN)%_tw) xsrc = _w-(_w-MARGIN)%_tw;
    points[0].x = points[3].x = _w -xsrc; // +1;
    points[1].x = points[2].x = _w;  
  }
  visible.col = new_top;
 } 
 XCopyArea(GetDisplay(), _id, _id, _gc, xsrc, ysrc,_w-(_w-MARGIN)%_tw-cpywidth,
            _h-(_h-MARGIN)%_th-cpyheight, xdest, ydest);
  XClearArea(GetDisplay(), _id, 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);

}




void  OXTextFrame::HandleScrollTimer ()
{
 Coord pos;
 
 if (is_marked)
  switch(scrolling)
  {
   case -1:
    break;
   case 0:
    if (visible.col == 0)
    {
     scrolling = -1;
     break;
    }
    else
    {
     pos.row = current.row; 
     pos.col = visible.col-1;
     SBPosChange(visible.row, visible.col-1);
     Mark(pos);
    }  
    break;
   case 1:
    if (visible.col >= text->GetLineLength(marked_end.row)-(_w-MARGIN)/_tw)
    {
     scrolling = -1;
     break;
    }
    else
    {
     pos.row = current.row; 
     pos.col = visible.col+(_w-MARGIN)/_tw;
     SBPosChange(visible.row, visible.col+1);
     Mark(pos);
    }  
    break;
   case 2:
    if (visible.row == 0)
    {
     scrolling = -1;
     break;
    }
    else
    {
     pos.row = visible.row-1;
     pos.col = current.col; 
     SBPosChange(visible.row-1, visible.col);
     Mark(pos);
    }  
    break;
   case 3:
    if (visible.row >= text->RowCount()-(_h-MARGIN)/_th)
    {
     scrolling = -1;
     break;
    }
    else
    {
     pos.row = visible.row+(_h-MARGIN)/_th;
     pos.col = current.col; 
     SBPosChange(visible.row+1, visible.col);
     Mark(pos);
    }  
    break;
   default:
    break;
  }
 delete csr_timer;
 csr_timer = new OTimer(this, 75);
}

int OXTextFrame::HandleTimer(OTimer *t) {
  if (t == csr_timer) {
    HandleScrollTimer();
    return False;
  }

  if (cursorstate == 1)
    cursorstate = 2;
  else
    cursorstate = 1;         
  DrawCursor(cursorstate);

  delete timer;
  timer = new OTimer(this, 500);
}

int OXTextFrame::HandleSelectionRequest(XSelectionRequestEvent *event) {
  XSelectionEvent reply;
  char *buffer, *temp_buffer;
  long len, prev_len, temp_len;
  long count;
  Coord pos;

  reply.type = SelectionNotify;
  reply.selection = event->selection;
  reply.target = event->target;
  reply.property = event->property;
  reply.time = event->time;
  reply.requestor = event->requestor;

  for (len = count = 0; count < clipboard->RowCount(); count++)
    len += clipboard->GetLineLength(count)+1;
 
  pos.row = pos.col = 0;
  buffer = new char[len];
  prev_len = temp_len = 0;

  for (pos.row = 0; pos.row < clipboard->RowCount(); pos.row++) {
    temp_len = clipboard->GetLineLength(pos.row);
    temp_buffer = new char[temp_len];
    clipboard->GetLine(pos, temp_buffer, temp_len);
    strncpy(buffer+prev_len, temp_buffer, temp_len);
    buffer[prev_len+temp_len] = '\n';
    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;
}

int OXTextFrame::HandleSelection(XSelectionEvent *event) {
  Coord start_src, end_src, pos;
  long count, old_pos;
  Atom actual_type_return;
  int actual_format_return;
  unsigned long nitems_return, bytes_after_return;
  unsigned char *prop_return;
  char *buffer, *temp_buffer;

  if (XGetSelectionOwner(GetDisplay(), XA_PRIMARY) == None)
    XGetWindowProperty(GetDisplay(), _id, XA_CUT_BUFFER0, 0, LONG_MAX,
                       False, AnyPropertyType, &actual_type_return,
                       &actual_format_return, &nitems_return,
                       &bytes_after_return, &prop_return);

  else
    XGetWindowProperty(GetDisplay(), _id, event->property, 0, LONG_MAX,
                       False, AnyPropertyType, &actual_type_return,
                       &actual_format_return, &nitems_return,
                       &bytes_after_return, &prop_return);

  if (actual_type_return != XA_STRING) return True;

  buffer = new char[nitems_return]; 
  strncpy(buffer, (char*)prop_return, nitems_return);
  delete clipboard;
  clipboard = new OText();
  pos.row = pos.col = 0;
  count = old_pos = 0;

  while (count <= nitems_return) {
    if ((buffer[count] == 10) || (count == nitems_return)) {
      temp_buffer = new char[count-old_pos];
      strncpy(temp_buffer, buffer+old_pos, count-old_pos);
      clipboard->InsLine(pos, temp_buffer, count-old_pos);
      delete temp_buffer;
      old_pos = count+1;
      pos.row++;  
    }
    count++;
  }

  start_src.row = start_src.col = 0;
  
  end_src.row = clipboard->RowCount()-1;
  end_src.col = clipboard->GetLineLength(end_src.row)-1;
  text->InsText(current, clipboard, start_src, end_src);
  XClearWindow(GetDisplay(), _id);
  DrawRegion(MARGIN,MARGIN, _w-(_w-MARGIN)%_tw, _h-(_h-MARGIN)%_th);

  pos.row = current.row+clipboard->RowCount()-1;
  pos.col = clipboard->GetLineLength(clipboard->RowCount()-1)+current.col;
  SetCurrent(pos);

  if (pos.row >= visible.row+(_h-MARGIN)/_th)
    pos.row = pos.row - (_h-MARGIN)/_th/2;
  else
    pos.row = visible.row;

  if (pos.col >= visible.col+(_w-MARGIN)/_tw)
    pos.col = pos.col - (_w-MARGIN)/_tw/2;
  else
    if (pos.col < visible.col) {
      if (visible.col - (_w-MARGIN)/_tw/2 > 0)   
        pos.col = visible.col - (_w-MARGIN)/_tw/2;
      else
        pos.col = 0;
    } else {
      pos.col = visible.col;
    }

  VSBChange(pos.row, pos.col);
  HSBChange(pos.row, pos.col);
}


int OXTextFrame::HandleButton(XButtonEvent *event) {
  Coord pos;

  if (event->type == ButtonPress) {
    if (event->button == Button1) {
      pos.row = (event->y-MARGIN)/_th; 
      if (pos.row < 0) pos.row = 0;
        pos.row += visible.row;
      if (pos.row > text->RowCount()-1)
        pos.row = text->RowCount()-1;
      pos.col = (event->x-MARGIN)/_tw;
      if (pos.col < 0) pos.col = 0;
      pos.col += visible.col;
      if (pos.col > text->GetLineLength(pos.row))
        pos.col = text->GetLineLength(pos.row);
      if (pos.row > visible.row+(_h-MARGIN)/_th-1)
        pos.row = visible.row+(_h-MARGIN)/_th-1;
      if (pos.col > visible.col+(_w-MARGIN)/_tw-1)
        pos.col = visible.col+(_w-MARGIN)/_tw-1; 

      is_marked = False;
      DrawRegion(MARGIN,MARGIN, _w-MARGIN-(_w-MARGIN)%_tw, 
                                _h-MARGIN-(_h-MARGIN)%_th); 

      marked_start.row = pos.row;                                    
      marked_start.col = pos.col;   
      marked_end.col = pos.col;
      marked_end.row = pos.row;

      if ((pos.row <= text->RowCount()) 
           && (pos.col <= text->GetLineLength(pos.row))) {
        SetCurrent(pos); 
        marking = True;
        is_marked = True;
        SendMessage(_parent, MK_MSG(XC_TEXTEDIT, TXT_POSCHANGE),
                    current.row, current.col);
      }
    } else if (event->button == Button3) {
      SendMessage(_parent, MK_MSG(XC_TEXTEDIT,TXT_CLICK), 
                           Button3,(event->y_root << 16) | event->x_root);
    }
  } else if (event->type == ButtonRelease) {
    if (event->button == Button1) {
      if (is_marked == True) {
        scrolling = -1;
        if ((marked_start.row == marked_end.row) 
         && (marked_start.col == marked_end.col)) {
          SendMessage(_parent, MK_MSG(XC_TEXTEDIT,TXT_ISMARKED), False,0);
          is_marked = False;
        } else {
          // Markierter Text fuer alle anbieten
          SendMessage(_parent, MK_MSG(XC_TEXTEDIT,TXT_ISMARKED), True,0);
        }
      }
    }
    marking = False;
  }

  return True;
}

int OXTextFrame::HandleMotion(XMotionEvent *event)
{
 Coord pos, pos2;
 int temp;

 if ((is_marked == False) || (marking == False))
  return False;
 pos2.row = visible.row;
 pos2.col = visible.col;

 if (event->x < 0) 
 {
  scrolling = 0;
  return False; 
 }
 if (event->x >= _w-(_w-MARGIN)%_tw) 
 {
  scrolling = 1;
  return False; 
 }
 if (event->y < 0)
 {
  scrolling = 2;
  return False; 
 } 
 if (event->y >= _h-(_h-MARGIN)%_th)
 {
  scrolling = 3;
  return False; 
 }
  scrolling = -1;
  pos.row = (event->y-MARGIN)/_th +visible.row;
  pos.col = (event->x-MARGIN)/_tw +visible.col;

 if ((pos.row == current.row) && (pos.col == current.col))
  return 0;

 Mark(pos);
}

void OXTextFrame::Mark(Coord pos)
{
 Coord pos_start, pos_end;

 if (pos.row > text->RowCount()-1)
  pos.row = text->RowCount()-1;
 if (pos.col > text->GetLineLength(pos.row))
  pos.col = text->GetLineLength(pos.row);

 if (pos.row < marked_start.row)
 {
  pos_end.row = marked_start.row;
  if ((marked_from_y == 1) || (marked_from_x == 1))
  {
   pos_end.row = marked_end.row;
   marked_end.col = marked_start.col;
   marked_end.row = marked_start.row;
  }
  pos_start.row = pos.row;
  marked_start.row = pos.row;
  marked_start.col = pos.col;
  marked_from_y = 0;
  marked_from_x = 0;
 }
 else
  if (pos.row > marked_end.row)
  {
   pos_start.row = marked_end.row;
   if ((marked_from_y == 0) || (marked_from_x == 0))
     {
      if (marked_start.row != marked_end.row)
      {
       pos_start.row = marked_start.row;
       marked_start.col = marked_end.col;
       marked_start.row = marked_end.row;
     }
    }
   marked_end.row = pos.row;
   marked_end.col = pos.col -1;
   marked_from_y = 1;
   marked_from_x = 1;

   if (marked_end.col == -1)
   {
    marked_end.row = pos.row-1;
    marked_end.col = text->GetLineLength(marked_end.row)-1;
    if (marked_end.col < 0)
     marked_end.col = 0;
   }
   pos_end.row = marked_end.row;
  }
  else
  {
   if ((pos.col <=  marked_start.col) 
        && (pos.row == marked_start.row))
   {
     pos_end.row = marked_start.row;
     if ((marked_from_y == 1) || (marked_from_x == 1))
     {
       pos_end.row = marked_end.row;
       marked_end.col = marked_start.col;  
       marked_end.row = marked_start.row;
     
     }
     marked_start.col = pos.col;
     marked_from_y = 0;
     marked_from_x = 0;
     pos_start.row = marked_start.row;
   }
   else
   {  
    if ((pos.col > marked_end.col) && (pos.row == marked_end.row))
    {
     pos_start.row = marked_end.row;
     if ((marked_from_y == 0) || (marked_from_x == 0))
     {
      pos_start.row = marked_start.row;
      marked_start.col = marked_end.col;
      marked_start.row = marked_end.row;
     }
     marked_end.col = pos.col-1;
     marked_from_y = 1;
     marked_from_x = 1;
     pos_end.row = marked_end.row;
    }
    else 
    { 
     if ((marked_from_y == 0) || (marked_from_x == 0))
     {
      pos_start.row = marked_start.row;
      marked_start.row = pos.row;
      marked_start.col = pos.col;
      pos_end.row = marked_start.row;
      marked_from_x = 0;
      if ((marked_start.row == marked_end.row) 
          && (marked_start.col > marked_end.col))
      {
       marked_start.col = marked_end.col;
       marked_end.col = pos.col-1;
       marked_from_x  = 1;
      }  
     } 
     else
      if ((marked_from_x == 1) || (marked_from_y == 1))
      {
       pos_start.row = pos.row;
       pos_end.row = marked_end.row;
       marked_end.row = pos.row;
       marked_end.col = pos.col -1;
       marked_from_y = 1;
       marked_from_x = 1;

      if (marked_end.col == -1)
      {
       marked_end.row = pos.row-1;
       marked_end.col = text->GetLineLength(marked_end.row)-1;
       if (marked_end.col < 0)
        marked_end.col = 0;
      }
      marked_from_x = 1;
      if ((marked_start.row == marked_end.row)
         && (marked_start.col > marked_end.col))
      {
       marked_end.col = marked_start.col;
       marked_start.col = pos.col;
       marked_from_x  = 0;
      }
     }
    }
   } 
  }
  pos_start.row -= visible.row;
  pos_end.row -= visible.row;

  DrawRegion(MARGIN, pos_start.row*_th+MARGIN,
             _w-MARGIN-(_w-MARGIN)%_tw,
             (pos_end.row-pos_start.row+1)*_th+MARGIN);
 SetCurrent(pos);

 return;
}



int OXTextFrame::HandleKey(XKeyEvent *event)
{
 bool mark_ok=False;
 char input[2];
 int input_size = 2;
 int  charcount;
 input[0] = 0;
 KeySym keysym;
 XComposeStatus compose;
 if (event->type == KeyPress)
 {
  charcount = XLookupString(event, input, input_size, &keysym,
                           &compose); 
 AdjustPos();

   if ((keysym > 0x1F) && (keysym < 0x7f))
    InsChar(input[0]);
   else 
    switch (keysym) {
    case XK_Delete :
      if (is_marked)
       Cut();
      break;
    case XK_Return :
      BreakLine();
      break;
     case XK_Tab :
      TabKey();
      break;
     case XK_BackSpace :
      DelChar();
      break;
     case XK_Left :
      mark_ok=True;
      PrevChar();
      break;
     case XK_Right :
      mark_ok=True;
      NextChar();
      break;
     case XK_Up :
      mark_ok=True;
      LineUp();
      break;
     case XK_Down :
      mark_ok=True;
      LineDown();
      break;
     case XK_Page_Up :
      mark_ok=True;
      ScreenUp();
      break;
     case XK_Page_Down :
      mark_ok=True;
      ScreenDown();
      break;
     case XK_Home :
      mark_ok=True;
      Home();
      break;
     case XK_End :
      mark_ok=True;
      End();
      break;
     default:
      break;
   }
   if ((event->state & ShiftMask == True) && (mark_ok))
   {    
    AdjustPos();
    is_marked = True;
    SendMessage(_parent, MK_MSG(XC_TEXTEDIT,TXT_ISMARKED), True,0);
    Mark(current);
   }
   else
   {
    if (is_marked == True)
    {
     is_marked = False;     
     DrawRegion(MARGIN,MARGIN,_w,_h);
     SendMessage(_parent, MK_MSG(XC_TEXTEDIT,TXT_ISMARKED), False,0);
    }
     marked_start.row = marked_end.row = current.row;
     marked_start.col = marked_end.col = current.col;
   }
 }
 return True;
}

void OXTextFrame::InsChar(char character)
{
 Coord pos;
 text->InsChar(current, character);
 pos.col = current.col + 1;
 pos.row = current.row;
 if (current.col >= (visible.col+(_w-MARGIN)/_tw)-1)
 {
   HSBChange(visible.row, visible.col + (_w-MARGIN)/_tw/2);
 }
 else
  {
   HSBChange(visible.row, visible.col);
     XCopyArea(GetDisplay(), _id, _id, _gc, (current.col-visible.col)*_tw+MARGIN, 
               (current.row-visible.row)*_th+MARGIN,
                _w, _th, 
                (current.col-visible.col+1)*_tw+MARGIN,
                (current.row-visible.row)*_th+MARGIN); 
 XDrawImageString(GetDisplay(), _id, _gc, 
(current.col-visible.col)*_tw+MARGIN,
                  (current.row-visible.row+1)*_th+MARGIN - font->descent,
                        &character, 1);     
  }
 SetCurrent(pos); 
}

void OXTextFrame::DelChar()
{
 char *buffer;
 Coord pos;
 long len;
 pos.row = current.row;
 pos.col = current.col;

 if (current.col > 0)
 {
  text->DelChar(current);
  pos.col = current.col - 1;
  if ((current.col == visible.col))
  {
   if (visible.col - (_w-MARGIN)/_tw/2 > 0)
    HSBChange(visible.row, visible.col - (_w-MARGIN)/_tw/2);
   else
    HSBChange(visible.row, 0);
  }
  else
   HSBChange(visible.row, visible.col);
 }
 else
 {
  if (current.row > 0)
  {
   len = text->GetLineLength(current.row);
   if (len > 0)
   {
    buffer = new char[len];
    text->GetLine(current, buffer, len);
    pos.row--;
    pos.col = text->GetLineLength(current.row-1);
    text->InsLine(pos, buffer, len);
    pos.row++;
   }
   else
    pos.col = text->GetLineLength(current.row -1);
   text->DelLine(current.row);
   len = text->GetLineLength(current.row -1);
   if (len > visible.col + (_w-MARGIN)/_tw-1)
    HSBChange(visible.row, visible.col);
   XCopyArea(GetDisplay(), _id, _id, _gc, 0, (current.row-visible.row+1)*_th+MARGIN,
                _w, ((_h-MARGIN)/_th-1-(current.row-visible.row))*_th, 0,
                (current.row-visible.row)*_th+MARGIN);
   pos.row = (_h-MARGIN)/_th-1 + visible.row;
   DrawLine(pos.row,0); 
   if (pos.col > visible.col + (_w-MARGIN)/_tw-1)
    HSBChange(visible.row, pos.col-((_w-MARGIN)/_tw)/2);

   if (current.row <= visible.row)
    VSBChange(visible.row-1, visible.col);
   else 
    VSBChange(visible.row, visible.col);
   pos.row = current.row - 1;
  }
 }
  DrawLine(pos.row,0);
 SetCurrent(pos);
}

void OXTextFrame::BreakLine()
{
 Coord pos;
 pos.col = visible.col;
 text->BreakLine(current);
 HSBChange(visible.row, 0);
 if (current.row < visible.row + (_h-MARGIN)/_th -1)
 {
  if (pos.col == visible.col)
  {
   XCopyArea(GetDisplay(), _id, _id, _gc, 0, 
            (current.row-visible.row+1)*_th+MARGIN,_w, 
            _h-(_h-MARGIN)%_th-((current.row-visible.row+2)*_th+MARGIN), 0,
                (current.row-visible.row+2)*_th+MARGIN);
  }
  pos.row = current.row;
  pos.col = 0;
  DrawLine(current.row,0);
  DrawLine(current.row+1,0);
 }
 else
 {
  VSBChange(visible.row +1, visible.col);
  DrawLine(current.row,0);
 }

 pos.row = current.row+1;
 pos.col = 0;
 SetCurrent(pos); 
}

void OXTextFrame::TabKey()
{
 int i;
 for (i = 0; i < 8; i++)
  InsChar(' ');
}

void OXTextFrame::PrevChar()
{
 Coord pos;
 long len;
 pos.row = current.row;
 pos.col = current.col;
 if (current.col > 0)
 {
  if (current.col == visible.col)
  {
   if (visible.col - (_w-MARGIN)/_tw/2 > 0)
    SBPosChange(visible.row, visible.col - (_w-MARGIN)/_tw/2);
   else
    SBPosChange(visible.row, 0);
  }
  pos.row = current.row;
  pos.col = current.col -1;
 }
 else
 {
  if (current.row > 0)
  {
   len = text->GetLineLength(current.row-1);
   if (current.row <= visible.row)
   {
    SBPosChange(visible.row -1, visible.col);
   }
   if (len > visible.col + (_w-MARGIN)/_tw-1)
   {
    SBPosChange(visible.row, len - (_w /_tw)/ 2);
   }
   pos.row = current.row - 1;
   pos.col = len;
  }
 }
 SetCurrent(pos);
}

void OXTextFrame::NextChar()
{
 Coord pos;
 long len = text->GetLineLength(current.row);
 pos.row = current.row;
 pos.col = current.col;
 if (current.col < len)
 {
  if (current.col == visible.col + (_w-MARGIN)/_tw-1)
  {
    SBPosChange(visible.row, visible.col +(_w-MARGIN)/_tw/2);
  }
  pos.row = current.row;
  pos.col = current.col +1;
 }
 else
 {
  if (current.row < text->RowCount()-1)
  {
   if (current.row >= visible.row + (_h-MARGIN)/_th-1)
    SBPosChange(visible.row+1, 0);
   else
    SBPosChange(visible.row, 0);
   pos.row = current.row + 1;
   pos.col = 0;
  }
 }
 
 SetCurrent(pos);
}

void OXTextFrame::LineUp()
{
 Coord pos;
 long len;
 if (current.row > 0)
 {
   pos.row = current.row - 1;
  if (current.row <= visible.row)
  {
    SBPosChange(visible.row-1, visible.col);
  }
  len = text->GetLineLength(current.row-1);
  if (current.col > len)
  {
   if (len < visible.col)
   {
    if (len >= _w-MARGIN-1)
     SBPosChange(visible.row, len - (_w/_tw)/2);
    else 
     SBPosChange(visible.row, 0);
   }
   pos.col = len;
  }
  else
   pos.col = current.col;
  SetCurrent(pos);
 }
}

void OXTextFrame::LineDown()
{
 Coord pos;
 long len;
 if (current.row < text->RowCount()-1)
 {

  len = text->GetLineLength(current.row+1);
  pos.row = current.row +1;
  if (current.row == visible.row + (_h-MARGIN)/_th-1)
   SBPosChange(visible.row+1, visible.col);
  if (current.col > len)
  {
   if (len < visible.col)
   {
    if (len >= _w-MARGIN-1)
     SBPosChange(visible.row, len - (_w/_tw)/2);
    else
     SBPosChange(visible.row, 0);
   }
   pos.col = len;
  }   
  else
   pos.col = current.col;
  SetCurrent(pos);
 }
}

void OXTextFrame::ScreenUp() 
{
 if (visible.row - (_h - MARGIN)/_th +1 >= 0)
  VSBChange(visible.row - (_h - MARGIN)/_th+1, visible.col);
 else
  VSBChange(0, visible.col);
}

void OXTextFrame::ScreenDown() 
{
 long count = text->RowCount()-1;
 if (visible.row + (_h - MARGIN)/_th -1< count)
  VSBChange(visible.row + (_h - MARGIN)/_th-1, visible.col);
}


void OXTextFrame::Home()
{
 Coord pos;
 pos.row = current.row;
 pos.col = 0;
 if (pos.col != visible.col);
  HSBChange(visible.row, pos.col);
 SetCurrent(pos);
}

void OXTextFrame::End()
{
 Coord pos;
 pos.row = current.row;
 pos.col = text->GetLineLength(pos.row);
 if (pos.col > visible.col + (_w-MARGIN)/_tw-1)
  HSBChange(visible.row, pos.col - (_w-MARGIN)/_tw+1);
 SetCurrent(pos);
}

void OXTextFrame::HSBChange(long row, long col)
{
 SendMessage(_parent, MK_MSG(XC_TEXTEDIT,TXT_HSBCHANGE), 0, 0);

 if (col != visible.col)
  SBPosChange(visible.row, col);
}

void OXTextFrame::VSBChange(long row, long col)
{
 SendMessage(_parent, MK_MSG(XC_TEXTEDIT,TXT_VSBCHANGE), 0, 0);

 if (row != visible.row)
  SBPosChange(row, visible.col);
}

void OXTextFrame::SBPosChange(long row, long col)
{ 
 SetTopCol(col);
 SetTopLine(row);

 SendMessage(_parent, MK_MSG(XC_TEXTEDIT, TXT_SBPOS), row, col);
}

