/* 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 "eb.h" #include "canvas.h" #define EB_INDEX_STYLE_ASIS 1 #include #include #include #define MULTI_HACK extern GList *group_list; extern GtkWidget *multi_tree_scroll; extern GtkWidget *multi_tree; extern GtkWidget *note_tree; extern GtkWidget *note_text; extern GtkWidget *entry_box; GtkWidget *container_child(GtkWidget *container); typedef struct { GdkPixmap *pixbuff; gchar *text; } CANDIDATE_DATA; gint global_multi_code; static GList *multi_search_list=NULL; static GtkWidget *candidate_scroll=NULL; static GtkWidget *candidate_tree=NULL; static GtkWidget *entry_table=NULL; static GtkWidget *multi_entry[EB_MAX_MULTI_ENTRIES]; static BOOK_INFO *global_book_info; static gint global_entry_id; static GtkWidget *current_candidate = NULL; static gint x_max=0; static void show_multi_tree(); static void start_multi_search(GtkWidget *widget, gpointer data); static void clear_candidate(); static void show_candidate(BOOK_INFO *binfo, gint code); static void candidate_pressed(GtkWidget *widget, gpointer data); static void candidate_select_row(GtkWidget *widget, gint row, gint column, GdkEventButton *bevent, gpointer user_data); static void candidate_unselect_row(GtkWidget *widget, gint row, gint column, GdkEventButton *bevent, gpointer user_data); static void draw_candidate_text(GtkWidget *item); void show_multi() { EB_Error_Code error_code; EB_Position pos; MULTI_SEARCH *mp; BOOK_INFO *binfo; DICT_GROUP *group; DICT_MEMBER *member; GList *group_item; GList *member_item=NULL; gchar label[256]; gint i; GList *list; EB_Multi_Search_Code multi_codes[EB_MAX_MULTI_SEARCHES]; int multi_count; list = g_list_first(multi_search_list); while(list){ free(list->data); list = g_list_next(list); } if(multi_search_list) { g_list_free(multi_search_list); multi_search_list = NULL; } group_item = g_list_first(group_list); while(group_item != NULL){ group = (DICT_GROUP *)(group_item->data); if(group->active == TRUE){ member_item = group->member; break; } group_item = g_list_next(group_item); } for( ; member_item != NULL ; member_item = g_list_next(member_item)){ member = (DICT_MEMBER *)(member_item->data); if(member->active != TRUE) { continue; } binfo = member->binfo; if(binfo->search_method[SEARCH_METHOD_MULTI] != TRUE) continue; error_code = eb_multi_search_list(binfo->book, multi_codes, &multi_count); if(error_code != EB_SUCCESS) { fprintf(stderr, "Failed to list multi search : %s\n", eb_error_message(error_code)); return; } for(i=0 ; i < multi_count ; i ++){ // EBライブラリの3.2.3までは eb_multi_title()が存在しない。 // とりあえずは "複合検索1"などとしておく。 #ifdef HAVE_EB_MULTI_TITLE error_code = eb_multi_title(binfo->book, multi_codes[i], label); if(error_code != EB_SUCCESS) { fprintf(stderr, "Failed to get multi title : %s\n", eb_error_message(error_code)); return; } #else sprintf(label, "%s %d", _("Multiword Search"), i+1); #endif mp = (MULTI_SEARCH *)calloc(sizeof(SEARCH_RESULT), 1); mp->code = multi_codes[i]; mp->book_info = binfo; mp->text = strdup(label); multi_search_list = g_list_append(multi_search_list, mp); } } clear_candidate(); show_multi_tree(); } void multi_select_row(GtkWidget *widget, gint row, gint column, GdkEventButton *bevent, gpointer user_data) { GtkWidget *node; MULTI_SEARCH *idata; gchar *text; g_return_if_fail (GTK_IS_CLIST (widget)); node = (GtkWidget *)gtk_ctree_node_nth(GTK_CTREE(widget), row); idata = gtk_ctree_node_get_row_data(GTK_CTREE(widget), GTK_CTREE_NODE(node)); if(idata != NULL) { show_candidate( idata->book_info, idata->code); } return; } static void show_multi_tree() { MULTI_SEARCH *mp; GList *l; ITEM_DATA *idata; GtkWidget *node=NULL; GtkWidget *leaf; BOOK_INFO *last_book = NULL; gint result_count=0; char *node_text[1]; gchar *text; gint i; gtk_clist_freeze(GTK_CLIST(multi_tree)); while(1){ node = (GtkWidget *)gtk_ctree_node_nth(GTK_CTREE(multi_tree), 0); if(node == NULL) break; gtk_ctree_post_recursive(GTK_CTREE(multi_tree), (GtkCTreeNode *)node, (GtkCTreeFunc)gtk_ctree_remove_node, NULL); } if(multi_search_list == NULL) { gtk_clist_thaw(GTK_CLIST(multi_tree)); return; } /* gtk_adjustment_set_value( gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(multi_tree_scroll)), 0); */ for(l = multi_search_list ; l != NULL; i ++, l = g_list_next(l)){ mp = (MULTI_SEARCH *)(l->data); // 前の結果と辞書が違う場合 if(last_book != mp->book_info){ //サブツリーを作る //1つ前のサブツリーをexpand状態にする node_text[0] = mp->book_info->subbook_title; node = (GtkWidget *)gtk_ctree_insert_node(GTK_CTREE(multi_tree), NULL, //parent NULL, //sibling node_text, //text 0, //spacing book_closed_pixmap, //pixmap closed book_closed_mask, //mask closed book_open_pixmap, //pixmap opened book_open_mask, //mask opened FALSE,//is leaf TRUE);//expanded last_book = mp->book_info; } node_text[0] = mp->text; leaf = (GtkWidget *)gtk_ctree_insert_node(GTK_CTREE(multi_tree), GTK_CTREE_NODE(node), //parent NULL, //sibling node_text, //text 2, //spacing item_pixmap, //pixmap closed item_mask, //mask closed item_pixmap, //pixmap opened item_mask, //mask opened TRUE,//is leaf TRUE);//expanded // gtk_widget_set_usize( leaf, x, HEADING_HEIGHT ); gtk_ctree_node_set_row_data(GTK_CTREE(multi_tree), GTK_CTREE_NODE(leaf), (gpointer)mp); } gtk_clist_thaw(GTK_CLIST(multi_tree)); /* gtk_container_resize_children(GTK_CONTAINER(tree_scroll)); gtk_container_check_resize(GTK_CONTAINER(tree_scroll)); gtk_container_check_resize(GTK_CONTAINER(tree_root)); gtk_clist_set_column_width(GTK_CLIST(tree_root), 0, x_max+60); */ gtk_notebook_set_page(GTK_NOTEBOOK(note_tree), 1); gtk_notebook_set_page(GTK_NOTEBOOK(note_text), 1); } static void show_candidate(BOOK_INFO *binfo, gint code){ EB_Error_Code error_code; gint entry_count; gchar name[256]; EB_Position position; gint i; GtkWidget *label; GtkWidget *button; GtkWidget *frame; GtkAttachOptions xoption, yoption; gboolean have_candidate=FALSE; // xoption = GTK_EXPAND | GTK_SHRINK; // yoption = GTK_EXPAND | GTK_SHRINK; xoption = 0; yoption = 0; error_code = eb_multi_entry_count(binfo->book, code, &entry_count); if(error_code != EB_SUCCESS) { fprintf(stderr, "Failed to get multi entry count : %s\n", eb_error_message(error_code)); return; } global_book_info = binfo; global_multi_code = code; clear_candidate(); /* if(entry_table) gtk_widget_destroy(entry_table); if(candidate_tree) gtk_widget_destroy(candidate_tree); if(candidate_scroll) gtk_widget_destroy(candidate_scroll); */ for(i=0; i < EB_MAX_MULTI_ENTRIES; i ++){ multi_entry[i] = NULL; } frame = gtk_frame_new(_("Keyword")); gtk_box_pack_start(GTK_BOX(entry_box), frame, FALSE, FALSE, 0); entry_table = gtk_table_new(3, EB_MAX_MULTI_ENTRIES+1, FALSE); gtk_container_add (GTK_CONTAINER (frame), entry_table); for(i=0 ; i < entry_count ; i ++){ error_code = eb_multi_entry_label(binfo->book, code, i, name); if(error_code != EB_SUCCESS) { fprintf(stderr, "Failed to get multi title : %s\n", eb_error_message(error_code)); return; } error_code = eb_multi_entry_candidates(binfo->book, code, i, &position); if(error_code == EB_ERR_NO_CANDIDATES){ have_candidate = FALSE; } else if(error_code != EB_SUCCESS){ fprintf(stderr, "Failed to get multi candidates : %s\n", eb_error_message(error_code)); return; } else { have_candidate = TRUE; } label = gtk_label_new(name); gtk_table_attach(GTK_TABLE(entry_table), label, 0, 1, i, i+1, xoption, yoption, 5, 1); multi_entry[i] = gtk_entry_new(); gtk_signal_connect(GTK_OBJECT (multi_entry[i]), "activate", GTK_SIGNAL_FUNC(start_multi_search), (gpointer)NULL); gtk_table_attach(GTK_TABLE(entry_table), multi_entry[i], 1, 2, i, i+1, xoption, yoption, 5, 1); if(have_candidate == TRUE){ button = gtk_button_new_with_label(_("Candidates")); gtk_signal_connect(GTK_OBJECT (button), "pressed", GTK_SIGNAL_FUNC(candidate_pressed), (gpointer)i); gtk_table_attach(GTK_TABLE(entry_table), button, 2, 3, i, i+1, xoption, yoption, 5, 1); } /* if(i == 0){ button = gtk_button_new_with_label(_("Search")); gtk_signal_connect(GTK_OBJECT (button), "pressed", GTK_SIGNAL_FUNC(start_multi_search), (gpointer)NULL); gtk_table_attach(GTK_TABLE(entry_table), button, 3, 4, i, i+1, xoption, yoption, 5, 1); } */ } button = gtk_button_new_with_label(_("Start search")); gtk_signal_connect(GTK_OBJECT (button), "pressed", GTK_SIGNAL_FUNC(start_multi_search), (gpointer)NULL); gtk_box_pack_start(GTK_BOX(entry_box), button, FALSE, TRUE, 2); frame = gtk_frame_new(_("Candidates")); gtk_box_pack_start(GTK_BOX(entry_box), frame, TRUE, TRUE, 0); candidate_scroll = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (candidate_scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_container_add (GTK_CONTAINER (frame), candidate_scroll); candidate_tree = gtk_ctree_new(1, 0); gtk_clist_set_row_height(GTK_CLIST(candidate_tree),HEADING_HEIGHT); gtk_ctree_set_line_style(GTK_CTREE(candidate_tree),GTK_CTREE_LINES_NONE); gtk_clist_set_column_width(GTK_CLIST(candidate_tree), 0, 100); gtk_signal_connect (GTK_OBJECT(candidate_tree), "select_row", GTK_SIGNAL_FUNC(candidate_select_row), candidate_tree); gtk_signal_connect (GTK_OBJECT(candidate_tree), "unselect_row", GTK_SIGNAL_FUNC(candidate_unselect_row), candidate_tree); gtk_container_add (GTK_CONTAINER (candidate_scroll), candidate_tree); gtk_widget_show_all(entry_box); gtk_notebook_set_page(GTK_NOTEBOOK(note_text), 1); } static void clear_candidate(){ GList *list; while(1) { list = gtk_container_children(GTK_CONTAINER(entry_box)); if(list == NULL) break; gtk_container_remove(GTK_CONTAINER(entry_box), list->data); } } static show_candidate_tree(BOOK_INFO *binfo, gint page, gint offset, GtkWidget *tree, GtkWidget *parent) { gchar *text; gchar *p; gchar start_tag[512]; gchar end_tag[512]; gchar tag_name[512]; gchar attr[512]; gchar body[65536]; gchar *content; gchar *candidate; gint content_length; gint body_length; gint l_page=0, l_offset=0; char *node_text[1]; GtkWidget *node; CANDIDATE_DATA *cdata; current_candidate = NULL; text = ebook_get_candidate(global_book_info, page, offset); body_length = 0; p = text; while(*p != '\0'){ if(*p == '<'){ if(body_length != 0){ fprintf(stderr, "candidate format error0\n"); body_length = 0; } get_start_tag(p, start_tag); get_tag_name(start_tag, tag_name); if(strcmp(tag_name, "candidate") == 0){ get_end_tag(p, tag_name, end_tag); l_page = l_offset = 0; get_attr(end_tag, "page", attr); l_page = strtol(attr, NULL, 16); get_attr(end_tag, "offset", attr); l_offset = strtol(attr, NULL, 16); get_content(p, tag_name, &content, &content_length); candidate = g_strndup(content, content_length); if(l_page == 0){ // Leaf node_text[0] = candidate; node = (GtkWidget *)gtk_ctree_insert_node(GTK_CTREE(tree), (GtkCTreeNode *)parent, NULL, node_text, 0, NULL, NULL, NULL, NULL, TRUE,TRUE); } else { // Not leaf node_text[0] = candidate; node = (GtkWidget *)gtk_ctree_insert_node(GTK_CTREE(tree), (GtkCTreeNode *)parent, NULL, node_text, 0, NULL, NULL, NULL, NULL, FALSE,FALSE); show_candidate_tree(binfo, l_page, l_offset, tree, node); } cdata = (CANDIDATE_DATA *)calloc(sizeof(CANDIDATE_DATA), 1); g_assert(cdata != NULL); cdata->text = candidate; gtk_ctree_node_set_row_data(GTK_CTREE(candidate_tree), GTK_CTREE_NODE(node), (gpointer)cdata); draw_candidate_text(node); if(current_candidate == NULL){ current_candidate = node; } skip_end_tag(&p, tag_name); } else { fprintf(stderr, "candidate format error1\n"); body[body_length] = *p; body_length ++; body[body_length] = '\0'; p++; fprintf(stderr, "%s\n", body); } } else if (*p == '\n'){ p++; } else { fprintf(stderr, "candidate format error2\n"); body[body_length] = *p; body_length ++; body[body_length] = '\0'; p++; } } if(body_length != 0){ fprintf(stderr, "candidate format erro3\n"); } free(text); } static void draw_candidate_text(GtkWidget *item) { CANDIDATE_DATA *cdata; gint x,y; gint max_width, max_height; GdkGC *gc; CANVAS *canvas; DRAW_TEXT text; gboolean is_leaf; if(item == NULL) return; cdata = gtk_ctree_node_get_row_data(GTK_CTREE(candidate_tree), GTK_CTREE_NODE(item)); if(cdata == NULL){ return; } if(cdata->text == NULL) return; x = 0; // y = HEADING_PIXMAP_HEIGHT; y = font_ascent+2; // Pixmapのサイズを決めるために描画をシミュレートする canvas = create_canvas(window); canvas->x = x; canvas->y = y; canvas->width = HEADING_PIXMAP_WIDTH; // canvas->height = HEADING_PIXMAP_HEIGHT; canvas->height = font_height + 2; text.text = cdata->text; text.length = strlen(text.text); draw_content(canvas, &text, global_book_info, NULL); x = canvas->x; y = canvas->y; if(x > HEADING_PIXMAP_WIDTH) x = HEADING_PIXMAP_WIDTH; if(y > font_height) y = font_height; y += 2; // 最大幅を記憶しておく if(x_max < x) { x_max = x; } gc = gdk_gc_new(window->window); if(cdata->pixbuff != NULL){ gdk_pixmap_unref(cdata->pixbuff); } cdata->pixbuff = gdk_pixmap_new( window->window, x, y, -1 ); if(cdata->pixbuff == NULL){ g_assert(cdata->pixbuff != NULL); } if(item == current_candidate){ gdk_gc_set_foreground(gc, &candidate_tree->style->bg[GTK_STATE_SELECTED]); gdk_draw_rectangle(cdata->pixbuff, gc, TRUE, 0,0, x, y); } else { gdk_gc_set_foreground(gc, &candidate_tree->style->base[GTK_STATE_NORMAL]); gdk_draw_rectangle(cdata->pixbuff, gc, TRUE, 0,0, x, y); } max_width = HEADING_PIXMAP_WIDTH; max_height = font_height+2; // unused x = 0; y = font_ascent+2; // 選択されているかどうかで色を変える if(item == current_candidate){ gdk_gc_set_foreground(gc, &candidate_tree->style->fg[GTK_STATE_SELECTED]); gdk_gc_set_background(gc, &candidate_tree->style->bg[GTK_STATE_SELECTED]); } else { gdk_gc_set_foreground(gc, &candidate_tree->style->fg[GTK_STATE_NORMAL]); gdk_gc_set_background(gc, &candidate_tree->style->base[GTK_STATE_NORMAL]); } // 描画する canvas->pixmap = cdata->pixbuff; canvas->x = x; canvas->y = y; canvas->width = max_width; canvas->height = max_height; canvas->gc = gc; text.text = cdata->text; text.length = strlen(text.text); draw_content(canvas, &text, global_book_info, NULL); gdk_gc_destroy(gc); // このノードとPixmapを関連づける gtk_ctree_get_node_info(GTK_CTREE(candidate_tree), (GtkCTreeNode *)item, NULL, NULL, NULL, NULL, NULL, NULL, &is_leaf, NULL); if(is_leaf){ gtk_ctree_node_set_pixmap(GTK_CTREE(candidate_tree), GTK_CTREE_NODE(item), 0, cdata->pixbuff, NULL); } else { gtk_ctree_node_set_pixmap(GTK_CTREE(candidate_tree), GTK_CTREE_NODE(item), 1, cdata->pixbuff, NULL); } free(canvas); } void candidate_select_row(GtkWidget *widget, gint row, gint column, GdkEventButton *bevent, gpointer user_data) { GtkCTreeNode *node; GtkWidget *last_candidate; gboolean is_leaf; gchar *text; CANDIDATE_DATA *cdata; g_return_if_fail (GTK_IS_CLIST (widget)); node = gtk_ctree_node_nth(GTK_CTREE(widget), row); last_candidate = current_candidate; current_candidate = (GtkWidget *)node; draw_candidate_text((GtkWidget *)last_candidate); gtk_ctree_get_node_info(GTK_CTREE(widget), node, &text, NULL, NULL, NULL, NULL, NULL, &is_leaf, NULL); cdata = gtk_ctree_node_get_row_data(GTK_CTREE(candidate_tree), GTK_CTREE_NODE(node)); if(is_leaf == TRUE){ switch (bevent->type) { case GDK_BUTTON_PRESS: case GDK_BUTTON_RELEASE: gtk_entry_set_text(GTK_ENTRY(multi_entry[global_entry_id]), cdata->text); break; case GDK_2BUTTON_PRESS: start_multi_search(NULL, NULL); break; default: break; } } draw_candidate_text((GtkWidget *)node); } static void candidate_unselect_row(GtkWidget *widget, gint row, gint column, GdkEventButton *bevent, gpointer user_data) { GtkCTreeNode *node; node = gtk_ctree_node_nth(GTK_CTREE(widget), row); current_candidate = NULL; draw_candidate_text((GtkWidget *)node); return; } static void candidate_pressed(GtkWidget *widget, gpointer data) { EB_Position position; EB_Error_Code error_code; gchar *text; GtkWidget *button; GtkWidget *node; global_entry_id = (gint)data; error_code = eb_multi_entry_candidates(global_book_info->book, global_multi_code, global_entry_id, &position); if (error_code == EB_ERR_NO_CANDIDATES) { return; } else if (error_code != EB_SUCCESS) { /* それ以外のエラー */ return; } gtk_clist_freeze(GTK_CLIST(candidate_tree)); while(1){ node = (GtkWidget *)gtk_ctree_node_nth(GTK_CTREE(candidate_tree), 0); if(node == NULL) break; gtk_ctree_post_recursive(GTK_CTREE(candidate_tree), (GtkCTreeNode *)node, (GtkCTreeFunc)gtk_ctree_remove_node, NULL); } show_candidate_tree(global_book_info, position.page, position.offset, candidate_tree, NULL); gtk_clist_thaw(GTK_CLIST(candidate_tree)); } static void start_multi_search(GtkWidget *widget, gpointer data) { EB_Position position; EB_Error_Code error_code; gchar *text; gchar word[256]; gint i; gchar attr[512]; gchar *p; guint code; #ifdef MULTI_HACK EB_Search saved_search[EB_MAX_MULTI_ENTRIES]; EB_Multi_Search *multi; #endif word[0] = '\0'; for(i=0; i < EB_MAX_MULTI_ENTRIES; i ++){ if(multi_entry[i] == NULL) break; text = gtk_entry_get_text(GTK_ENTRY(multi_entry[i])); if(text == NULL) break; if(strstr(text, "> 8); p ++; *p = (code & 0xff); p ++; *p = ' '; p ++; *p = '\0'; } else { strcat(word, text); strcat(word, " "); } } word[strlen(word) - 1] = '\0'; // gtk_entry_set_text(GTK_ENTRY(word_entry), word); if(strlen(word) == 0) return; clear_search_result(); clear_tree(tree_root); error_code = ebook_simple_search(global_book_info, word, SEARCH_METHOD_MULTI); // save_word_history(word); if (error_code != EB_SUCCESS) { return; } #ifdef MULTI_HACK if(g_list_length(search_result) != 0){ show_result_tree(); return; } // インデックスの取扱方法が規格と違う辞書があるように思えるので、 // 本来の方法で1件もヒットしなかったら、 // すべてそのまま格納されているものとして再度検索してみる。 g_print("HACK!!\n"); multi = &(global_book_info->book->subbook_current->multis[global_multi_code]); for(i=0; ientries[i]; multi->entries[i].katakana = EB_INDEX_STYLE_ASIS; multi->entries[i].lower = EB_INDEX_STYLE_ASIS; multi->entries[i].mark = EB_INDEX_STYLE_ASIS; multi->entries[i].long_vowel = EB_INDEX_STYLE_ASIS; multi->entries[i].double_consonant = EB_INDEX_STYLE_ASIS; multi->entries[i].contracted_sound = EB_INDEX_STYLE_ASIS; multi->entries[i].voiced_consonant = EB_INDEX_STYLE_ASIS; multi->entries[i].small_vowel = EB_INDEX_STYLE_ASIS; multi->entries[i].p_sound = EB_INDEX_STYLE_ASIS; multi->entries[i].space = EB_INDEX_STYLE_ASIS; } error_code = ebook_simple_search(global_book_info, word, SEARCH_METHOD_MULTI); for(i=0; ientries[i] = saved_search[i]; } if (error_code != EB_SUCCESS) { return; } #endif show_result_tree(); }