/*  Copyright (C) 2001-2002  Kenichi Suto
 *
 *  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/


#include "defs.h"
#include "global.h"
#include "render.h"
#include "eb.h"

static gint click_event (GtkWidget *widget, GdkEventButton *event);
static gint expose_event (GtkWidget *widget, GdkEventExpose *event);
static gint release_event (GtkWidget *widget, GdkEventButton *event);
static gint configure_event(GtkWidget *widget, GdkEventConfigure *event);
static gint motion_event(GtkWidget *widget, GdkEventMotion *event);

void clear_link(CONTENT_AREA *content_area);
void clear_location(CONTENT_AREA *content_area);
void clear_history(CONTENT_AREA *content_area);
void save_history(CONTENT_AREA *content_area, BOOK_INFO *binfo, gint page, gint offset);
void dump_hex();


extern GdkAtom clipboard_atom;
extern gchar *clipboard;

#ifdef HAVE_ICONV_H
#include <iconv.h>
#endif

#define CHAR_KIND_ALPHA   0
#define CHAR_KIND_GAIJI   3
#define CHAR_KIND_SPACE   4
#define CHAR_KIND_DIGIT   5
#define CHAR_KIND_ASCII   6
#define CHAR_KIND_ZENKAKU_KIGOU    11
#define CHAR_KIND_ZENKAKU_DIGIT    12
#define CHAR_KIND_ZENKAKU_ALPHA    13
#define CHAR_KIND_ZENKAKU_HIRAGANA 14
#define CHAR_KIND_ZENKAKU_KATAKANA 15
#define CHAR_KIND_ZENKAKU_GREEK    16
#define CHAR_KIND_ZENKAKU_RUSIA    17
#define CHAR_KIND_ZENKAKU_KEISEN   18
#define CHAR_KIND_ZENKAKU_KANJI    19
#define CHAR_KIND_OTHER   99


gint char_kind(LOCATION *loc)
{
	GdkWChar wc_buff[2];
	char *p;
	
#ifdef HAVE_ICONV_H
	iconv_t cd;
	const char* icode = "euc-jp";
	const char* ocode = "iso-2022-jp";
	//	const char* ocode = "shift_jis";
	
	int r = 0;
	size_t isize;
	size_t osize;
	char *outbuf;
	char *result;
	short kchar;
	char *lang;
#endif
	g_assert(loc != NULL);
	
	if(loc->gaiji_no[0] != '\0') {
		return(CHAR_KIND_GAIJI);
	}
	
	wc_buff[0] = loc->wc;
	wc_buff[1] = 0x00000000;
	
	p = gdk_wcstombs(wc_buff);
	
	if(isalpha(*p)) {
		return(CHAR_KIND_ALPHA);
	}
	
	if(isdigit(*p)) {
		return(CHAR_KIND_DIGIT);
	}
	
	if(isspace(*p)) {
		return(CHAR_KIND_SPACE);
	}
	
	if(isascii(*p)) {
		return(CHAR_KIND_ASCII);
	}
	
	
#ifdef HAVE_ICONV_H
	
	// $B4A;z$H$R$i$,$J$J$I$r6hJL$9$k!#(B
	// $B$$$C$?$s(BJIS$B%3!<%I$KJQ49$7$F$+$iHO0OHf3S$9$k!#(B
	lang = getenv("LANG");
	if(lang == NULL){
		lang = getenv("LC_ALL");
		if(lang == NULL){
			fprintf(stderr, "Failed to get current locale.\n");
			return(CHAR_KIND_OTHER);		
		}
	}
	if(strcmp(lang, "ja_JP.eucJP") == 0){
		icode = "euc-jp";
	} else if(strcmp(lang, "ja_JP.SJIS") == 0){
		icode = "shift_jis";
	} else {
		return(CHAR_KIND_OTHER);		
	}
	
	cd = iconv_open( ocode, icode );
	if( (int)cd == -1 )
		return(CHAR_KIND_OTHER);		
	
	isize = strlen(p) + 1;
	osize = isize*2;
	
	result = outbuf = (char*)malloc(osize);
	r = iconv(cd, (char **)&p, &isize, &outbuf, &osize);
	
	kchar = result[3];
	kchar = kchar << 8;
	kchar |= result[4];
	
	if(kchar == 0x213c){
		return(CHAR_KIND_ZENKAKU_KATAKANA);
	}
	
	if((0x2100 <= kchar) && (kchar <= 0x21ff)){
		//$B5-9f(B
		return(CHAR_KIND_ZENKAKU_KIGOU);
	}
	if((0x2200 <= kchar) && (kchar <= 0x22ff)){
		//$B5-9f(B
		return(CHAR_KIND_ZENKAKU_KIGOU);
	}
	if((0x2330 <= kchar) && (kchar <= 0x2339)){
		//$B?t;z(B
		return(CHAR_KIND_ZENKAKU_DIGIT);
	}
	if((0x2340 <= kchar) && (kchar <= 0x23ff)){
		//$B%"%k%U%!%Y%C%H(B
		return(CHAR_KIND_ZENKAKU_ALPHA);
	}
	if((0x2400 <= kchar) && (kchar <= 0x24ff)){
		//$B$R$i$,$J(B
		return(CHAR_KIND_ZENKAKU_HIRAGANA);
	}
	if((0x2500 <= kchar) && (kchar <= 0x25ff)){
		//$B%+%?%+%J(B
		return(CHAR_KIND_ZENKAKU_KATAKANA);
	}
	if((0x2600 <= kchar) && (kchar <= 0x26ff)){
		//$B%.%j%7%cJ8;z(B
		return(CHAR_KIND_ZENKAKU_GREEK);
	}
	if((0x2700 <= kchar) && (kchar <= 0x27ff)){
		//$B%m%7%"J8;z(B?
		return(CHAR_KIND_ZENKAKU_RUSIA);
	}
	if((0x2800 <= kchar) && (kchar <= 0x28ff)){
		//$B7S@~(B
		return(CHAR_KIND_ZENKAKU_KEISEN);
	}
	if((0x3000 <= kchar) && (kchar <= 0x74ff)){
		//$B4A;z(B
		return(CHAR_KIND_ZENKAKU_KANJI);
	}
	
	iconv_close(cd);
	free(result);
#endif
	
	return(CHAR_KIND_OTHER);
}


/*
  1$BJ8;z$rH?E>$5$;$?$jLa$7$?$j$9$k(B
  $BDL>o$NJ8;z$G$b30;z$G$bNI$$!#(B
  reverse = TRUE $B$J$iH?E>!"(BFALSE$B$J$iLa$9!#(B
*/
static void reverse_selection(CONTENT_AREA *content_area, LOCATION *loc, gboolean reverse)
{
	GdkGC *gc=NULL;
	char *p;
	GdkWChar wc_buff[2];
	GdkPixmap *gaiji_pixmap = NULL;
	GdkBitmap *mask;
	gint width, height;
	
	wc_buff[0] = loc->wc;
	wc_buff[1] = 0x00000000;
	
	p = gdk_wcstombs(wc_buff);
	
	g_assert(loc != NULL);

	gc = gdk_gc_new(content_area->area->window);
	
	if(reverse == TRUE){
		gdk_gc_set_foreground(gc, &content_area->area->style->bg[GTK_STATE_SELECTED]);
		gdk_gc_set_background(gc, &content_area->area->style->bg[GTK_STATE_SELECTED]);
	} else {
		gdk_gc_set_foreground(gc, &content_area->area->style->bg[GTK_STATE_NORMAL]);
		gdk_gc_set_background(gc, &content_area->area->style->bg[GTK_STATE_NORMAL]);
	}
	gdk_draw_rectangle( content_area->canvas->pixmap, 
			    gc,
			    TRUE, loc->x,loc->y,
			    loc->width, loc->height);
	
	if(reverse == TRUE){
		gdk_gc_set_foreground(gc, &content_area->area->style->fg[GTK_STATE_SELECTED]);
		gdk_gc_set_background(gc, &content_area->area->style->bg[GTK_STATE_SELECTED]);
	} else {
		if(loc->link_type == LINK_TYPE_JUMP){
			gdk_gc_set_foreground(gc, &colors[COLOR_BLUE]);
			
		} else if(loc->link_type == LINK_TYPE_WAVE){
			gdk_gc_set_foreground(gc, &colors[COLOR_GREEN]);
			
		} else if(loc->link_type == LINK_TYPE_MPEG){
			gdk_gc_set_foreground(gc, &colors[COLOR_GREEN]);
			
		} else if(loc->link_type == LINK_TYPE_KEYWORD){
			gdk_gc_set_foreground(gc, &colors[COLOR_BROWN]);
			
		} else {
			gdk_gc_set_foreground(gc, &content_area->area->style->fg[GTK_STATE_NORMAL]);
		}
		gdk_gc_set_background(gc, &content_area->area->style->bg[GTK_STATE_NORMAL]);
	}
	
	if(loc->type == LOCATION_CHAR){
		if(loc->link_type == LINK_TYPE_SUPERSCRIPT){
			gdk_draw_text_wc(content_area->canvas->pixmap, 
					 loc->font, 
					 gc,
					 loc->x, 
					 loc->y+font_ascent-6, 
					 &(loc->wc), 1);
		} else {
			gdk_draw_text_wc(content_area->canvas->pixmap, 
					 loc->font, 
					 gc,
					 loc->x, 
					 loc->y+font_ascent, 
					 &(loc->wc), 1);
		}
	} else if(loc->type == LOCATION_GAIJI){
		load_xbm(current_book_info, loc->gaiji_no, gc, &gaiji_pixmap, &mask, &width, &height);
		gdk_draw_pixmap(content_area->canvas->pixmap,
				gc,
				gaiji_pixmap,
				0,0,
				loc->x,loc->y,
				loc->width,loc->height);
		if(gaiji_pixmap != NULL)
			gdk_pixmap_unref(gaiji_pixmap);
		if(mask != NULL)
			gdk_bitmap_unref(mask);
	} else {
		fprintf(stderr, "Internal error : unknown location type\n");
		exit(1);
	}
	
	gdk_draw_pixmap(content_area->area->window,
			content_area->area->style->fg_gc[GTK_WIDGET_STATE(content_area->area)],
			content_area->canvas->pixmap,
			loc->x, loc->y,
			loc->x, loc->y,
			loc->width,
			loc->height);
	
	loc->reverse = reverse;

}

/*
  $BC18l$rA*Br$9$k(B
  $BJ8;z<o$,F1$8$b$N$rC18l$H$7$FH=CG$9$k(B
*/
void select_word(CONTENT_AREA *content_area, GList *center)
{
	LOCATION *loc;
	GList *loc_item;
	gint kind;
	
	g_assert(center != NULL);
	g_assert(content_area != NULL);
	
	content_area->selection_start = content_area->selection_end = center;
	kind = char_kind((LOCATION *)(center->data));
	
	loc_item = center;
	while(loc_item){
		loc = (LOCATION *)(loc_item->data);
		if(kind == char_kind(loc))
			content_area->selection_start = loc_item;
		else 
			break;
		loc_item = g_list_previous(loc_item);
	}
	loc_item = center;
	while(loc_item){
		loc = (LOCATION *)(loc_item->data);
		if(kind == char_kind(loc))
			content_area->selection_end = loc_item;
		else 
			break;
		loc_item = g_list_next(loc_item);
	}
	
	content_area->direction = DIRECTION_FORWARD;
	
	loc_item = content_area->selection_start;
	while(loc_item){
		loc = (LOCATION *)(loc_item->data);
		reverse_selection(content_area, loc, TRUE);
		if(loc_item == content_area->selection_end)
			break;
		loc_item = g_list_next(loc_item);
	}
	
	if(!bauto_lookup)
		gtk_selection_owner_set(content_area->area, GDK_SELECTION_PRIMARY,GDK_CURRENT_TIME);
}

static gint configure_event(GtkWidget *widget, GdkEventConfigure *event)
{
/*
  gchar *text=NULL;
  CONTENT_AREA *content_area;
  
  content_area = gtk_object_get_user_data(GTK_OBJECT(widget));
  
  if((widget->parent->allocation.width == content_area->old_width) &&
  (widget->parent->allocation.height == content_area->old_height)) { 
  return TRUE;
  }
  
  
  content_area->old_width = widget->parent->allocation.width;
  content_area->old_height = widget->parent->allocation.height;
  
  if(current_position.page >= 0){
  text = ebook_get_text(current_book_info,
  current_position.page,
  current_position.offset);
  show_text(current_book_info, text);
  free(text);
  }
*/
	return FALSE;
}


static gint expose_event (GtkWidget *widget, GdkEventExpose *event)
{
	
	CONTENT_AREA *content_area;
	
	content_area = gtk_object_get_user_data(GTK_OBJECT(widget));
	
	gdk_draw_pixmap(content_area->area->window,
			content_area->area->style->fg_gc[GTK_WIDGET_STATE(content_area->area)],
			content_area->canvas->pixmap,
			0, 0,
			0, 0,
			content_area->area->allocation.width,
			content_area->area->allocation.height);
	
	content_area->old_width = widget->parent->allocation.width;
	content_area->old_height = widget->parent->allocation.height;
	
	
/*
  
if((widget->parent->allocation.width == content_area->old_width) && 
(widget->parent->allocation.height == content_area->old_height)) {
draw_from_buffer(content_area);
} else {
draw_from_buffer(content_area);

content_area->old_width = widget->parent->allocation.width;
content_area->old_height = widget->parent->allocation.height;
}
*/
	return FALSE;
}

static gint click_event (GtkWidget *widget, GdkEventButton *event)
{
	char *text;
	gchar filename[512];
	GList *link_item;
	LINK *link;
	GList *loc_item;
	LOCATION *loc;
	
	CONTENT_AREA *content_area;
	
	content_area = gtk_object_get_user_data(GTK_OBJECT(widget));
	
	if(event->type == GDK_BUTTON_PRESS){
		if ((event->button == 2) || (event->button == 3)){
			return(FALSE);
		}
		
		// $B%j%s%/$,$"$k$+$r%A%'%C%/(B
		link_item = g_list_first(content_area->link);
		while(link_item) {
			link = (LINK *)(link_item->data);
			if(((int)event->x >= link->start_x) && 
			   ((int)event->x <= link->end_x) && 
			   ((int)event->y >= link->start_y) && 
			   ((int)event->y <= link->end_y)){
				// $BB>$X$N%8%c%s%W(B
				if(link->type & LINK_TYPE_JUMP){
					text = ebook_get_text(
						current_book_info,
						link->page, 
						link->offset);
					save_history(content_area,
						     current_book_info,
						     link->page, link->offset);
					current_position.page = link->page;
					current_position.offset = link->offset;
/*
  if(bauto_lookup && bshow_popup){
  show_popup(current_book_info, text, TRUE);
  }
  else
  show_text(current_book_info, text);
*/
//					content_area->show_func(((SEARCH_RESULT *)(search_result->data))->book_info, text);
					content_area->show_func(current_book_info, text);
					
					if(hex_dlg != NULL)
						dump_hex();
					if(text_dlg != NULL)
						dump_text();
					//$B$3$3$+$i2<$O(Bl$B$NCM$,=q$-49$o$C$F$$$k$N$G(B
					// $B;2>H$7$F$O$$$1$J$$(B
					free(text);
					gtk_signal_emit_stop_by_name(GTK_OBJECT(widget), "button_press_event");
					return(TRUE);
					// $B2;@<$X$N%j%s%/(B
				} else if (link->type & LINK_TYPE_WAVE){
					sprintf(filename, "%s/ebview-%d.wav", tmp_dir, getpid());
					ebook_output_wave(current_book_info, 
							  filename, 
							  link->page,
							  link->offset,
							  link->size);
					play_external(filename, LINK_TYPE_WAVE);
//					unlink(filename);
					gtk_signal_emit_stop_by_name(GTK_OBJECT(widget), "button_press_event");
					
					return(TRUE);
					// $BF02h$X$N%j%s%/(B
				} else if (link->type & LINK_TYPE_MPEG){
					sprintf(filename, "%s/ebview-%d.mpg", tmp_dir, getpid());
					ebook_output_mpeg(current_book_info, 
							  link->filename,
							  filename);
					play_external(filename, LINK_TYPE_MPEG);
//					unlink(filename);
					
					gtk_signal_emit_stop_by_name(GTK_OBJECT(widget), "button_press_event");
					return(TRUE);
				}
			}
			link_item = g_list_next(link_item);
		}
		
		// $B%j%s%/$G$O$J$$>l9g(B
		
		// $BH?E>$7$F$$$k$b$N$,$"$C$?$i85$KLa$9(B
		loc_item = content_area->location;
		while(loc_item){
			loc = (LOCATION *)(loc_item->data);
			if(loc->reverse == TRUE){

				reverse_selection(content_area, loc, FALSE);

				// $B%$%?%j%C%/$N$H$-$KJ8;z$,7g$1$k$N$rKI$0$?$a!"(B
				// 1$B$DA0$NJ8;z$r:FIA2h$9$k!#(B

				if (g_list_previous(loc_item))
					reverse_selection(content_area,
							  (LOCATION *)((g_list_previous(loc_item))->data),
							  FALSE);


				loc->reverse = FALSE;
			}
			loc_item = g_list_next(loc_item);
		}
		
		
		loc_item = g_list_first(content_area->location);
		while(loc_item){
			loc = (LOCATION *)(loc_item->data);
			if(((int)event->x >= loc->x) && 
			   ((int)event->x <= loc->x+loc->width) && 
			   ((int)event->y >= loc->y) && 
			   ((int)event->y <= loc->y+loc->height)){
				content_area->button_x = event->x;
				content_area->button_y = event->y;
				content_area->selection_start = loc_item;
				content_area->selection_end = loc_item;
				if(!bauto_lookup)
					gtk_selection_owner_set(content_area->area, GDK_SELECTION_PRIMARY,GDK_CURRENT_TIME);
				content_area->bbutton_down = TRUE;
				return(FALSE);
			}
			loc_item = g_list_next(loc_item);
			
		}
	} else if((event->button == 1) && (event->type == GDK_2BUTTON_PRESS)){
		loc_item = g_list_first(content_area->location);
		while(loc_item){
			loc = (LOCATION *)(loc_item->data);
			if(((int)event->x >= loc->x) && 
			   ((int)event->x <= loc->x+loc->width) && 
			   ((int)event->y >= loc->y) && 
			   ((int)event->y <= loc->y+loc->height)){
				select_word(content_area, loc_item);
			}
			loc_item = g_list_next(loc_item);
			
		}
	}
	
	return(FALSE);
}

static gint release_event (GtkWidget *widget, GdkEventButton *event)
{
	CONTENT_AREA *content_area;
	
	content_area = gtk_object_get_user_data(GTK_OBJECT(widget));
	
	content_area->bbutton_down = FALSE;
	
	return FALSE;
}

gboolean in_the_link = FALSE;


static gint motion_event(GtkWidget *widget, GdkEventMotion *event)
{
	LINK *link;
	GdkCursor *cursor;
	LOCATION *loc;
	LOCATION *loc_end;
	gboolean line_skip;
	GList *save_start, *save_end;
	gint save_direction;
	gboolean exist;
	GList *loc_item;
	GList *loc_item2;
	GList *link_item;
	
	CONTENT_AREA *content_area;
	
	
	content_area = gtk_object_get_user_data(GTK_OBJECT(widget));
	
	if(content_area->bbutton_down == TRUE){
		
		if(content_area->selection_start == NULL){
			return(FALSE);
		}
		
		if(content_area->selection_end == NULL){
			return(FALSE);
		}
		
		save_start = content_area->selection_start;
		save_end = content_area->selection_end;
		save_direction = content_area->direction;
		
		if(content_area->selection_start == content_area->selection_end){
			loc = (LOCATION *)(content_area->selection_start->data);
			if(loc->reverse == FALSE)
				reverse_selection(content_area, (LOCATION *)(content_area->selection_start->data), TRUE);
		}
		
		
		// $B%\!<%@!<NN0h$@$C$?$iJd@5(B
		if(event->x < h_border)
			event->x = h_border;
		if(event->y < v_space)
			event->y = v_space;			
		
		//$BF1$8J8;z$NNN0hFb$@$C$?$i$J$K$b$7$J$$!#(B
		loc_end = (LOCATION *)(content_area->selection_end->data);
		if((loc_end != NULL) &&
		   ((int)event->x >= loc_end->x) && 
		   ((int)event->x <= loc_end->x+loc_end->width) && 
		   ((int)event->y >= loc_end->y) && 
		   ((int)event->y <= loc_end->y+loc_end->height))
		{
			return(FALSE);
		}
		
		content_area->direction = DIRECTION_FORWARD;
		line_skip = FALSE;
		
		// $B<!$N9T$K$$$C$?$+(B
		if(content_area->button_y < event->y){
			loc_item = g_list_first(content_area->location);
			while(loc_item){
				loc = (LOCATION *)(loc_item->data);
				if(((LOCATION *)(content_area->selection_start->data))->y < loc->y){
					// $B<!$N9T$KF~$C$?$+(B
					if(event->y >= loc->y){
						line_skip=TRUE;
						content_area->direction = DIRECTION_FORWARD;
					}
					break;
				}
				loc_item = g_list_next(loc_item);
			}
		}
		
		// $BA0$N9T$+(B
		if(content_area->button_y > event->y){
			if(event->y < ((LOCATION *)(content_area->selection_start->data))->y){
				line_skip=TRUE;
				content_area->direction = DIRECTION_BACKWARD;
			}
		}
		
		// $BF1$89T$N>l9g(B
		if(line_skip == FALSE){
			// $B1&$K?J$s$G$$$k$+(B
			if(((LOCATION *)(content_area->selection_start->data))->x < event->x){
				content_area->direction = DIRECTION_FORWARD;
				// $B$=$l$H$b:8$+(B
			} else if(((LOCATION *)(content_area->selection_start->data))->x > event->x) {
				content_area->direction = DIRECTION_BACKWARD;
			} else {
				return(FALSE);
			}
		}
		
		// end$B$r7h$a$k(B
		// direction$B$O$I$A$i$G$"$C$F$b(Bend$B$,=*E@$K$J$k(B
		loc_item = content_area->selection_start;
		while(loc_item){
			loc = (LOCATION *)(loc_item->data);
			if(((int)event->x >= loc->x) && 
			   ((int)event->x <= loc->x+loc->width) && 
			   ((int)event->y >= loc->y) && 
			   ((int)event->y <= loc->y+loc->height)){
				content_area->selection_end = loc_item;
				break;
			}
			if(content_area->direction == DIRECTION_FORWARD)
				loc_item = g_list_next(loc_item);
			else 
				loc_item = g_list_previous(loc_item);
		}
		
		if(loc_item == NULL){
			content_area->selection_start = save_start;
			content_area->selection_end = save_end;
			content_area->direction = save_direction;
			return(FALSE);
		}
		
		// $B0JA0$OA*Br$5$l$F$$$?$,:#2s$OA*Br$5$l$F$$$J$$$b$N$OLa$9(B
		loc_item = save_start;
		while(loc_item){
			loc = (LOCATION *)(loc_item->data);
			exist = FALSE;
			loc_item2 = content_area->selection_start;
			while(loc_item2){
				// $BA0$bA*Br$5$l$F$$$?(B
				if(loc_item == loc_item2) {
					exist = TRUE;
					break;
				}
				if(loc_item2 == content_area->selection_end)
					break;
				if(content_area->direction == DIRECTION_FORWARD)
					loc_item2 = g_list_next(loc_item2);
				else 
					loc_item2 = g_list_previous(loc_item2);
			}
			
			if(exist == FALSE) {
				if(loc->reverse == TRUE) {
					reverse_selection(content_area, loc, FALSE);
					// $B%$%?%j%C%/$N$H$-$KJ8;z$,7g$1$k$N$rKI$0$?$a!"(B
					// 1$B$DA0$NJ8;z$r:FIA2h$9$k!#(B

					if(g_list_previous(loc_item))
						reverse_selection(content_area,
								  (LOCATION *)((g_list_previous(loc_item))->data),
								  FALSE);
					
				}
			}
			
			if(loc_item == save_end)
				break;
			
			if(save_direction == DIRECTION_FORWARD)
				loc_item = g_list_next(loc_item);
			else 
				loc_item = g_list_previous(loc_item);
		}
		
		loc = (LOCATION *)(content_area->selection_start->data);
		if(loc->reverse == FALSE)
			reverse_selection(content_area, loc, TRUE);
		
		// $B:#2s$OA*Br$5$l$F$$$F0JA0$OA*Br$5$l$F$$$J$+$C$?$b$N$OH?E>(B
		loc_item = content_area->selection_start;
		while(loc_item){
			loc = (LOCATION *)(loc_item->data);
			exist = FALSE;
			loc_item2 = save_start;
			while(loc_item2){
				if(loc_item == loc_item2) {
					exist = TRUE;
					break;
				}
				if(loc_item2 == save_end)
					break;
				if(save_direction == DIRECTION_FORWARD)
					loc_item2 = g_list_next(loc_item2);
				else 
					loc_item2 = g_list_previous(loc_item2);
			}
			
			if(exist == FALSE)
				reverse_selection(content_area, loc, TRUE);
			
			if(loc_item == content_area->selection_end)
				break;
			
			if(content_area->direction == DIRECTION_FORWARD)
				loc_item = g_list_next(loc_item);
			else 
				loc_item = g_list_previous(loc_item);
		}
		
		
		if(!bauto_lookup)
			gtk_selection_owner_set(content_area->area, GDK_SELECTION_PRIMARY,GDK_CURRENT_TIME);
		
		return(FALSE);
	}
	
	link_item = content_area->link;
	while(link_item) {
		link = (LINK *)(link_item->data);
		if(((int)event->x >= link->start_x) && 
		   ((int)event->x <= link->end_x) && 
		   ((int)event->y >= link->start_y) && 
		   ((int)event->y <= link->end_y)){
			if(in_the_link == FALSE){
				gchar msg[512];
				cursor = gdk_cursor_new (CURSOR_LINK);
				gdk_window_set_cursor (content_area->area->window, cursor);
				gdk_cursor_destroy (cursor);
				in_the_link = TRUE;
				
				if(link->type & LINK_TYPE_JUMP) {
					sprintf(msg, "REFERENCE: page=%08x offset=%03x", 
						link->page, link->offset);
				} else if(link->type & LINK_TYPE_WAVE) {
					sprintf(msg, "WAVE: page=%08x offset=%03x size=%d", 
						link->page, link->offset, link->size);
				} else if(link->type & LINK_TYPE_MPEG) {
					sprintf(msg, "MPEG: filename=%s", 
						link->filename);
				}
				if(!bauto_lookup || !bshow_popup)
					status_message(msg);
			}
			return(FALSE);
		}
		link_item = g_list_next(link_item);
	}
	if(in_the_link == TRUE){
		cursor = gdk_cursor_new (CURSOR_NORMAL);
		gdk_window_set_cursor (content_area->area->window, cursor);
		gdk_cursor_destroy (cursor);
		in_the_link = FALSE;
		clear_status_message(NULL);
	}
	
	return(FALSE);
}

void
selection_get (GtkWidget *widget, 
	       GtkSelectionData *selection_data,
	       guint      info,
	       guint      time,
	       gpointer   data)
{
	guchar *buffer;
	gint len;
	GdkAtom type = GDK_NONE;
	gint format;
	GdkWChar wc_buff[65536];
	LOCATION *loc;
	GList *loc_item;
	
	guchar *ctext;
	CONTENT_AREA *content_area;
	
	content_area = gtk_object_get_user_data(GTK_OBJECT(widget));
	
	if(selection_data->selection == clipboard_atom){
		if(clipboard == NULL){
			buffer = NULL;
			len = 0;
		} else {
			buffer = clipboard;
			len = strlen(buffer);
		}
	} else {
		if (content_area->selection_start == NULL)
		{
			buffer = NULL;
			len = 0;
		}      
		else
		{
			len = 0;
			if(content_area->direction == DIRECTION_FORWARD){
				loc_item = content_area->selection_start;
				while(loc_item){
					loc = (LOCATION *)(loc_item->data);
					if(loc->type == LOCATION_CHAR){
						wc_buff[len] = loc->wc;
						len++;
						wc_buff[len] = 0x00000000;
					}
					if(loc_item == content_area->selection_end)
						break;
					loc_item = g_list_next(loc_item);
				}
			} else {
				loc_item = content_area->selection_end;
				while(loc_item){
					loc = (LOCATION *)(loc_item->data);
					if(loc->type == LOCATION_CHAR){
						wc_buff[len] = loc->wc;
						len++;
						wc_buff[len] = 0x00000000;
					}
					if(loc_item == content_area->selection_start)
						break;
					loc_item = g_list_previous(loc_item);
				}
			}
			buffer = gdk_wcstombs(wc_buff);
			len = strlen(buffer);
		}
	}
	
	switch (info)
	{
	case COMPOUND_TEXT:
		type = gdk_atom_intern ("COMPOUND_TEXT",FALSE);
		break;
	case TEXT:
		type = gdk_atom_intern ("COMPOUND_TEXT",FALSE);
		break;
	case STRING:
		type = gdk_atom_intern ("STRING",FALSE);
		break;
	default:
		fprintf(stderr, "unsupported data type requested\n");
	}
	
	gdk_string_to_compound_text(buffer, &type, &format, &ctext, &len);
	if(ctext == NULL) {
		fprintf(stderr, "gdk_string_to_compound_text failed\n");
	} else {
		gtk_selection_data_set (selection_data, type, 8, ctext, len);
		gdk_free_compound_text(ctext);
	}
}

gint selection_clear (GtkWidget *widget, GdkEventSelection *event)
{
	
	GList *loc_item;
	LOCATION *loc;
	
	CONTENT_AREA *content_area;
	
	content_area = gtk_object_get_user_data(GTK_OBJECT(widget));
	
	// $BH?E>$7$F$$$k$b$N$r85$KLa$9(B
	if((content_area->selection_start != NULL) && (content_area->selection_end != NULL)){
		if(content_area->direction == DIRECTION_FORWARD){
			loc_item = content_area->selection_start;
			while(loc_item){
				loc = (LOCATION *)(loc_item->data);
				if(loc->reverse == TRUE) {
					reverse_selection(content_area, loc, FALSE);
					// $B%$%?%j%C%/$N$H$-$KJ8;z$,7g$1$k$N$rKI$0$?$a!"(B
					// 1$B$DA0$NJ8;z$r:FIA2h$9$k!#(B

					if(g_list_previous(loc_item))
						reverse_selection(content_area,
								  (LOCATION *)((g_list_previous(loc_item))->data),
								  FALSE);

				}
				if(loc_item == content_area->selection_end)
					break;
				loc_item = g_list_next(loc_item);
			}
		} else {
			loc_item = content_area->selection_end;
			while(loc_item){
				loc = (LOCATION *)(loc_item->data);
				if(loc->reverse == TRUE) {
					reverse_selection(content_area, loc, FALSE);
					// $B%$%?%j%C%/$N$H$-$KJ8;z$,7g$1$k$N$rKI$0$?$a!"(B
					// 1$B$DA0$NJ8;z$r:FIA2h$9$k!#(B

					if(g_list_previous(loc_item))
						reverse_selection(content_area,
								  (LOCATION *)((g_list_previous(loc_item))->data),
								  FALSE);

				}
				if(loc_item == content_area->selection_start)
					break;
				loc_item = g_list_previous(loc_item);
			}
		}
	}
	
	
	content_area->selection_start = NULL;
	content_area->selection_end = NULL;
	return TRUE;
}

CONTENT_AREA *create_content_area(gint width, gint height)
{
	CONTENT_AREA *content_area;
	static GtkTargetEntry targetlist[] = {
		{ "STRING",        0, STRING },
		{ "TEXT",          0, TEXT },
		{ "COMPOUND_TEXT", 0, COMPOUND_TEXT }
	};
	static gint ntargets = sizeof(targetlist) / sizeof(targetlist[0]);
	
	
	content_area = (CONTENT_AREA *)calloc(sizeof(CONTENT_AREA) , 1);
	g_assert(content_area != NULL);
	
	content_area->area = gtk_drawing_area_new();
	g_assert(content_area->area != NULL);
	
	gtk_object_set_user_data(GTK_OBJECT(content_area->area), content_area);
	
	gtk_signal_connect( GTK_OBJECT (content_area->area), "expose_event",
			    (GtkSignalFunc)expose_event, (gpointer)content_area);
	
	gtk_signal_connect( GTK_OBJECT(content_area->area),"configure_event",
			    (GtkSignalFunc)configure_event, (gpointer)content_area);
	
//	gtk_signal_connect( GTK_OBJECT(canvas->area),"draw",
//			    (GtkSignalFunc)panel_configure_event, NULL);
	
	gtk_signal_connect( GTK_OBJECT(content_area->area),"button_press_event",
			    (GtkSignalFunc)click_event, (gpointer)content_area);
	gtk_signal_connect( GTK_OBJECT(content_area->area),"button_release_event",
			    (GtkSignalFunc)release_event, (gpointer)content_area);
	gtk_signal_connect( GTK_OBJECT(content_area->area),"motion_notify_event",
			    (GtkSignalFunc)motion_event, (gpointer)content_area);
	
	gtk_signal_connect (GTK_OBJECT(content_area->area), "selection_clear_event",
			    GTK_SIGNAL_FUNC (selection_clear), (gpointer)content_area);
	clipboard_atom = gdk_atom_intern ("CLIPBOARD", FALSE);
	
	gtk_selection_add_targets (content_area->area, GDK_SELECTION_PRIMARY,
				   targetlist, ntargets);
	
	gtk_selection_add_targets (content_area->area, clipboard_atom,
				   targetlist, ntargets);
	
	
	gtk_selection_add_targets (content_area->area, GDK_SELECTION_PRIMARY,
				   targetlist, ntargets);
	
	gtk_signal_connect (GTK_OBJECT(content_area->area), "selection_get",
			    GTK_SIGNAL_FUNC (selection_get), (gpointer)content_area);
	
	gtk_widget_set_events (content_area->area, GDK_EXPOSURE_MASK
			       | GDK_ENTER_NOTIFY_MASK
			       | GDK_LEAVE_NOTIFY_MASK
			       | GDK_KEY_PRESS_MASK
			       | GDK_BUTTON_PRESS_MASK
			       | GDK_BUTTON_RELEASE_MASK
			       //			 | GDK_BUTTON_MOTION_MASK
			       | GDK_POINTER_MOTION_MASK
			       //			 | GDK_POINTER_MOTION_HINT_MASK
		);	
	
	return(content_area);
	
}

gboolean resize_content_area(CONTENT_AREA *content_area, gint width, gint height)
{
	gtk_widget_set_usize( content_area->area, width, height);
}


gboolean clear_content_area(CONTENT_AREA *content_area)
{
	clear_link(content_area);
	clear_location(content_area);
}

gboolean destroy_content_area(CONTENT_AREA *content_area)
{
	clear_content_area(content_area);
	clear_history(content_area);
	free(content_area);
}


syntax highlighted by Code2HTML, v. 0.9.1