/* 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 #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 // 漢字とひらがななどを区別する。 // いったんJISコードに変換してから範囲比較する。 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)){ //記号 return(CHAR_KIND_ZENKAKU_KIGOU); } if((0x2200 <= kchar) && (kchar <= 0x22ff)){ //記号 return(CHAR_KIND_ZENKAKU_KIGOU); } if((0x2330 <= kchar) && (kchar <= 0x2339)){ //数字 return(CHAR_KIND_ZENKAKU_DIGIT); } if((0x2340 <= kchar) && (kchar <= 0x23ff)){ //アルファベット return(CHAR_KIND_ZENKAKU_ALPHA); } if((0x2400 <= kchar) && (kchar <= 0x24ff)){ //ひらがな return(CHAR_KIND_ZENKAKU_HIRAGANA); } if((0x2500 <= kchar) && (kchar <= 0x25ff)){ //カタカナ return(CHAR_KIND_ZENKAKU_KATAKANA); } if((0x2600 <= kchar) && (kchar <= 0x26ff)){ //ギリシャ文字 return(CHAR_KIND_ZENKAKU_GREEK); } if((0x2700 <= kchar) && (kchar <= 0x27ff)){ //ロシア文字? return(CHAR_KIND_ZENKAKU_RUSIA); } if((0x2800 <= kchar) && (kchar <= 0x28ff)){ //罫線 return(CHAR_KIND_ZENKAKU_KEISEN); } if((0x3000 <= kchar) && (kchar <= 0x74ff)){ //漢字 return(CHAR_KIND_ZENKAKU_KANJI); } iconv_close(cd); free(result); #endif return(CHAR_KIND_OTHER); } /* 1文字を反転させたり戻したりする 通常の文字でも外字でも良い。 reverse = TRUE なら反転、FALSEなら戻す。 */ 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; } /* 単語を選択する 文字種が同じものを単語として判断する */ 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); } // リンクがあるかをチェック 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)){ // 他へのジャンプ 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(); //ここから下はlの値が書き換わっているので // 参照してはいけない free(text); gtk_signal_emit_stop_by_name(GTK_OBJECT(widget), "button_press_event"); return(TRUE); // 音声へのリンク } 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); // 動画へのリンク } 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); } // リンクではない場合 // 反転しているものがあったら元に戻す loc_item = content_area->location; while(loc_item){ loc = (LOCATION *)(loc_item->data); if(loc->reverse == TRUE){ reverse_selection(content_area, loc, FALSE); // イタリックのときに文字が欠けるのを防ぐため、 // 1つ前の文字を再描画する。 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); } // ボーダー領域だったら補正 if(event->x < h_border) event->x = h_border; if(event->y < v_space) event->y = v_space; //同じ文字の領域内だったらなにもしない。 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; // 次の行にいったか 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){ // 次の行に入ったか if(event->y >= loc->y){ line_skip=TRUE; content_area->direction = DIRECTION_FORWARD; } break; } loc_item = g_list_next(loc_item); } } // 前の行か 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; } } // 同じ行の場合 if(line_skip == FALSE){ // 右に進んでいるか if(((LOCATION *)(content_area->selection_start->data))->x < event->x){ content_area->direction = DIRECTION_FORWARD; // それとも左か } else if(((LOCATION *)(content_area->selection_start->data))->x > event->x) { content_area->direction = DIRECTION_BACKWARD; } else { return(FALSE); } } // endを決める // directionはどちらであってもendが終点になる 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); } // 以前は選択されていたが今回は選択されていないものは戻す loc_item = save_start; while(loc_item){ loc = (LOCATION *)(loc_item->data); exist = FALSE; loc_item2 = content_area->selection_start; while(loc_item2){ // 前も選択されていた 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); // イタリックのときに文字が欠けるのを防ぐため、 // 1つ前の文字を再描画する。 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); // 今回は選択されていて以前は選択されていなかったものは反転 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)); // 反転しているものを元に戻す 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); // イタリックのときに文字が欠けるのを防ぐため、 // 1つ前の文字を再描画する。 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); // イタリックのときに文字が欠けるのを防ぐため、 // 1つ前の文字を再描画する。 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); }