/*
================================ COPYRIGHT ================================
The contents of this file are subject to the VTech Informations Ltd. License
of VT-OS Ver. 1.1 operating system (the "License"); you may not use this 
file except in compliance with the License.  

Software distributed under the License is distributed on an "AS IS" basis, 
WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the License 
for the specific language governing rights and limitations under the License.
  
The Original Code is VT-OS Ver. 1.1 Operating System, released 
on October 1st, 1999
	
The Initial Developer of the Original Code is VTech Informations Ltd.  All 
codes are Copyright (C) VTech Informations Ltd. 1999.  All Rights Reserved.
===========================================================================
*/

/*
===========================================================================
File        :   lcdapi.c
Author(s)   :   Brian Lee, Thomas Cheng
Company     :   VTech Informations Ltd.
Project     :   Helio 
Date:	    :   October 1st, 1999
Purpose:	:   LCD API calls
Revision    :   1.1
Note        :   None
===========================================================================
*/              

#include "stdafx.h"
#include "LcdApi.h"
#include "TmrApi.h"
#include "StrApi.h"
#include "uievent.h"
#include "pr31700s.h"
#include "pr31700c.h"
#include "sylang.h"

//#define DEBUG_HENRY
extern Keyboard keyboard;
extern BOOLEAN  keyboard_status;
extern WORD     pen_xy_rotate;
extern SHORT    stored_bitmap_index;

BYTE    rotation;
BOOLEAN insert_pt_enable, insert_pt_on;
BYTE    insert_pt_font;
UWORD   insert_pt_tmr;
SHORT   insert_pt_xcoord, insert_pt_ycoord;

BitmapTemplate insert_pt_off_bmp;

#define MAX_FONT_PIXEL 	16			// 16 x 16 characters // brian
#define MAX_FONT_SIZE	MAX_FONT_PIXEL * MAX_FONT_PIXEL * BITS_OF_GREY / SIZE_OF_WORD // brian
UBYTE font_template[sizeof(BitmapTemplate)]; // brian
UWORD font_data[MAX_FONT_SIZE]; // brian

const 	USHORT  *font_ptr;
BYTE fcolor;
BYTE fbcolor;

const UWORD s_mask[8] = {0x00000000, 0xf0000000, 0xff000000, 0xfff00000,
		0xffff0000, 0xfffff000, 0xffffff00, 0xfffffff0};
const UWORD e_mask[8] = {0x0fffffff, 0x00ffffff, 0x000fffff, 0x0000ffff,
		0x00000fff, 0x000000ff, 0x0000000f, 0x00000000};

/* color level arrangement array */
BYTE	color_mode 		= BLACK_AND_WHITE_MODE;
BYTE    color_level[4]  = {COLOR_GREY1_16, COLOR_GREY1_16, COLOR_GREY16_16, COLOR_GREY16_16};


/*	Function	: LcdSetColorMode
*	Purpose		: This function is called to set the color mode of the display
*  Scope		: Application
*	Input		: color_mode	BLACK_AND_WHITE_MODE, GREYSCALE_MODE
*	Output      : None
*	Comment		: None
*/
void	LcdSetColorMode(BYTE input_color_mode)
{
    if (color_mode == input_color_mode)
        return;
    
	color_mode = input_color_mode;
	
    AHTurnLcdOff();
	if (input_color_mode == GREYSCALE_MODE)
	{
        color_level[COLOR_BLACK_COLOR]  =   COLOR_GREY16_16;
        color_level[COLOR_GREY2_COLOR]  =   COLOR_GREY3_4;
        color_level[COLOR_GREY1_COLOR]  =   COLOR_GREY2_4;
        color_level[COLOR_WHITE_COLOR]  =   COLOR_GREY1_16;
		
        CPU->REG_VIDEO_CTRL_7 = 0xFA50;
		
        CPU->REG_VIDEO_CTRL_8 = 0x7DA;
        CPU->REG_VIDEO_CTRL_9 = 0x7DBEA5A5;
        CPU->REG_VIDEO_CTRL_10 = 0x7DFBE;
        CPU->REG_VIDEO_CTRL_11 = 0x7A5AD;
        CPU->REG_VIDEO_CTRL_12 = 0xFBFDFE7;
        CPU->REG_VIDEO_CTRL_13 = 0x7B5ADEF;
        CPU->REG_VIDEO_CTRL_14 = 0xB9DC663;
		
		/*        
        CPU->REG_VIDEO_CTRL_8 = 0x7DA;
        CPU->REG_VIDEO_CTRL_9 = 0x7DBEA5A5;
        CPU->REG_VIDEO_CTRL_10 = 0x7DFBE;
        CPU->REG_VIDEO_CTRL_11 = 0x7A5AD;
        CPU->REG_VIDEO_CTRL_12 = 0xFBFDFE7;
        CPU->REG_VIDEO_CTRL_13 = 0x7B5ADEF;
        CPU->REG_VIDEO_CTRL_14 = 0xB9DC663;
		*/        
	}
	else
	{
        color_level[COLOR_BLACK_COLOR]  =   COLOR_GREY16_16;
        color_level[COLOR_GREY2_COLOR]  =   COLOR_GREY16_16;
        color_level[COLOR_GREY1_COLOR]  =   COLOR_GREY1_16;
        color_level[COLOR_WHITE_COLOR]  =   COLOR_GREY1_16;
		
        CPU->REG_VIDEO_CTRL_8 = 0xFFF;
        CPU->REG_VIDEO_CTRL_9 = 0xFFFF0000;
        CPU->REG_VIDEO_CTRL_10 = 0xFFFFF;
        CPU->REG_VIDEO_CTRL_11 = 0xFFFFF;
        CPU->REG_VIDEO_CTRL_12 = 0xFFFFFFF;
        CPU->REG_VIDEO_CTRL_13 = 0xFFFFFFF;
        CPU->REG_VIDEO_CTRL_14 = 0xFFFFFFF;
		
        CPU->REG_VIDEO_CTRL_8 = 0xFFF;
        CPU->REG_VIDEO_CTRL_9 = 0xFFFF0000;
        CPU->REG_VIDEO_CTRL_10 = 0xFFFFF;
        CPU->REG_VIDEO_CTRL_11 = 0xFFFFF;
        CPU->REG_VIDEO_CTRL_12 = 0xFFFFFFF;
        CPU->REG_VIDEO_CTRL_13 = 0xFFFFFFF;
        CPU->REG_VIDEO_CTRL_14 = 0xFFFFFFF;        
	}
    LcdTurnOn();
}

/*	Function	: LcdGetColorMode
*	Purpose		: This function is called to set the color mode of the display
*  Scope		: Application
*	Input		: color_mode	BLACK_AND_WHITE_MODE, GREYSCALE_MODE
*	Output      : 
*	Return		: color_mode
*	Comment		: None
*/
BYTE    LcdGetColorMode()
{
	return color_mode;
}

/*	Function	: LcdDrawHoriLine
*	Purpose		: Draw horizontal line
*  Scope		: Internal
*	Input		: Two x coordinates, one y coordinate and color
*	Output      : None
*	Comment		: x1 must not be larger than x2
*/
void LcdDrawHoriLine(SHORT xcoord1, SHORT xcoord2, SHORT ycoord1, BYTE style, BYTE color, BYTE bg_color)
{
	BYTE i, start = xcoord1	% (SIZE_OF_WORD / BITS_OF_GREY), end = (xcoord2 + 1) % (SIZE_OF_WORD / BITS_OF_GREY),
		start_block = xcoord1 / (SIZE_OF_WORD / BITS_OF_GREY), end_block = (xcoord2 + 1) / (SIZE_OF_WORD / BITS_OF_GREY);
	UWORD word;
	
	if (style == DOTTED_LINE)
	{
        word = (color * 0x10101010) | (bg_color * 0x01010101);
	}
	else if (style == NON_DOTTED_LINE)
	{
		word = color * 0x11111111;
	}
	
	if (start_block == end_block)
	{
		WordWrite(xcoord1, ycoord1, WordHead2Pack(WordRead(xcoord1, ycoord1),
			word, start, end));
	}
	else
	{
		WordWrite(xcoord1, ycoord1, WordPack(WordRead(xcoord1, ycoord1), 0, word, 0, start));
		
		for (i = start_block + 1; i < end_block; i++)
			WordWrite(SIZE_OF_WORD * i / BITS_OF_GREY, ycoord1, word);
		WordWrite(xcoord2, ycoord1, WordPack(word, 0, WordRead(xcoord2, ycoord1), 0, end));
	}
}

/*	Function	: LcdDrawVertLine
*	Purpose		: Draw vertical line
*  Scope		: Internal
*	Input		: One x coordinate, two y coordinates and color
*	Output      : None
*	Comment		: y1 must not be larger than y2
*/
void LcdDrawVertLine(SHORT xcoord1, SHORT ycoord1, SHORT ycoord2, BYTE style, BYTE color, BYTE bg_color)
{
	SHORT i;
	if (style == NON_DOTTED_LINE)
		for (i = ycoord1; i <= ycoord2; i++)
		{
			PixelWrite(xcoord1, i, color);
		}
		else if (style == DOTTED_LINE)
		{
			for (i = ycoord1; i <= ycoord2; i += 2)
				PixelWrite(xcoord1, i, color);
			for (i = ycoord1 + 1; i <= ycoord2; i += 2)
				PixelWrite(xcoord1, i, bg_color);
		}
}

/*	Function	: LcdInvertHoriLine
*	Purpose		: Invert the color of the line
*  Scope		: Internal
*	Input		: One x coordinate and two y coordinates
*	Output      : None
*	Comment		: y1 must not be larger than y2
*/
void LcdInvertHoriLine(SHORT xcoord1, SHORT xcoord2, SHORT ycoord1)
{
	BYTE i, start = xcoord1	% (SIZE_OF_WORD / BITS_OF_GREY), end = (xcoord2 + 1) % (SIZE_OF_WORD / BITS_OF_GREY),
		start_block = xcoord1 / (SIZE_OF_WORD / BITS_OF_GREY), end_block = (xcoord2) / (SIZE_OF_WORD / BITS_OF_GREY);
	if (end == 0)
		end = (SIZE_OF_WORD / BITS_OF_GREY);
	if (start_block == end_block)
	{
		WordWrite(xcoord1, ycoord1, WordRead(xcoord1, ycoord1) ^
			(((WORD_MASK << (start*BITS_OF_GREY)) >> (SIZE_OF_WORD + (start - end)*BITS_OF_GREY)) << (SIZE_OF_WORD - end*BITS_OF_GREY)));
	}
	else
	{
		WordWrite(xcoord1, ycoord1, WordRead(xcoord1, ycoord1) ^
			(WORD_MASK >> (start*BITS_OF_GREY)));
		for (i = start_block + 1; i < end_block; i++)
			WordWrite(SIZE_OF_WORD * i / BITS_OF_GREY, ycoord1, ~WordRead(SIZE_OF_WORD * i / BITS_OF_GREY, ycoord1));
		WordWrite(xcoord2, ycoord1, WordRead(xcoord2, ycoord1) ^
			(WORD_MASK << (SIZE_OF_WORD - end*BITS_OF_GREY)));
	}
}

/*	Function	: LcdGetPixel
*	Purpose		: Get color of specified pixel
*  Scope		: OS
*	Input		: Cartesian coordinates
*	Output      : Color
*	Comment		:
*/
void LcdGetPixel(SHORT xcoord1, SHORT ycoord1, BYTE *color)
{
	*color = PixelRead(xcoord1, ycoord1);
}

/*	Function	: LcdSetPixel
*	Purpose		: Set color of specified pixel
*  Scope		: OS
*	Input		: Cartesian coordinates and color
*	Output      : None
*	Comment		:
*/
void LcdSetPixel(SHORT xcoord1, SHORT ycoord1, BYTE color)
{
	PixelWrite(xcoord1, ycoord1, color);
}

/*	Function	: LcdDrawLine
*	Purpose		: Set color of specified pixel
*  Scope		: OS
*	Input		: Cartesian coordinates and color
*	Output      : None
*	Comment		:
*/
void LcdDrawLine(SHORT xcoord1, SHORT ycoord1, SHORT xcoord2, SHORT ycoord2,
				 BYTE thick, BYTE style, BYTE color, BYTE bg_color)
{
	SHORT temp, i;
	if (thick != 1)
	{
		LcdDrawEllipse(xcoord1, ycoord1, thick/2, thick/2, color, bg_color, FILL_SOLID);
		LcdDrawEllipse(xcoord2, ycoord2, thick/2, thick/2, color, bg_color, FILL_SOLID);
	}
	if (ycoord1 == ycoord2)
	{
		if (xcoord1 > xcoord2)
		{
			temp=xcoord1; xcoord1=xcoord2; xcoord2=temp;
		}
		for (i=(SHORT)-(thick/2); i<thick-(thick/2); i++)
			LcdDrawHoriLine(xcoord1, xcoord2, ycoord1+i, style, color, bg_color);
	}
	else if (xcoord1 == xcoord2)
	{
		if (ycoord1 > ycoord2)
		{
			temp=ycoord1; ycoord1=ycoord2; ycoord2=temp;
		}
		//		for (i=(SHORT)-floor(thick/2); i<thick-floor(thick/2); i++)
		for (i=(SHORT)-(thick/2); i<thick-(thick/2); i++)
			LcdDrawVertLine(xcoord1+i, ycoord1, ycoord2, style, color, bg_color);
	}
	else
	{
		SHORT sign, xcoord3, ycoord3;
		if (abs(xcoord2-xcoord1) > abs(ycoord2-ycoord1))
		{
			sign = (xcoord2>xcoord1)*2 - 1;
			/* Add */
			if (thick == 3)
			{
				temp = 100*abs(ycoord2-ycoord1)/abs(xcoord2-xcoord1);
				if (temp > 60)
					thick = 4;
			}
			else if (thick == 5)
			{
				temp = 100*abs(ycoord2-ycoord1)/abs(xcoord2-xcoord1);
				if (temp > 83)
					thick = 7;
				else if (temp > 46)
					thick = 6;
			}
			
			for (xcoord3=xcoord1; (SHORT)(xcoord3-xcoord2)*sign<=0; xcoord3+=sign)
			{
				ycoord3 = (SHORT)((float)ycoord1 + ((float)ycoord2-ycoord1)*((float)xcoord3-xcoord1)/((float)xcoord2-xcoord1) + 0.5);
				for (i=(SHORT)-(thick/2); i<thick-(thick/2); i++)
					LcdSetPixel(xcoord3, ycoord3+i, color);
			}
		}
		else
		{
			sign = (ycoord2>ycoord1)*2 - 1;
			
			/* Add */
			if (thick == 3)
			{
				temp = 100*abs(xcoord2-xcoord1)/abs(ycoord2-ycoord1);
				if (temp > 60)
					thick = 4;
			}
			else if (thick == 5)
			{
				temp = 100*abs(xcoord2-xcoord1)/abs(ycoord2-ycoord1);
				if (temp > 83)
					thick = 7;
				else if (temp > 46)
					thick = 6;
			}
			
			for (ycoord3=ycoord1; (SHORT)(ycoord3-ycoord2)*sign<=0; ycoord3+=sign)
			{
				xcoord3 = (SHORT)((float)xcoord1 + ((float)xcoord2-xcoord1)*((float)ycoord3-ycoord1)/((float)ycoord2-ycoord1) + 0.5);
				for (i=(SHORT)-(thick/2); i<thick-(thick/2); i++)
					LcdSetPixel(xcoord3+i, ycoord3, color);
			}
		}
	}
}

/*	Function	: LcdDrawBox
*	Purpose		: Draw the box
*  Scope		: OS
*	Input		: Cartesian coordinates, width, height, color and fill
*	Output      : None
*	Comment		:
*/
void LcdDrawBox(ObjectBounds *bounds, BYTE color, BYTE bg_color, BYTE fill)
{
	WORD i;
	if (fill == FILL_SOLID)
	{
		for (i=bounds->ycoord; i<bounds->ycoord+bounds->height; i++)
			LcdDrawHoriLine(bounds->xcoord, bounds->xcoord+bounds->width-1, i, NON_DOTTED_LINE, color, bg_color);
	}
	else if (fill == FILL_EMPTY)
	{
		for (i=bounds->ycoord; i<bounds->ycoord+bounds->height; i++)
			LcdDrawHoriLine(bounds->xcoord, bounds->xcoord+bounds->width-1, i, NON_DOTTED_LINE, bg_color, color);
		LcdDrawHoriLine(bounds->xcoord, bounds->xcoord+bounds->width-1, bounds->ycoord, NON_DOTTED_LINE, color, bg_color);
		LcdDrawHoriLine(bounds->xcoord, bounds->xcoord+bounds->width-1, bounds->ycoord+bounds->height-1, NON_DOTTED_LINE, color, bg_color);
		LcdDrawVertLine(bounds->xcoord, bounds->ycoord, bounds->ycoord+bounds->height-1, NON_DOTTED_LINE, color, bg_color);
		LcdDrawVertLine(bounds->xcoord+bounds->width-1, bounds->ycoord, bounds->ycoord+bounds->height-1, NON_DOTTED_LINE, color, bg_color);
	}
	else if (fill == FILL_TRANSPARENT)
	{
		LcdDrawHoriLine(bounds->xcoord, bounds->xcoord+bounds->width-1, bounds->ycoord, NON_DOTTED_LINE, color, bg_color);
		LcdDrawHoriLine(bounds->xcoord, bounds->xcoord+bounds->width-1, bounds->ycoord+bounds->height-1, NON_DOTTED_LINE, color, bg_color);
		LcdDrawVertLine(bounds->xcoord, bounds->ycoord, bounds->ycoord+bounds->height-1, NON_DOTTED_LINE, color, bg_color);
		LcdDrawVertLine(bounds->xcoord+bounds->width-1, bounds->ycoord, bounds->ycoord+bounds->height-1, NON_DOTTED_LINE, color, bg_color);
	}
}

/*	Function	: LcdInvertBox
*	Purpose		: Invert the box
*  Scope		: OS
*	Input		: Cartesian coordinates, width and height
*	Output      : None
*	Comment		:
*/
void LcdInvertBox(ObjectBounds *bounds)
{
	//	SHORT i;
	WORD i;
	for (i=bounds->ycoord; i<bounds->ycoord+bounds->height; i++)
		LcdInvertHoriLine(bounds->xcoord, bounds->xcoord+bounds->width-1, i);
}

void LcdDrawEllipse(SHORT xcoord1, SHORT ycoord1, SHORT major_radius,
					SHORT minor_radius, BYTE color, BYTE bg_color, BYTE fill)
{
	SHORT i, j, x1, x2, y1, y2, tmp;
	for (j=ycoord1-minor_radius*3/4; j<=ycoord1; j++)
	{
		tmp = sqrt(minor_radius*minor_radius-(j-ycoord1)*(j-ycoord1))+0.5;
		x1=(SHORT)(xcoord1-tmp);
		x2=(SHORT)(xcoord1+tmp);
		if (fill == FILL_SOLID)
		{
			LcdDrawHoriLine(x1, x2, j, NON_DOTTED_LINE, color, bg_color);
			LcdDrawHoriLine(x1, x2, 2*ycoord1 - j, NON_DOTTED_LINE, color, bg_color);
		}
		else if (fill == FILL_EMPTY)
		{
			LcdDrawHoriLine(x1, x2, j, NON_DOTTED_LINE, bg_color, color);
			LcdSetPixel(x1, j, color);
			LcdSetPixel(x2, j, color);
			LcdDrawHoriLine(x1, x2, 2*ycoord1 - j, NON_DOTTED_LINE, bg_color, color);
			LcdSetPixel(x1, 2*ycoord1 - j, color);
			LcdSetPixel(x2, 2*ycoord1 - j, color);
		}
		else if (fill == FILL_TRANSPARENT)
		{
			LcdSetPixel(x1, j, color);
			LcdSetPixel(x2, j, color);
			LcdSetPixel(x1, 2*ycoord1 - j, color);
			LcdSetPixel(x2, 2*ycoord1 - j, color);
		}
	}
	for (i=xcoord1-minor_radius*3/4; i<=xcoord1; i++)
	{
		tmp = sqrt(minor_radius*minor_radius-(i-xcoord1)*(i-xcoord1))+0.5;
		y1=(SHORT)(ycoord1-tmp);
		y2=(SHORT)(ycoord1+tmp);
		
		LcdSetPixel(i, y1, color);
		LcdSetPixel(i, y2, color);
		
		LcdSetPixel(2*xcoord1 - i, y1, color);
		LcdSetPixel(2*xcoord1 - i, y2, color);
	}
}

void LcdDrawArc(SHORT xcoord1, SHORT ycoord1, SHORT radius, BYTE style,
				BYTE color, BYTE bg_color, BYTE fill, BYTE arc_location)
{
	if (arc_location == 0)
	{
		LcdSetPixel(xcoord1, ycoord1-2, color);
		LcdSetPixel(xcoord1+1, ycoord1-1, color);
		LcdSetPixel(xcoord1+2, ycoord1, color);
	}
	else if (arc_location == 1)
	{
		LcdSetPixel(xcoord1, ycoord1-2, color);
		LcdSetPixel(xcoord1-1, ycoord1-1, color);
		LcdSetPixel(xcoord1-2, ycoord1, color);
	}
	else if (arc_location == 2)
	{
		LcdSetPixel(xcoord1, ycoord1+2, color);
		LcdSetPixel(xcoord1-1, ycoord1+1, color);
		LcdSetPixel(xcoord1-2, ycoord1, color);
	}
	else if (arc_location == 3)
	{
		LcdSetPixel(xcoord1, ycoord1+2, color);
		LcdSetPixel(xcoord1+1, ycoord1+1, color);
		LcdSetPixel(xcoord1+2, ycoord1, color);
	}
}

void LcdEraseRegion(ObjectBounds *bounds)
{
	LcdDrawBox(bounds, color_level[COLOR_WHITE_COLOR], ColorInvert(color_level[COLOR_WHITE_COLOR]), FILL_SOLID);
}

void LcdGetBitmap(BitmapTemplate *save_behind)
{
	SHORT i, j, k=0;
	BYTE start=save_behind->xcoord%(SIZE_OF_WORD / BITS_OF_GREY),
		end=(save_behind->xcoord+save_behind->width)%(SIZE_OF_WORD / BITS_OF_GREY);
	BYTE width=save_behind->width%(SIZE_OF_WORD / BITS_OF_GREY);
	UWORD word1[2];
	SHORT xcoord;
	save_behind->quantisation = Q_FOUR_BIT;
	save_behind->size = ((save_behind->width-1)/(SIZE_OF_WORD / BITS_OF_GREY)+1)*save_behind->height*sizeof(UWORD);
    save_behind->bitmap_data = (UWORD *)qmalloc(save_behind->size);
	if (end == 0)
		end = (SIZE_OF_WORD / BITS_OF_GREY);
	for (i=save_behind->ycoord; i<save_behind->ycoord+save_behind->height; i++)
	{
		xcoord = save_behind->xcoord;
		word1[0] = WordRead(xcoord, i);
		xcoord += (SIZE_OF_WORD / BITS_OF_GREY);
		for (j=0; j<(SHORT)(save_behind->width)/(SIZE_OF_WORD / BITS_OF_GREY);j++)
		{
			word1[1] = WordRead(xcoord, i);
			save_behind->bitmap_data[k++] = WordBodyPack(word1[0], word1[1], (SIZE_OF_WORD / BITS_OF_GREY)-start);
			word1[0] = word1[1];
			xcoord += (SIZE_OF_WORD / BITS_OF_GREY);
		}
		if (width != 0)
		{
			if ((SIZE_OF_WORD / BITS_OF_GREY)-start >= width+(width==0)*(SIZE_OF_WORD / BITS_OF_GREY))
			{
				save_behind->bitmap_data[k++] = WordTail1Pack(word1[0], start, 0, end-start);
			}
			else
			{
				word1[1] = WordRead(xcoord, i);
				save_behind->bitmap_data[k++] = WordTail2Pack(word1[0], word1[1], 0, (SIZE_OF_WORD / BITS_OF_GREY)-start, end-start);
			}
		}
	}
}

void LcdDrawBitmap(BitmapTemplate *bitmap_struct, BOOLEAN invert)
{
	UWORD xw_start, xw_end, mask_start, mask_end;
	UWORD *b_data = bitmap_struct->bitmap_data;
	UWORD line, xw, line_end;
	UWORD r_shift, o_data, size;
	ObjectBounds bounds;
	BitmapTemplate bitmap_struct2;
	
	if (bitmap_struct->quantisation == Q_TWO_BIT)
	{
		BmpQ2ToBmpQ4(bitmap_struct, &bitmap_struct2);
		b_data = bitmap_struct2.bitmap_data;
	}
	
	xw_start = bitmap_struct->xcoord;
	xw_end = xw_start + bitmap_struct->width - 1 + (bitmap_struct->width==0);
	
	r_shift = xw_start & 7;
	mask_start = s_mask[(UBYTE)(xw_start & 7)];
	mask_end = e_mask[(UBYTE)(xw_end & 7)];
	xw_start = xw_start & 0xfffffff8;
    xw_end = xw_end & 0xfffffff8;
	
	if(xw_start == xw_end) {
		mask_start = mask_start | mask_end;
		mask_end = ~mask_start;
	}
	
	line = bitmap_struct->width & 7;
	if(line == 0) line = 8;
	if((line+r_shift) <= 8) size = 1; else size = 0;
	
	r_shift = r_shift << 2;
	line = bitmap_struct->ycoord;
	line_end = line + bitmap_struct->height;
	
	while(line < line_end) {
		
		xw = xw_start;
		if(xw_start == xw_end) {
			o_data = (WordRead(xw, line) & mask_start) | ((*b_data >> r_shift) & mask_end);
		}
		else {
			o_data = (WordRead(xw, line) & mask_start) | (*b_data >> r_shift);
			WordWrite(xw, line, o_data);
			xw+=8;
			
			while(xw < xw_end) {
				if(r_shift) o_data = *b_data << (32 - r_shift); else o_data = 0;
				b_data++;
				o_data = o_data | (*b_data >> r_shift);
				WordWrite(xw, line, o_data);
				xw+=8;
			}
			
			if(r_shift) o_data = *b_data << (32 - r_shift); else o_data = 0;
			
			if(size) {
				b_data++;
				o_data = o_data | (*b_data >> r_shift);
			}
			o_data = (WordRead(xw, line) & mask_end) | (o_data & ~mask_end);
		}
		WordWrite(xw, line, o_data);
		b_data++;
		line++;
	}
	
	if (invert == COLOR_INVERT)
	{
		bounds.xcoord = bitmap_struct->xcoord;
		bounds.ycoord = bitmap_struct->ycoord;
		bounds.width = bitmap_struct->width;
		bounds.height = bitmap_struct->height;
		LcdInvertBox(&bounds);
	}
	
	if (bitmap_struct->quantisation == Q_TWO_BIT)
        pfree(bitmap_struct2.bitmap_data);
}

BitmapTemplate *FontToBmp(BYTE character, BYTE color, BYTE bg_color, BYTE font)
{
	// brian    BYTE i, j;
    USHORT 	byte;
    UWORD 	i, j, word1, word2; // brian
    const 	USHORT  *byte_ptr;
	UWORD 	*data_ptr = font_data;
	UWORD 	width;
	BitmapTemplate *bitmap_struct;
	
    bitmap_struct = (BitmapTemplate *)font_template; // brian
	
													 /*
													 byte_ptr = FONT_1[(UBYTE)character];
													 if (font == MEDIUM_FONT)
													 byte_ptr = FONT_2[(UBYTE)character];
													 else if (font == LARGE_FONT)
													 byte_ptr = FONT_3[(UBYTE)character];
													 
													   if(font_ptr != byte_ptr || fcolor != color || fbcolor == bg_color) {
													   font_ptr = byte_ptr;
													   fcolor = color;
													   fbcolor = bg_color;
													   
														 width = FONT_WIDTH[font][(UBYTE)character];
	*/
	
	
	if(character < 0)
		byte_ptr = Font1 + (FONT_1_HEIGHT * (NUM_OF_CHARS + character));
	else
		byte_ptr = Font1 + (FONT_1_HEIGHT * character);
	
	
   	if (font == MEDIUM_FONT)
	{
		if(character < 0)
			byte_ptr = Font2 + (FONT_2_HEIGHT * (NUM_OF_CHARS + character));
		else
			byte_ptr = Font2 + (FONT_2_HEIGHT * character);
	}
   	else if (font == LARGE_FONT)
	{
		if(character < 0)
			byte_ptr = Font3 + (FONT_3_HEIGHT * (NUM_OF_CHARS + character));
		else
			byte_ptr = Font3 + (FONT_3_HEIGHT * character);
	}			
	
	if(font_ptr != byte_ptr || fcolor != color || fbcolor == bg_color) {
		font_ptr = byte_ptr;
		fcolor = color;
		fbcolor = bg_color;
		
		width = FontWidth[font * NUM_OF_CHARS + (UBYTE)character];
		
		bitmap_struct->width = width;
		
		bitmap_struct->height = (SHORT)(FONT_HEIGHT[font] + SPACE_LINE);
		
		bitmap_struct->bitmap_data = (UWORD *)font_data;
		bitmap_struct->quantisation = Q_FOUR_BIT;
		
		// calculate the background color
		i = SIZE_OF_WORD/BITS_OF_GREY;
		j = 0;
		do {j <<= BITS_OF_GREY; j += bg_color;} while (--i);
		
		// fill the space line with background color
		i = SPACE_LINE;
		while(i) {
			*data_ptr++ = j;
			if (width > 8) *data_ptr++ = j;
			i--;
		}
		
		i = FONT_HEIGHT[font];
		do {
			byte = *byte_ptr;
			byte_ptr++;
			word1 = 0;
			word2 = 0;
			
			j = SIZE_OF_WORD/BITS_OF_GREY;
			do {
				word1 <<= BITS_OF_GREY;
				if ((USHORT)(byte & 0x8000) == 0x8000)
					word1 += color;
				else
					word1 += bg_color;
				if (width > 8) {
					word2 <<= BITS_OF_GREY;
					if ((USHORT)(byte & 0x0080) == 0x0080)
						word2 += color;
					else
						word2 += bg_color;
				}
				byte <<= 1;
			} while (--j);
			
			*data_ptr++ = word1;
			if (width > 8) *data_ptr++ = word2;
		} while (--i);
	}
	return bitmap_struct;
}

void BmpQ2ToBmpQ4(BitmapTemplate *bitmap_struct1, BitmapTemplate *bitmap_struct2)
{
    WORD i, j, l, k;
	UWORD *src, *dest;
	UWORD word1, word2;
	bitmap_struct2->xcoord = bitmap_struct1->xcoord;
	bitmap_struct2->ycoord = bitmap_struct1->ycoord;
	bitmap_struct2->width = bitmap_struct1->width;
	bitmap_struct2->height = bitmap_struct1->height;
	bitmap_struct2->quantisation = Q_FOUR_BIT;
	bitmap_struct2->size = ((bitmap_struct2->width-1)/(SIZE_OF_WORD / BITS_OF_GREY)+1) * bitmap_struct2->height * sizeof(UWORD);
    bitmap_struct2->bitmap_data = (UWORD *)pmalloc(bitmap_struct2->size);
	
	src = bitmap_struct1->bitmap_data;
	dest = bitmap_struct2->bitmap_data;
	
	for (i = 0; i <	bitmap_struct2->height; i++)
	{
		for (j = 0; j < (bitmap_struct2->width-1)/(SIZE_OF_WORD/2)+1; j++)
		{
			word2 = *src++;
			for (k = 0; k < 2; k++)
			{
				word1 = 0;
				for (l = 0; l < SIZE_OF_WORD/BITS_OF_GREY ; l++)
				{
					word1 <<= BITS_OF_GREY;
					if(word2 >= 0xC0000000) word1 += color_level[COLOR_BLACK_COLOR];
					else if (word2 >= 0x80000000) word1 += color_level[COLOR_GREY2_COLOR];
                    else if (word2 >= 0x40000000) word1 += color_level[COLOR_GREY1_COLOR];
					else word1 += color_level[COLOR_WHITE_COLOR];
					word2 <<= 2;
				}
				if (16*j+8*k < bitmap_struct2->width) *dest++ = word1;
			}
		}
	}
}

void BmpQ4ToBmpQ1(BitmapTemplate *bitmap_struct1, BitmapTemplate *bitmap_struct2)
{
	BYTE i, j, l, k;
	USHORT m=0, n=0;
	UWORD word1, word2, word3;
	bitmap_struct2->xcoord = bitmap_struct1->xcoord;
	bitmap_struct2->ycoord = bitmap_struct1->ycoord;
	bitmap_struct2->width = bitmap_struct1->width;
	bitmap_struct2->height = bitmap_struct1->height;
	bitmap_struct2->quantisation = Q_ONE_BIT;
	bitmap_struct2->size = ((bitmap_struct2->width-1)/SIZE_OF_WORD+1)*bitmap_struct2->height*sizeof(UWORD);
    bitmap_struct2->bitmap_data = (UWORD *)qmalloc(bitmap_struct2->size);
	
	for (i = 0; i <	bitmap_struct2->height; i++)
	{
		for (j = 0; j < (bitmap_struct2->width-1)/SIZE_OF_WORD+1; j++)
		{
			word1 = 0;
			for (k = 0; k < 4; k++)
			{
				if (32*j+8*k < bitmap_struct2->width)
					word2 = bitmap_struct1->bitmap_data[m++];
				else
					word2 = 0;
				for (l = 0; l < SIZE_OF_WORD/BITS_OF_GREY ; l++)
				{
					word1 <<= 1;
					word3 = word2 & 0x80000000;
					if (word3 != 0)
						word1 += 1;
					word2 <<= 4;
				}
			}
			bitmap_struct2->bitmap_data[n++] = word1;
		}
	}
}

void BmpQ1ToBmpQ4(BitmapTemplate *bitmap_struct1, BitmapTemplate *bitmap_struct2)
{
	BYTE i, j, l, k;
	USHORT m=0, n=0;
	UWORD word1, word2, word3;//, size;
	bitmap_struct2->xcoord = bitmap_struct1->xcoord;
	bitmap_struct2->ycoord = bitmap_struct1->ycoord;
	bitmap_struct2->width = bitmap_struct1->width;
	bitmap_struct2->height = bitmap_struct1->height;
	bitmap_struct2->quantisation = Q_FOUR_BIT;
	bitmap_struct2->size = ((bitmap_struct2->width-1)/(SIZE_OF_WORD / BITS_OF_GREY)+1)*bitmap_struct2->height*sizeof(UWORD);
    bitmap_struct2->bitmap_data = (UWORD *)qmalloc(bitmap_struct2->size);
	
	for (i = 0; i <	bitmap_struct2->height; i++)
	{
		for (j = 0; j < (bitmap_struct2->width-1)/SIZE_OF_WORD+1; j++)
		{
			word2 = bitmap_struct1->bitmap_data[m++];
			for (k = 0; k < 4; k++)
			{
				word1 = 0;
				for (l = 0; l < SIZE_OF_WORD/BITS_OF_GREY ; l++)
				{
					word1 <<= BITS_OF_GREY;
					word3 = word2 & 0x80000000;
					if (word3 != 0)
						word1 += 0xF;
					word2 <<= 1;
				}
				if (32*j+8*k < bitmap_struct2->width)
					bitmap_struct2->bitmap_data[n++] = word1;
			}
		}
	}
}

void LcdDrawCharacter(SHORT xcoord, SHORT ycoord, BYTE character,
					  BYTE color, BYTE bg_color, BYTE font)
{
	BitmapTemplate *bitmap_struct;
	bitmap_struct = FontToBmp(character, color, bg_color, font);
	bitmap_struct->xcoord = xcoord;
	bitmap_struct->ycoord = ycoord;
	LcdDrawBitmap(bitmap_struct, COLOR_NO_INVERT);
}

void LcdDrawCharacterS(SHORT xcoord, SHORT ycoord, BYTE character,
					   BYTE color, BYTE bg_color, BYTE font)
{
	BitmapTemplate *bitmap_struct;
	UWORD i, *buf;
	bitmap_struct = FontToBmp(character, color, bg_color, font);
	bitmap_struct->xcoord = xcoord;
	bitmap_struct->ycoord = ycoord;
	buf = bitmap_struct->bitmap_data;
	LcdGetBitmap(bitmap_struct);
	for (i=0; i<bitmap_struct->size/4; i++)
		bitmap_struct->bitmap_data[i] |= buf[i];
	LcdDrawBitmap(bitmap_struct, COLOR_NO_INVERT);
    qfree(bitmap_struct->bitmap_data);
}

void LcdDrawCharSpace(SHORT xcoord1, SHORT ycoord1, BYTE color, BYTE font)
{
	SHORT i, j;
	for (i=xcoord1; i<xcoord1+SPACE_CHAR; i++)
		for (j=ycoord1; j<ycoord1+FONT_HEIGHT[font]+SPACE_LINE; j++)
			PixelWrite(i, j, color);
}

void LcdDrawCharSpaceS(SHORT xcoord1, SHORT ycoord1, BYTE color, BYTE font)
{
	SHORT i, j;
	for (i=xcoord1; i<xcoord1+SPACE_CHAR; i++)
		for (j=ycoord1; j<ycoord1+FONT_HEIGHT[font]+SPACE_LINE; j++)
			PixelWrite(i, j, color | PixelRead(i, j));
}

void LcdDrawFixedString(ObjectBounds *bounds, BYTE *string, BYTE color,
						BYTE bg_color, BYTE font, BOOLEAN dotdot, SHORT margin)
{
	SHORT xcoord1 = bounds->xcoord, ycoord1 = bounds->ycoord, text_width;
	ObjectBounds bounds2;
	BYTE strdotdot[4] = "...";
	BYTE *temp_string = string;
	
	
	//	if (bounds->height >= FONT_HEIGHT[font] + SPACE_LINE)
	//	{
	xcoord1 += margin;
	ycoord1 += margin;
	
	text_width = StrGetWidth(temp_string, font);
	
	if (text_width <= bounds->width)
	{
		while ((UBYTE)*temp_string)
		{
			if ((UBYTE)*temp_string == '\r')
				break;
			
			else if ((UBYTE)*temp_string == 11)
			{
				temp_string++;
				xcoord1 += (UBYTE)*temp_string++;
			}
			else
			{
				LcdDrawCharSpace(xcoord1, ycoord1, bg_color, font);
				xcoord1 += SPACE_CHAR;
				LcdDrawCharacter(xcoord1, ycoord1, *temp_string, color, bg_color, font);
				//xcoord1 += FONT_WIDTH[font][(UBYTE)*temp_string++];
				xcoord1 += FontWidth[font * NUM_OF_CHARS + (UBYTE)*temp_string++];
			}
		}
		LcdDrawCharSpace(xcoord1, ycoord1, bg_color, font);
		xcoord1 += SPACE_CHAR;
	}
	else
	{
		while ((UBYTE)*temp_string)
		{
			if ((UBYTE)*temp_string == '\r')
				break;
			else if (*temp_string == 11)
			{
				temp_string++;
				xcoord1 += (UBYTE)*temp_string++;
			}
			else
			{
				LcdDrawCharSpace(xcoord1, ycoord1, bg_color, font);
				xcoord1 += SPACE_CHAR;
				if (dotdot == DOTDOT)
				{
					if (xcoord1 + FontWidth[font * NUM_OF_CHARS + (UBYTE)*temp_string] + StrGetWidth((BYTE *)strdotdot, font) >=
						bounds->xcoord + bounds->width)
					{
						bounds2.xcoord = xcoord1 - SPACE_LINE;
						bounds2.ycoord = ycoord1;
						bounds2.width = StrGetWidth((BYTE *)strdotdot, font);
						LcdDrawFixedString(&bounds2, (BYTE *)strdotdot, color, bg_color, font, 0,MARGIN_0);
						xcoord1 += bounds2.width - 2*SPACE_LINE;
						break;
					}
				}
				LcdDrawCharacter(xcoord1, ycoord1, *temp_string, color, bg_color, font);
				xcoord1 += FontWidth[font * NUM_OF_CHARS + (UBYTE)*temp_string++];
				if (dotdot == NO_DOTDOT)
				{
					if (xcoord1 + FontWidth[font * NUM_OF_CHARS + (UBYTE)*temp_string] + SPACE_CHAR >
						bounds->xcoord + bounds->width)
						break;
				}
			}
		}
		LcdDrawCharSpace(xcoord1, ycoord1, bg_color, font);
		xcoord1 += SPACE_CHAR;
	}
	//	}
}

void LcdDrawFixedStringS(ObjectBounds *bounds, BYTE *string, BYTE color,
						 BYTE bg_color, BYTE font, BOOLEAN dotdot, SHORT margin)
{
	SHORT xcoord1 = bounds->xcoord, ycoord1 = bounds->ycoord, text_width;
	ObjectBounds bounds2;
	BYTE strdotdot[4] = "...";
	BYTE *temp_string = string;
	
	if (color < bg_color)
		LcdDrawFixedString(bounds, string, color, bg_color, font, dotdot, margin);
	else
	{
		xcoord1 += margin;
		ycoord1 += margin;
		
		text_width = StrGetWidth(temp_string, font);
		
		if (text_width <= bounds->width)
		{
			while ((UBYTE)*temp_string)
			{
				LcdDrawCharSpaceS(xcoord1, ycoord1, bg_color, font);
				xcoord1 += SPACE_CHAR;
				LcdDrawCharacterS(xcoord1, ycoord1, *temp_string, color, bg_color, font);
				//xcoord1 += FONT_WIDTH[font][(UBYTE)*temp_string++];
				xcoord1 += FontWidth[font * NUM_OF_CHARS + (UBYTE)*temp_string++];
			}
			LcdDrawCharSpaceS(xcoord1, ycoord1, bg_color, font);
			xcoord1 += SPACE_CHAR;
		}
		else
		{
			while ((UBYTE)*temp_string)
			{
				LcdDrawCharSpaceS(xcoord1, ycoord1, bg_color, font);
				xcoord1 += SPACE_CHAR;
				if (dotdot == DOTDOT)
				{
					if (xcoord1 + FontWidth[font * NUM_OF_CHARS + (UBYTE)*temp_string] + StrGetWidth((BYTE *)strdotdot, font) >
						bounds->xcoord + bounds->width)
					{
						bounds2.xcoord = xcoord1 - SPACE_LINE;
						bounds2.ycoord = ycoord1;
						bounds2.width = StrGetWidth((BYTE *)strdotdot, font);
						LcdDrawFixedStringS(&bounds2, (BYTE *)strdotdot, color, bg_color, font, 0,MARGIN_0);
						xcoord1 += bounds2.width - 2*SPACE_LINE;
						break;
					}
				}
				LcdDrawCharacterS(xcoord1, ycoord1, *temp_string, color, bg_color, font);
				xcoord1 += FontWidth[font * NUM_OF_CHARS + (UBYTE)*temp_string++];
				if (dotdot == NO_DOTDOT)
				{
					if (xcoord1 + FontWidth[font * NUM_OF_CHARS + (UBYTE)*temp_string] + SPACE_CHAR >
						bounds->xcoord + bounds->width)
						break;
				}
			}
			LcdDrawCharSpaceS(xcoord1, ycoord1, bg_color, font);
			xcoord1 += SPACE_CHAR;
		}
	}
}

void LcdDrawVariableString(Field *field_ptr)
{
	SHORT xcoord1, xcoord2, ycoord1, ycoord2, width, height;
	WORD i, highlight_start, highlight_length;
	BYTE *char_string, *string = field_ptr->field_string;
	ObjectBounds bounds1, bounds2;
	//	field_ptr->field_current_num_chars = strlen((char *)string);
	//    char_string = (BYTE *)qmalloc(1000*sizeof(BYTE));
    BYTE char_buf[160];
    char_string = char_buf;
	
    if (field_ptr->field_highlight_length == 0 ||
        !(field_ptr->field_attr.field_highlight))
	{
		highlight_start = 0;
		highlight_length = 0;
	}
	else if (field_ptr->field_highlight_length > 0)
	{
		highlight_start = field_ptr->field_highlight_start_char;
		highlight_length = field_ptr->field_highlight_length;
	}
	else
	{
		highlight_start = field_ptr->field_highlight_start_char
			+ field_ptr->field_highlight_length;
		highlight_length = -field_ptr->field_highlight_length;
	}
	if (field_ptr->identification.table_related == 0xFFFF)
	{
		xcoord1 = field_ptr->bounds.xcoord + (SHORT)(field_ptr->field_style);
		ycoord1 = field_ptr->bounds.ycoord + (SHORT)(field_ptr->field_style);
		width = field_ptr->bounds.width - (SHORT)(field_ptr->field_style)*2;
		height = field_ptr->bounds.height - (SHORT)(field_ptr->field_style)*2;
	}
	else
	{
		xcoord1 = field_ptr->screen_bounds.xcoord + (SHORT)(field_ptr->field_style);
		ycoord1 = field_ptr->screen_bounds.ycoord + (SHORT)(field_ptr->field_style);
		width = field_ptr->screen_bounds.width - (SHORT)(field_ptr->field_style)*2;
		height = field_ptr->screen_bounds.height - (SHORT)(field_ptr->field_style)*2;
	}
	
	//StrAnalyzeLine(field_ptr);
	
	field_ptr->field_num_lines_displayed = height / (SPACE_LINE + FONT_HEIGHT[field_ptr->field_font_id]);
	if (field_ptr->field_top_line_num + field_ptr->field_num_lines_displayed > field_ptr->field_total_num_lines)
		field_ptr->field_num_lines_displayed = field_ptr->field_total_num_lines - field_ptr->field_top_line_num;
	
	ycoord2 = ycoord1;
	
   	bounds1.width = LCD_WIDTH;	bounds1.height = LCD_HEIGHT;
	for (i = field_ptr->field_top_line_num; i < field_ptr->field_top_line_num + field_ptr->field_num_lines_displayed; i++)
	{
		xcoord2 = xcoord1;
		if (highlight_start >= field_ptr->field_lineinfo[i].start + field_ptr->field_lineinfo[i].length
			|| highlight_start + highlight_length <= field_ptr->field_lineinfo[i].start)
		{
			StrExtract(string, field_ptr->field_lineinfo[i].start, field_ptr->field_lineinfo[i].length, char_string);
			bounds1.xcoord = xcoord2;	bounds1.ycoord = ycoord2;
			LcdDrawFixedString(&bounds1, char_string, field_ptr->field_font_color, field_ptr->field_background_color, field_ptr->field_font_id, NO_DOTDOT,MARGIN_0);
		}
		else
		{
			if (highlight_start > field_ptr->field_lineinfo[i].start)
			{
				StrExtract(string, field_ptr->field_lineinfo[i].start, highlight_start - field_ptr->field_lineinfo[i].start, char_string);
				bounds1.xcoord = xcoord2;	bounds1.ycoord = ycoord2;
				LcdDrawFixedString(&bounds1, char_string, field_ptr->field_font_color, field_ptr->field_background_color, field_ptr->field_font_id, NO_DOTDOT,MARGIN_0);
				xcoord2 += StrGetWidth(char_string, field_ptr->field_font_id) - SPACE_CHAR;
				if (highlight_start + highlight_length
					< field_ptr->field_lineinfo[i].start + field_ptr->field_lineinfo[i].length)
				{
					StrExtract(string, highlight_start, highlight_length, char_string);
					bounds1.xcoord = xcoord2;	bounds1.ycoord = ycoord2;
					LcdDrawFixedString(&bounds1, char_string, ColorInvert(field_ptr->field_font_color), ColorInvert(field_ptr->field_background_color), field_ptr->field_font_id, NO_DOTDOT,MARGIN_0);                 	
					xcoord2 += StrGetWidth(char_string, field_ptr->field_font_id) - SPACE_CHAR;
					StrExtract(string, highlight_start + highlight_length,
						field_ptr->field_lineinfo[i].start + field_ptr->field_lineinfo[i].length
						- highlight_start - highlight_length, char_string);
					bounds1.xcoord = xcoord2;	bounds1.ycoord = ycoord2;
					LcdDrawFixedString(&bounds1, char_string, field_ptr->field_font_color, field_ptr->field_background_color, field_ptr->field_font_id, NO_DOTDOT,MARGIN_0);
                    LcdDrawCharSpace(xcoord2, ycoord2, ColorInvert(field_ptr->field_background_color), field_ptr->field_font_id);
				}
				else
				{
					StrExtract(string, highlight_start, field_ptr->field_lineinfo[i].start
						+ field_ptr->field_lineinfo[i].length - highlight_start, char_string);
					bounds1.xcoord = xcoord2;	bounds1.ycoord = ycoord2;
					LcdDrawFixedString(&bounds1, char_string, ColorInvert(field_ptr->field_font_color), ColorInvert(field_ptr->field_background_color), field_ptr->field_font_id, NO_DOTDOT,MARGIN_0);
				}
			}
			else
			{
				
				if (highlight_start + highlight_length
					< field_ptr->field_lineinfo[i].start + field_ptr->field_lineinfo[i].length)
				{
					StrExtract(string, field_ptr->field_lineinfo[i].start, highlight_start
						+ highlight_length - field_ptr->field_lineinfo[i].start, char_string);
					bounds1.xcoord = xcoord2;	bounds1.ycoord = ycoord2;
					LcdDrawFixedString(&bounds1, char_string, ColorInvert(field_ptr->field_font_color), ColorInvert(field_ptr->field_background_color), field_ptr->field_font_id, NO_DOTDOT,MARGIN_0);
					xcoord2 += StrGetWidth(char_string, field_ptr->field_font_id) - SPACE_CHAR;
					StrExtract(string, highlight_start + highlight_length,
						field_ptr->field_lineinfo[i].start + field_ptr->field_lineinfo[i].length
						- highlight_start - highlight_length, char_string);
					bounds1.xcoord = xcoord2;	bounds1.ycoord = ycoord2;
					LcdDrawFixedString(&bounds1, char_string, field_ptr->field_font_color, field_ptr->field_background_color, field_ptr->field_font_id, NO_DOTDOT,MARGIN_0);
                    LcdDrawCharSpace(xcoord2, ycoord2, ColorInvert(field_ptr->field_background_color), field_ptr->field_font_id);
				}
				else
				{
					StrExtract(string, field_ptr->field_lineinfo[i].start, field_ptr->field_lineinfo[i].length,
						char_string);
					bounds1.xcoord = xcoord2;	bounds1.ycoord = ycoord2;
					LcdDrawFixedString(&bounds1, char_string, ColorInvert(field_ptr->field_font_color), ColorInvert(field_ptr->field_background_color), field_ptr->field_font_id, NO_DOTDOT,MARGIN_0);
				}
			}
		}
		/* Erase Region after text */
		bounds2.xcoord = xcoord2 + StrGetWidth(char_string, field_ptr->field_font_id);
		bounds2.ycoord = ycoord2;
		bounds2.width =	xcoord1 + width - bounds2.xcoord;
		bounds2.height = FONT_HEIGHT[field_ptr->field_font_id]+SPACE_LINE;
		LcdEraseRegion(&bounds2);
		if (field_ptr->field_back_line == 1 || (field_ptr->field_back_line == 2 && color_mode == BLACK_AND_WHITE_MODE))
			LcdDrawHoriLine(bounds2.xcoord, bounds2.xcoord+bounds2.width-1,
			ycoord2 + FONT_HEIGHT[field_ptr->field_font_id], DOTTED_LINE, color_level[COLOR_BLACK_COLOR], color_level[COLOR_WHITE_COLOR]);
        else if (field_ptr->field_back_line == 2)
			LcdDrawHoriLine(bounds2.xcoord, bounds2.xcoord+bounds2.width-1,
			ycoord2 + FONT_HEIGHT[field_ptr->field_font_id], NON_DOTTED_LINE, color_level[COLOR_GREY1_COLOR], color_level[COLOR_WHITE_COLOR]);
		ycoord2 += FONT_HEIGHT[field_ptr->field_font_id] + SPACE_LINE;
	}
	bounds2.xcoord = xcoord1;
	bounds2.width = width;
	bounds2.height = FONT_HEIGHT[field_ptr->field_font_id];
	
	/* Draw back line */
	if (field_ptr->field_back_line == 1 || (field_ptr->field_back_line == 2 && color_mode == BLACK_AND_WHITE_MODE))
		for (i = ycoord1+(field_ptr->field_num_lines_displayed+1)*(FONT_HEIGHT[field_ptr->field_font_id]+SPACE_LINE)-SPACE_LINE; i < ycoord1 + height; i += FONT_HEIGHT[field_ptr->field_font_id] + SPACE_LINE)
		{
			bounds2.ycoord = i-FONT_HEIGHT[field_ptr->field_font_id];
			LcdEraseRegion(&bounds2);
			LcdDrawHoriLine(xcoord1, xcoord1+width-1, i, DOTTED_LINE, color_level[COLOR_BLACK_COLOR], color_level[COLOR_WHITE_COLOR]);
		}
		else if (field_ptr->field_back_line == 2)
			for (i = ycoord1+(field_ptr->field_num_lines_displayed+1)*(FONT_HEIGHT[field_ptr->field_font_id]+SPACE_LINE)-SPACE_LINE; i < ycoord1 + height; i += FONT_HEIGHT[field_ptr->field_font_id] + SPACE_LINE)
			{
				bounds2.ycoord = i-FONT_HEIGHT[field_ptr->field_font_id];
				LcdEraseRegion(&bounds2);
				LcdDrawHoriLine(xcoord1, xcoord1+width-1, i, NON_DOTTED_LINE, color_level[COLOR_GREY1_COLOR], color_level[COLOR_WHITE_COLOR]);
			}
			else
				for (i = ycoord1+(field_ptr->field_num_lines_displayed+1)*(FONT_HEIGHT[field_ptr->field_font_id]+SPACE_LINE)-SPACE_LINE; i < ycoord1 + height; i += FONT_HEIGHT[field_ptr->field_font_id] + SPACE_LINE)
				{
				}
				
				/* Erase bottom region */
				bounds2.ycoord = i-FONT_HEIGHT[field_ptr->field_font_id];
				bounds2.height = ycoord1 + height - bounds2.ycoord;
				LcdEraseRegion(&bounds2);
}

void LcdEnableInsertPt(BOOLEAN enable, SHORT xcoord, SHORT ycoord, BYTE font)
{
    if (insert_pt_enable == TRUE)
        TmrIntDisable(insert_pt_tmr);
    if (insert_pt_enable == TRUE && insert_pt_on == TRUE)
		LcdDrawBlinkInsertPt();
	//        LcdDrawInsertPt(insert_pt_xcoord, insert_pt_ycoord);
	// Change 4s
    if (insert_pt_enable == TRUE)
	{
        qfree(insert_pt_off_bmp.bitmap_data);
		insert_pt_off_bmp.bitmap_data = 0;
	}
	// Change 4e
    insert_pt_enable = enable;
    insert_pt_xcoord = xcoord;
    insert_pt_ycoord = ycoord;
    insert_pt_font = font;
    insert_pt_on = FALSE;
    if (enable == TRUE)
	{
		// Change 4s
		insert_pt_off_bmp.xcoord = xcoord;
		insert_pt_off_bmp.ycoord = ycoord;
		insert_pt_off_bmp.width = SPACE_CHAR + 1;
		insert_pt_off_bmp.height = SPACE_LINE + FONT_HEIGHT[insert_pt_font];
		LcdGetBitmap(&insert_pt_off_bmp);
		// Change 4e
#ifdef PC_SIM
        insert_pt_tmr = TmrIntEnable(10, LcdDrawBlinkInsertPt);
#endif
#ifdef PR31700
        insert_pt_tmr = TmrIntEnable(300, LcdDrawBlinkInsertPt);
		//        insert_pt_tmr = TmrIntEnable(300, LcdMsgInsertPt);
#endif
	}
}

void LcdGetInsertPt(BOOLEAN *enable, SHORT *xcoord, SHORT *ycoord, BYTE *font)
{
    *enable = insert_pt_enable;
    *xcoord = insert_pt_xcoord;
    *ycoord = insert_pt_ycoord;
    *font = insert_pt_font;
}

BOOLEAN LcdCheckInsertPt(ObjectBounds bounds)
{
	if (insert_pt_enable == FALSE)
		return FALSE;
	if (insert_pt_xcoord + SPACE_CHAR >= bounds.xcoord
		&& insert_pt_xcoord <= bounds.xcoord + bounds.width - 1
		&& insert_pt_ycoord + SPACE_LINE + FONT_HEIGHT[insert_pt_font] - 1 >= bounds.ycoord
		&& insert_pt_ycoord <= bounds.ycoord + bounds.height - 1)
		return TRUE;
	return FALSE;
}

void LcdMsgInsertPt()
{
	MsgAppendMsgInt(LcdDrawBlinkInsertPt, 0, 0, 0, NULL);
}

void LcdDrawBlinkInsertPt()
{
	if (insert_pt_on == TRUE)
		insert_pt_on = FALSE;
	else if (insert_pt_on == FALSE)
		insert_pt_on = TRUE;
	LcdDrawInsertPt(insert_pt_xcoord, insert_pt_ycoord);
}

void LcdDrawInsertPt(SHORT xcoord, SHORT ycoord)
{
	ObjectBounds bounds;
	bounds.xcoord = xcoord;
	bounds.ycoord = ycoord;
	bounds.width = SPACE_CHAR + 1;
	bounds.height = SPACE_LINE + FONT_HEIGHT[insert_pt_font];
	//	LcdInvertBox(&bounds);
	if (insert_pt_on == TRUE)
		LcdDrawBox(&bounds, color_level[COLOR_BLACK_COLOR], color_level[COLOR_WHITE_COLOR], FILL_SOLID);
	else
    {
        if (insert_pt_off_bmp.bitmap_data != NULL)
            LcdDrawBitmap(&insert_pt_off_bmp, COLOR_NO_INVERT);
    }
}

void LcdTextboxDrawString(Textbox *tb_ptr)
{
	SHORT xcoord1, ycoord1, width, height, xcoord2, word_width=0, length=0;
	WORD highlight_start, highlight_length;
	ObjectBounds bounds1;
	BYTE *char_string, *string = tb_ptr->textbox_string;
	//    char_string = (BYTE *)qmalloc(1000*sizeof(BYTE));
    BYTE char_buf[160];
    char_string = char_buf;
	
    if (tb_ptr->textbox_highlight_length == 0 ||
        !(tb_ptr->textbox_attr.textbox_highlight))
	{
		highlight_start = 0;
		highlight_length = 0;
	}
	else if (tb_ptr->textbox_highlight_length > 0)
	{
		highlight_start = tb_ptr->textbox_highlight_start_char;
		highlight_length = tb_ptr->textbox_highlight_length;
	}
	else
	{
		highlight_start = tb_ptr->textbox_highlight_start_char
			+ tb_ptr->textbox_highlight_length;
		highlight_length = -tb_ptr->textbox_highlight_length;
	}
	if (tb_ptr->identification.table_related == 0xFFFF)
	{
		xcoord1 = tb_ptr->bounds.xcoord + (SHORT)(tb_ptr->textbox_style);
		ycoord1 = tb_ptr->bounds.ycoord + (SHORT)(tb_ptr->textbox_style);
		width = tb_ptr->bounds.width - (SHORT)(tb_ptr->textbox_style)*2;
		height = tb_ptr->bounds.height - (SHORT)(tb_ptr->textbox_style)*2;
	}
	else
	{
		xcoord1 = tb_ptr->screen_bounds.xcoord + (SHORT)(tb_ptr->textbox_style);
		ycoord1 = tb_ptr->screen_bounds.ycoord + (SHORT)(tb_ptr->textbox_style);
		width = tb_ptr->screen_bounds.width - (SHORT)(tb_ptr->textbox_style)*2;
		height = tb_ptr->screen_bounds.height - (SHORT)(tb_ptr->textbox_style)*2;
	}
	if (tb_ptr->textbox_back_line == 1 || (tb_ptr->textbox_back_line == 2 && color_mode == BLACK_AND_WHITE_MODE))
		LcdDrawHoriLine(xcoord1, xcoord1+width-1, ycoord1+FONT_HEIGHT[tb_ptr->textbox_font_id], DOTTED_LINE, color_level[COLOR_BLACK_COLOR], color_level[COLOR_WHITE_COLOR]);
	else if (tb_ptr->textbox_back_line == 2)
		LcdDrawHoriLine(xcoord1, xcoord1+width-1, ycoord1+FONT_HEIGHT[tb_ptr->textbox_font_id], NON_DOTTED_LINE, color_level[COLOR_GREY1_COLOR], color_level[COLOR_WHITE_COLOR]);
   	bounds1.width = LCD_WIDTH;	bounds1.height = LCD_HEIGHT;
   	xcoord2 = xcoord1;
   	if (highlight_start >= tb_ptr->textbox_left_char_pos + tb_ptr->textbox_num_chars_displayed
   	    || highlight_start + highlight_length <= tb_ptr->textbox_left_char_pos)
   	{
       	StrExtract(string, tb_ptr->textbox_left_char_pos, tb_ptr->textbox_num_chars_displayed, char_string);
       	bounds1.xcoord = xcoord2;	bounds1.ycoord = ycoord1;
		//     	bounds1.width = LCD_WIDTH;	bounds1.height = LCD_HEIGHT;
		LcdDrawFixedString(&bounds1, char_string, tb_ptr->textbox_font_color, tb_ptr->textbox_background_color, tb_ptr->textbox_font_id, NO_DOTDOT,MARGIN_0);
	}
	else
	{
		// Change
		//       	if (highlight_start >= tb_ptr->textbox_left_char_pos)
       	if (highlight_start > tb_ptr->textbox_left_char_pos)
        {
           	StrExtract(string, tb_ptr->textbox_left_char_pos, highlight_start - tb_ptr->textbox_left_char_pos, char_string);
           	bounds1.xcoord = xcoord2;	bounds1.ycoord = ycoord1;
			//         	bounds1.width = LCD_WIDTH;	bounds1.height = LCD_HEIGHT;
			LcdDrawFixedString(&bounds1, char_string, tb_ptr->textbox_font_color, tb_ptr->textbox_background_color, tb_ptr->textbox_font_id, NO_DOTDOT,MARGIN_0);
			xcoord2 += StrGetWidth(char_string, tb_ptr->textbox_font_id) - SPACE_CHAR;
			// Change
			//			if (highlight_start + highlight_length
			//			    <= tb_ptr->textbox_left_char_pos + tb_ptr->textbox_num_chars_displayed)
			if (highlight_start + highlight_length
				< tb_ptr->textbox_left_char_pos + tb_ptr->textbox_num_chars_displayed)
			{
               	StrExtract(string, highlight_start, highlight_length, char_string);
               	bounds1.xcoord = xcoord2;	bounds1.ycoord = ycoord1;
               	LcdDrawFixedString(&bounds1, char_string, ColorInvert(tb_ptr->textbox_font_color), tb_ptr->textbox_font_color, tb_ptr->textbox_font_id, NO_DOTDOT,MARGIN_0);
               	xcoord2 += StrGetWidth(char_string, tb_ptr->textbox_font_id) - SPACE_CHAR;
               	StrExtract(string, highlight_start + highlight_length,
					tb_ptr->textbox_left_char_pos + tb_ptr->textbox_num_chars_displayed
					- highlight_start - highlight_length, char_string);
               	bounds1.xcoord = xcoord2;	bounds1.ycoord = ycoord1;
				LcdDrawFixedString(&bounds1, char_string, tb_ptr->textbox_font_color, tb_ptr->textbox_background_color, tb_ptr->textbox_font_id, NO_DOTDOT,MARGIN_0);
				LcdDrawCharSpace(xcoord2, ycoord1, tb_ptr->textbox_font_color, tb_ptr->textbox_font_id);
			}
			else
			{
               	StrExtract(string, highlight_start, tb_ptr->textbox_left_char_pos
					+ tb_ptr->textbox_num_chars_displayed - highlight_start, char_string);
               	bounds1.xcoord = xcoord2;	bounds1.ycoord = ycoord1;
               	LcdDrawFixedString(&bounds1, char_string, ColorInvert(tb_ptr->textbox_font_color), tb_ptr->textbox_font_color, tb_ptr->textbox_font_id, NO_DOTDOT,MARGIN_0);
			}
		}
		else
		{
			if (highlight_start + highlight_length
				< tb_ptr->textbox_left_char_pos + tb_ptr->textbox_num_chars_displayed)
			{
				StrExtract(string, tb_ptr->textbox_left_char_pos, highlight_start
					+ highlight_length - tb_ptr->textbox_left_char_pos, char_string);
				bounds1.xcoord = xcoord2;	bounds1.ycoord = ycoord1;
               	LcdDrawFixedString(&bounds1, char_string, ColorInvert(tb_ptr->textbox_font_color), tb_ptr->textbox_font_color, tb_ptr->textbox_font_id, NO_DOTDOT,MARGIN_0);
				xcoord2 += StrGetWidth(char_string, tb_ptr->textbox_font_id) - SPACE_CHAR;
               	StrExtract(string, highlight_start + highlight_length,
					tb_ptr->textbox_left_char_pos + tb_ptr->textbox_num_chars_displayed
					- highlight_start - highlight_length, char_string);
               	bounds1.xcoord = xcoord2;	bounds1.ycoord = ycoord1;
				LcdDrawFixedString(&bounds1, char_string, tb_ptr->textbox_font_color, tb_ptr->textbox_background_color, tb_ptr->textbox_font_id, NO_DOTDOT,MARGIN_0);
				LcdDrawCharSpace(xcoord2, ycoord1, tb_ptr->textbox_font_color, tb_ptr->textbox_font_id);
			}
			else
			{
				StrExtract(string, tb_ptr->textbox_left_char_pos, tb_ptr->textbox_num_chars_displayed,
					char_string);
               	bounds1.xcoord = xcoord2;	bounds1.ycoord = ycoord1;
               	LcdDrawFixedString(&bounds1, char_string, ColorInvert(tb_ptr->textbox_font_color), tb_ptr->textbox_font_color, tb_ptr->textbox_font_id, NO_DOTDOT,MARGIN_0);
			}
		}
	}
}

void LcdSetOrientationMsg(MsgType *msg)
{
	EvtAppendEvt(EVT_KEYBOARD_STATUS, 0, 0, 0, NULL);
}


void LcdSetOrientation(BYTE rot)
{
	UWORD buffer[LCD_SIZE];
    UWORD i;
	BitmapTemplate bmp;
	
    i=LCD_SIZE-1;
	do {
		buffer[i] = WordRead((i%20)*8, i/20);
	} while (i--);
	
	bmp.xcoord = 0;
	bmp.ycoord = 0;
	bmp.width = 160;
	bmp.height = 160;
    bmp.bitmap_data = (UWORD *) buffer;
	bmp.size = LCD_SIZE * 4;
	bmp.quantisation = Q_FOUR_BIT;
	
    pen_xy_rotate = rot;
	rotation = rot;
	LcdDrawBitmap(&bmp, COLOR_NO_INVERT);
	
    if (rotation == 1 && keyboard_status)
    {        
        keyboard_status = FALSE;
        keyboard.keyboard_attr.keyboard_drawn = FALSE;
        KeyboardRestoreBitBehind();
        stored_bitmap_index = -1;
        MsgAppendMsgInt(LcdSetOrientationMsg,0,0,0,0);
    }
}


void LcdRot(BYTE rot)
{
    pen_xy_rotate = rot;
	rotation = rot;
}

void LcdShowExceptionCode(UWORD addr, UWORD code)
{
	ObjectBounds bounds;
	BYTE string[50];
	EvtType event;
	TmrWaitTime(1000);
	bounds.xcoord = 10;
	bounds.ycoord = 50;
	bounds.width = 140;
	bounds.height = 45;
	LcdDrawBox(&bounds, color_level[COLOR_BLACK_COLOR], color_level[COLOR_WHITE_COLOR], FILL_EMPTY);
	bounds.xcoord = 12;
	bounds.ycoord = 52;
	bounds.width = 136;
	bounds.height = 41;
	LcdDrawBox(&bounds, color_level[COLOR_BLACK_COLOR], color_level[COLOR_WHITE_COLOR], FILL_EMPTY);
	bounds.xcoord = 20;
	bounds.ycoord = 60;
	bounds.width = 125;
	bounds.height = 30;
	sprintf((char *)string, SYSTR10, addr);
	LcdDrawFixedString(&bounds, string, color_level[COLOR_BLACK_COLOR], color_level[COLOR_WHITE_COLOR], SMALL_FONT, NO_DOTDOT, 0);
	bounds.xcoord = 20;
	bounds.ycoord = 75;
	bounds.width = 125;
	bounds.height = 30;
	sprintf((char *)string, SYSTR11, code);
	LcdDrawFixedString(&bounds, string, color_level[COLOR_BLACK_COLOR], color_level[COLOR_WHITE_COLOR], SMALL_FONT, NO_DOTDOT, 0);
	while (1)
	{
		EvtGetEvent(&event);
	}
}

BYTE LcdGetOrientation()
{
	return rotation;
}

