/* This file is part of
* ======================================================
* 
*           LyX, the High Level Word Processor
* 	 
*	    Copyright (C) 1995 Matthias Ettrich
*
*======================================================*/

#include "config.h"
#include "lyxscreen.h"
#include "xdefinitions.h"


/* this is a workaround that _all_ screens use _one_ pixmap */
Pixmap foreground = 0;
LyXScreen* owner_of_pixmap = NULL;
int foreground_width = 0;
int foreground_height = 0;

static int foreground_refs = 0;	/* references to foreground pixmap */


/* Konstruktor */ 
LyXScreen::LyXScreen(FL_OBJECT* obj,  LyXText *text_ptr)
{
   canvas = obj;
   
   text = text_ptr;
   
   /* add reference to foreground pixmap */
   ++foreground_refs;

   /* get the dimensions of the canvas */ 
   height = obj->h;
   width = obj->w;

   first = 0;
   
   /* the cursor isnt yet visible */ 
   cursor_visible = 0;
   screen_refresh_y = -1;

   /* create the foreground pixmap */
   if (!foreground
       || foreground_height != height 
       || foreground_width != width){
     if (foreground) {
       /* free the comnmon pixmap */
       XFreePixmap(fl_display, foreground);
     }
     
     foreground = XCreatePixmap (fl_display,
				 RootWindow(fl_display, 0),
				 width, height, 
				 fl_get_visual_depth());
     foreground_height = height;
     foreground_width = width;
     owner_of_pixmap = this;
   }
}


/* destructor */ 
LyXScreen::~LyXScreen()
{
  // free the pixmap only if this was the last reference,
  // since other screens share it
  if (!(--foreground_refs)) {
	XFreePixmap(fl_display, foreground);
	foreground = None;
  }
}


void LyXScreen::Redraw()
{
   DrawFromTo(0, height);
   screen_refresh_y = -1;
   owner_of_pixmap = this;
   LyXExpose(0, 0, width, height);
   if (cursor_visible) {
      cursor_visible = 0;
      ShowCursor();
   }
}


void LyXScreen::LyXExpose(int x, int y, int exp_width, int exp_height)
{
  if (owner_of_pixmap == this) {
    XCopyArea (fl_display,
	       foreground,
	       FL_ObjWin(canvas),
	       LyXGetCopyGC(),
	       x, y,
	       exp_width, exp_height,
	       x+canvas->x, y+canvas->y);
  }
  else {
    Redraw();
  }
}


void LyXScreen::DrawFromTo(int y1, int y2)
{
   Row* row;
   long  y_text;
   long y;
   
   y_text = first + y1;
   
   /* get the first needed row */ 
   row = text->GetRowNearY(y_text);
   /* y_text is now the real beginning of the row */
   
   y = y_text - first;
   /* y1 is now the real beginning of row on the screen */
   
   while (row != NULL && y < y2) {
	 
      text->GetVisibleRow(foreground, y, row, y + first);
      y += row->height;
      row = row -> next;

   }
   
   /* maybe we have to clear the screen at the bottom */ 
   if (y < y2) {
      XFillRectangle(fl_display, foreground, LyXGetLightedGC(),
		     0, y, width, y2 - y);
   }
}


void LyXScreen::DrawOneRow(Row *row, long &y_text)
{
   int y;
   
   y = y_text - first;
      
   if (y + row->height > 0 && y - row->height <= height) {
      /* ok there is anything visible */
      
      text->GetVisibleRow(foreground, y, row, y + first);
   }
   y_text+=row->height;
}


/* draws the screen, starting with textposition y. uses as much already
* printed pixels as possible */
void LyXScreen::Draw(long  y)
{
   long old_first;
   if (y<0)
      y = 0;
   old_first = first;
   first = y;
   /* is any optimiziation possible? */ 
   if ((y - old_first) < height 
       && (old_first - y) < height) {
      if (first < old_first) {
	 XCopyArea (fl_display,
		    foreground,
		    foreground,
		    LyXGetCopyGC(),
		    0, 0, 
		    width , height - old_first + first,
		    0, old_first - first);
	DrawFromTo(0, old_first - first);
      }
      else  {
	 XCopyArea (fl_display,
		    foreground,
		    foreground,
		    LyXGetCopyGC(),
		    0, first - old_first, 
		    width , old_first + height - first, 
		    0, 0);
	 DrawFromTo(height + old_first - first, height);
      }
	
   }
   else {
      /* make a dump new-draw */ 
      DrawFromTo(0, height);
   }
   LyXExpose(0, 0, width, height);
}


void LyXScreen::ShowCursor()
{
   long  x1,x2,y1,y2;
   
   if (cursor_visible)
     return;
   
   x1 = text->cursor.x;
   x2 = text->cursor.x;
   y1 = text->cursor.y - LyXMaxAscent(text->real_current_font) - first + 1;
   if (y1 < 0)
     y1 =0;
   y2 = text->cursor.y + LyXMaxDescent(text->real_current_font) - first - 1;
   if (y2 > height)
     y2 =height;
   
   if (y2 > 0 && y1 < height) {
      XDrawLine(fl_display,
		FL_ObjWin(canvas),
		LyXGetSelectGC(),
		x1+canvas->x,y1+canvas->y,x2+canvas->x,y2+canvas->y);
      XDrawLine(fl_display,
		foreground,
		LyXGetSelectGC(),
		x1,y1,x2,y2);
      cursor_visible = 1;
   }
}


/* returns 1 if first has changed, otherwise 0 */ 
int  LyXScreen::FitManualCursor(long /*x*/, long y, int asc, int desc)
{
  long  newtop = first;
  
  if (y + desc  - first >= height)
    newtop = y - 3*height / 4;   /* the scroll region must be so big!! */
  else if (y - asc < first 
	   && first > 0) {
    newtop = y - height / 4;
  }
  if (newtop < 0)
    newtop = 0;
  
  if (newtop != first){
    Draw(newtop);
    first = newtop;
    return 1;
  }
  return 0;
}


void  LyXScreen::DrawManualCursor(long x, long y, int asc, int desc)
{
   long  x1,x2,y1,y2;
   x1 = x;
   x2 = x;
   y1 = y - first - asc;
   if (y1 < 0)
     y1 =0;
   y2 = y -first + desc;
   if (y2 > height)
     y2 =height;
   
   if (y2 > 0 && y1 < height) {
      XDrawLine(fl_display,
		FL_ObjWin(canvas),
		LyXGetSelectGC(),
		x1+canvas->x,y1+canvas->y,x2+canvas->x,y2+canvas->y);
      XDrawLine(fl_display,
		foreground,
		LyXGetSelectGC(),
		x1,y1,x2,y2);
   }
}


void LyXScreen::HideCursor()
{
   if (cursor_visible) {
      cursor_visible = 0;
      ShowCursor();
      cursor_visible = 0;
   }
}


void LyXScreen::CursorHi()
{
   if (!cursor_visible)
     ShowCursor();
}


void LyXScreen::CursorLo()
{
   if (cursor_visible)
     HideCursor();
}


void LyXScreen::CursorToggle()
{
   if (cursor_visible)
     HideCursor();
   else
     ShowCursor();
}


/* returns a new top so that the cursor is visible */ 
long LyXScreen::TopCursorVisible()
{
   long  newtop = first;

   if (text->cursor.y - text->cursor.row->baseline + text->cursor.row->height
       - first >= height){
     if (text->cursor.row->height < height && text->cursor.row->height > height/4)
       newtop = text->cursor.y + text->cursor.row->height - text->cursor.row->baseline - height;
     else
       newtop = text->cursor.y - 3*height / 4;   /* the scroll region must be so big!! */
   }
   else if (text->cursor.y - text->cursor.row->baseline < first 
	    && first > 0) {
     if (text->cursor.row->height < height && text->cursor.row->height > height/4)
       newtop = text->cursor.y - text->cursor.row->baseline;
     else
       newtop = text->cursor.y - height / 4;
   }
   if (newtop < 0)
     newtop = 0;
   
   return newtop;
}


/* scrolls the screen so that the cursor is visible, if necessary.
* returns 1 if a change was made, otherwise 0 */ 
int  LyXScreen::FitCursor()
{
   /* is a change necessary */ 
   long  newtop = TopCursorVisible();
   int result = (newtop != first);
   if (result)
     Draw(newtop);
   return result;
}

   
void  LyXScreen::Update()
{
   long y;
   
   if (text->status == LYX_NEED_MORE_REFRESH || 
       screen_refresh_y > -1 ) {
	  if (screen_refresh_y > -1 && screen_refresh_y < text->refresh_y)
	    y = screen_refresh_y;
	  else
	    y = text->refresh_y;
	  
	  if (y < first)
	    y = first;
	  DrawFromTo(y - first, height);
	  text->refresh_y = 0;
	  text->status = LYX_UNCHANGED;
	  screen_refresh_y = -1;
      XCopyArea (fl_display,
		 foreground,
		 FL_ObjWin(canvas),
		 LyXGetCopyGC(),
		 0, y - first,
		 width, height - y + first,
		 0+canvas->x, y - first+canvas->y);
   }
   else if (text->status == LYX_NEED_VERY_LITTLE_REFRESH) {
      /* ok I will update the current cursor row */
      y = text->refresh_y;
      DrawOneRow(text->refresh_row, y);
      text->status = LYX_UNCHANGED;
      XCopyArea (fl_display,
		 foreground,
		 FL_ObjWin(canvas),
		 LyXGetCopyGC(),
		 0, text->refresh_y - first,
		 width, text->refresh_row->height,
		 0+canvas->x, text->refresh_y - first+canvas->y);
   }
}


void LyXScreen::SmallUpdate()
{
   Row *row;
   long y, y2;
   if (text->status == LYX_NEED_MORE_REFRESH){
      /* ok I will update till the current cursor row */
      row = text->refresh_row;
      y = text->refresh_y;
      y2 = y;
      
      if (y > text->cursor.y) {
	 Update();
	 return;
      }
	 
      while (row && row != text->cursor.row && y < first + height) {
	 DrawOneRow(row, y);
	 row = row->next;
      }
      
      DrawOneRow(row, y);
      screen_refresh_y = y;
      screen_refresh_row = row->next;
      text->status = LYX_UNCHANGED;
      XCopyArea (fl_display,
		 foreground,
		 FL_ObjWin(canvas),
		 LyXGetCopyGC(),
		 0, y2 - first,
		 width, y - y2,
		 0+canvas->x, y2 - first+canvas->y);
   }
   else if (text->status == LYX_NEED_VERY_LITTLE_REFRESH) {
      /* ok I will update the current cursor row */
      row = text->refresh_row;
      y = text->refresh_y;
      DrawOneRow(row, y);
      text->status = LYX_UNCHANGED;
      XCopyArea (fl_display,
		 foreground,
		 FL_ObjWin(canvas),
		 LyXGetCopyGC(),
		 0, text->refresh_y - first,
		 width, row->height,
		 0+canvas->x, text->refresh_y - first+canvas->y);
   }
}


void  LyXScreen::DrawManualSelection(int x, int y, int w, int h)
{
    XFillRectangle(fl_display,FL_ObjWin(canvas),
		   LyXGetSelectGC(),
		   x+canvas->x, y+canvas->y, w, h);
    XFillRectangle(fl_display,foreground,
		   LyXGetSelectGC(),
		   x+canvas->x, y+canvas->y, w, h);
}


void LyXScreen::ToggleSelection()
{
   long top;
   long bottom;
   
   /* only if there is a selection */ 
   if (!text->selection)
     return;

   /* selection only in one row ?*/ 
   if (text->sel_start_cursor.y == text->sel_end_cursor.y) {

      /* only if anything is visible */ 
      if (text->sel_start_cursor.y - text->sel_start_cursor.row->baseline - first < height
	  && text->sel_start_cursor.y - text->sel_start_cursor.row->baseline + 
	  text->sel_start_cursor.row->height - first > 0) {
	top = text->sel_start_cursor.y - text->sel_start_cursor.row->baseline - first;
	bottom = top + text->sel_start_cursor.row->height;
	if (top<0)
	  top = 0;
	if (bottom > height)
	  bottom = height;
	 XFillRectangle(fl_display,FL_ObjWin(canvas),
		       LyXGetSelectGC(),
		       text->sel_start_cursor.x+canvas->x, 
		       top+canvas->y,
		       text->sel_end_cursor.x  - text->sel_start_cursor.x,
		       bottom - top);
	 XFillRectangle(fl_display,foreground,
		       LyXGetSelectGC(),
		       text->sel_start_cursor.x, 
		       top,
		       text->sel_end_cursor.x  - text->sel_start_cursor.x,
		       bottom - top);
      }
   }
   else  {
      /* the sel_start_cursor row first */ 
        /* only if anything is visible */ 
      if (text->sel_start_cursor.y - text->sel_start_cursor.row->baseline - first < height
	  && text->sel_start_cursor.y - text->sel_start_cursor.row->baseline + 
	  text->sel_start_cursor.row->height - first > 0) {
	top = text->sel_start_cursor.y - text->sel_start_cursor.row->baseline - first;
	bottom = top + text->sel_start_cursor.row->height;
	if (top<0)
	  top = 0;
	if (bottom > height)
	  bottom = height;
	 XFillRectangle(fl_display,FL_ObjWin(canvas),
		       LyXGetSelectGC(),
		       text->sel_start_cursor.x+canvas->x, 
		       top+canvas->y,
		       width - text->sel_start_cursor.x,
		       bottom - top);
	 XFillRectangle(fl_display,foreground,
		       LyXGetSelectGC(),
		       text->sel_start_cursor.x, 
		       top,
		       width - text->sel_start_cursor.x,
		       bottom - top);
      }

      /* the main body */ 
      
      if (text->sel_start_cursor.row->next != text->sel_end_cursor.row) {
	 top = text->sel_start_cursor.y - text->sel_start_cursor.row->baseline + text->sel_start_cursor.row->height;
	 bottom = text->sel_end_cursor.y - text->sel_end_cursor.row->baseline;
	 
	 if (top - first < 0)
	   top = first;
	 if (bottom - first < 0)
	   bottom = first;
	 
	 if (bottom - first > height)
	   bottom = first + height;
	 if (top - first > height)
	   top = first + height;
	 
	 if (top != bottom) {
	   XFillRectangle(fl_display,FL_ObjWin(canvas),
			  LyXGetSelectGC(),
			  0+canvas->x, 
			  top - first+canvas->y,
			  width,
			  bottom - top);
	   XFillRectangle(fl_display,foreground,
			  LyXGetSelectGC(),
			  0, 
			  top - first,
			  width,
			  bottom - top);
	 }
      }
      
      /* the sel_end_cursor row last */ 
      if (text->sel_end_cursor.y - text->sel_end_cursor.row->baseline - first < height
	  && text->sel_end_cursor.y - text->sel_end_cursor.row->baseline +
	  text->sel_end_cursor.row->height - first > 0) {
	top = text->sel_end_cursor.y - text->sel_end_cursor.row->baseline - first;
	bottom = top + text->sel_end_cursor.row->height;
	if (top<0)
	  top = 0;
	if (bottom > height)
	  bottom = height;
	 XFillRectangle(fl_display,FL_ObjWin(canvas),
		       LyXGetSelectGC(),
		       0+canvas->x, 
		       top+canvas->y,
		       text->sel_end_cursor.x,
		       bottom - top);
	 XFillRectangle(fl_display,foreground,
		       LyXGetSelectGC(),
		       0,
		       top,
		       text->sel_end_cursor.x,
		       bottom - top);
      }
   }
   
   XFlush(fl_display);
}
  
   
void LyXScreen::ToggleToggle()
{
   long top;
   long bottom;
   
   /* only if there is a selection */ 
   /* 
   if (!text->selection)
     return;
   */ 
   
   /* selection only in one row ?*/ 
   if (text->toggle_cursor.y == text->toggle_end_cursor.y) {

      /* only if anything is visible */ 
      if (text->toggle_cursor.y - text->toggle_cursor.row->baseline - first < height
	  && text->toggle_cursor.y - text->toggle_cursor.row->baseline + 
	  text->toggle_cursor.row->height - first > 0) {
	top = text->toggle_cursor.y - text->toggle_cursor.row->baseline - first;
	bottom = top + text->toggle_cursor.row->height;
	if (top<0)
	  top = 0;
	if (bottom > height)
	  bottom = height;
	 XFillRectangle(fl_display,FL_ObjWin(canvas),
		       LyXGetSelectGC(),
		       text->toggle_cursor.x+canvas->x, 
		       top+canvas->y,
		       text->toggle_end_cursor.x  - text->toggle_cursor.x,
		       bottom - top);
	 XFillRectangle(fl_display,foreground,
		       LyXGetSelectGC(),
		       text->toggle_cursor.x, 
		       top,
		       text->toggle_end_cursor.x  - text->toggle_cursor.x,
		       bottom - top);
      }
   }
   else  {
      /* the toggle_cursor row first */ 
        /* only if anything is visible */ 
      if (text->toggle_cursor.y - text->toggle_cursor.row->baseline - first < height
	  && text->toggle_cursor.y - text->toggle_cursor.row->baseline + 
	  text->toggle_cursor.row->height - first > 0) {
	top = text->toggle_cursor.y - text->toggle_cursor.row->baseline - first;
	bottom = top + text->toggle_cursor.row->height;
	if (top<0)
	  top = 0;
	if (bottom > height)
	  bottom = height;
	 XFillRectangle(fl_display,FL_ObjWin(canvas),
		       LyXGetSelectGC(),
		       text->toggle_cursor.x+canvas->x, 
		       top+canvas->y,
		       width - text->toggle_cursor.x,
		       bottom - top);
	 XFillRectangle(fl_display,foreground,
		       LyXGetSelectGC(),
		       text->toggle_cursor.x, 
		       top,
		       width - text->toggle_cursor.x,
		       bottom - top);
      }

      /* the main body */ 
      
      if (text->toggle_cursor.row->next != text->toggle_end_cursor.row) {
	 top = text->toggle_cursor.y - text->toggle_cursor.row->baseline + text->toggle_cursor.row->height;
	 bottom = text->toggle_end_cursor.y - text->toggle_end_cursor.row->baseline;
	 
	 if (top - first < 0)
	   top = first;
	 if (bottom - first < 0)
	   bottom = first;
	 
	 if (bottom - first > height)
	   bottom = first + height;
	 if (top - first > height)
	   top = first + height;
	 
	 if (top != bottom) {
	   XFillRectangle(fl_display,FL_ObjWin(canvas),
			  LyXGetSelectGC(),
			  0+canvas->x, 
			  top - first+canvas->y,
			  width,
			  bottom - top);
	   XFillRectangle(fl_display,foreground,
			  LyXGetSelectGC(),
			  0, 
			  top - first,
			  width,
			  bottom - top);
	 }
      }
      
      /* the toggle_end_cursor row last */ 
      if (text->toggle_end_cursor.y - text->toggle_end_cursor.row->baseline - first < height
	  && text->toggle_end_cursor.y - text->toggle_end_cursor.row->baseline +
	  text->toggle_end_cursor.row->height - first > 0) {
	top = text->toggle_end_cursor.y - text->toggle_end_cursor.row->baseline - first;
	bottom = top + text->toggle_end_cursor.row->height;
	if (top<0)
	  top = 0;
	if (bottom > height)
	  bottom = height;
	 XFillRectangle(fl_display,FL_ObjWin(canvas),
		       LyXGetSelectGC(),
		       0+canvas->x, 
		       top+canvas->y,
		       text->toggle_end_cursor.x,
		       bottom - top);
	 XFillRectangle(fl_display,foreground,
		       LyXGetSelectGC(),
		       0,
		       top,
		       text->toggle_end_cursor.x,
		       bottom - top);
      }
   }
   
   XFlush(fl_display);
}
