?? cellwidget.c
字號:
/*cellwriter -- a character recognition input methodCopyright (C) 2007 Michael Levin <risujin@risujin.org>This program is free software; you can redistribute it and/ormodify it under the terms of the GNU General Public Licenseas published by the Free Software Foundation; either version 2of 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with this program; if not, write to the Free SoftwareFoundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.*/#include "config.h"#include "common.h"#include "recognize.h"#include "keys.h"#include <string.h>/* stroke.c */void smooth_stroke(Stroke *s);void simplify_stroke(Stroke *s);/* cellwidget.c */int cell_widget_scrollbar_width(void);static void start_timeout(void);static void show_context_menu(int button, int time);static void stop_drawing(void);/* Cells*/#define ALTERNATES 5#define CELL_BASELINE (cell_height / 3)#define CELL_BORDER (cell_height / 12)#define KEY_WIDGET_COLS 4#define KEY_WIDGET_BORDER 6/* Msec of no mouse motion before a cell is finished */#define MOTION_TIMEOUT 500/* Cell flags */#define CELL_SHOW_INK 0x01#define CELL_DIRTY 0x02#define CELL_VERIFIED 0x04#define CELL_SHIFTED 0x08struct Cell { Sample sample, *alts[ALTERNATES]; gunichar2 ch; int alt_used[ALTERNATES]; char flags, alt_ratings[ALTERNATES];};/* Cell preferences */int cell_width = 40, cell_height = 70, cell_cols_pref = 12, cell_rows_pref = 4, enable_cairo = TRUE, training = FALSE, train_on_input = TRUE, right_to_left = FALSE, keyboard_enabled = TRUE, xinput_enabled = FALSE;/* Statistics */int corrections = 0, rewrites = 0, characters = 0, inputs = 0;/* Colors */GdkColor custom_active_color = RGB_TO_GDKCOLOR(255, 255, 255), custom_inactive_color = RGB_TO_GDKCOLOR(212, 222, 226), custom_ink_color = RGB_TO_GDKCOLOR(0, 0, 0), custom_select_color = RGB_TO_GDKCOLOR(204, 0, 0);static GdkColor color_active, color_inactive, color_ink, color_select;static Cell *cells = NULL, *cells_saved = NULL;static GtkWidget *drawing_area = NULL, *training_menu, *scrollbar;static GdkPixmap *pixmap = NULL;static GdkGC *pixmap_gc = NULL;static GdkColor color_bg, color_bg_dark;static cairo_t *cairo = NULL;static PangoContext *pango = NULL;static PangoFontDescription *pango_font_desc = NULL;static KeyWidget *key_widget;static gunichar2 *history[HISTORY_MAX];static int cell_cols, cell_rows, cell_row_view = 0, current_cell = -1, old_cc, cell_cols_saved, cell_rows_saved, cell_row_view_saved, timeout_source, drawing = FALSE, inserting = FALSE, eraser = FALSE, invalid = FALSE, potential_insert = FALSE, potential_hold = FALSE, cross_out = FALSE, show_keys = TRUE, is_clear = TRUE, keys_dirty = FALSE;static double cursor_x, cursor_y;static void cell_coords(int cell, int *px, int *py)/* Get the int position of a cell from its index */{ int cell_y, cell_x; cell -= cell_row_view * cell_cols; cell_y = cell / cell_cols; cell_x = cell - cell_y * cell_cols; *px = (!right_to_left ? cell_x * cell_width : (cell_cols - cell_x - 1) * cell_width) + 1; *py = cell_y * cell_height + 1;}static void set_pen_color(Sample *sample, int cell)/* Selects the pen color depending on if the sample being drawn is the input or the template sample */{ if (sample == input || sample == &cells[cell].sample) cairo_set_source_gdk_color(cairo, &color_ink, 1.); else cairo_set_source_gdk_color(cairo, &color_select, 1.);}static void render_point(Sample *sample, int cell, int stroke, Vec2 *offset)/* Draw a single point stroke */{ double x, y, radius; int cx, cy; if (!pixmap || stroke < 0 || !sample || stroke >= sample->len || sample->strokes[stroke]->len < 1) return; /* Apply offset */ x = sample->strokes[stroke]->points[0].x; y = sample->strokes[stroke]->points[0].y; if (offset) { x += offset->x; y += offset->y; } /* Unscale coordinates */ cell_coords(cell, &cx, &cy); x = cx + cell_width / 2 + x * cell_height / SCALE; y = cy + cell_height / 2 + y * cell_height / SCALE; /* Draw a dot with cairo */ cairo_new_path(cairo); radius = cell_height / 33.; cairo_arc(cairo, x, y, radius > 1. ? radius : 1., 0., 2 * M_PI); set_pen_color(sample, cell); cairo_fill(cairo); gtk_widget_queue_draw_area(drawing_area, x - radius - 0.5, y - radius - 0.5, radius * 2 + 0.5, radius * 2 + 0.5);}static void render_segment(Sample *sample, int cell, int stroke, int seg, Vec2 *offset)/* Draw a segment of the stroke FIXME since the segments are not properly connected according to Cairo, there is a bit of missing value at the segment connection points */{ double pen_width, x1, x2, y1, y2; int xmin, xmax, ymin, ymax, cx, cy, pen_range; if (!cairo || stroke < 0 || !sample || stroke >= sample->len || seg < 0 || seg >= sample->strokes[stroke]->len - 1) return; x1 = sample->strokes[stroke]->points[seg].x; x2 = sample->strokes[stroke]->points[seg + 1].x; y1 = sample->strokes[stroke]->points[seg].y; y2 = sample->strokes[stroke]->points[seg + 1].y; /* Apply offset */ if (offset) { x1 += offset->x; y1 += offset->y; x2 += offset->x; y2 += offset->y; } /* Unscale coordinates */ cell_coords(cell, &cx, &cy); x1 = cx + cell_width / 2 + x1 * cell_height / SCALE; x2 = cx + cell_width / 2 + x2 * cell_height / SCALE; y1 = cy + cell_height / 2 + y1 * cell_height / SCALE; y2 = cy + cell_height / 2 + y2 * cell_height / SCALE; /* Find minimum and maximum x and y */ if (x1 > x2) { xmax = x1 + 0.9999; xmin = x2; } else { xmin = x1; xmax = x2 + 0.9999; } if (y1 > y2) { ymax = y1 + 0.9999; ymin = y2; } else { ymin = y1; ymax = y2 + 0.9999; } /* Draw the new segment using Cairo */ cairo_new_path(cairo); cairo_move_to(cairo, x1, y1); cairo_line_to(cairo, x2, y2); set_pen_color(sample, cell); pen_width = cell_height / 33.; if (pen_width < 1.) pen_width = 1.; cairo_set_line_width(cairo, pen_width); cairo_stroke(cairo); /* Dirty only the new segment */ pen_range = 2 * pen_width + 0.9999; gtk_widget_queue_draw_area(drawing_area, xmin - pen_range, ymin - pen_range, xmax - xmin + pen_range + 1, ymax - ymin + pen_range + 1);}static void render_sample(Sample *sample, int cell)/* Render the ink from a sample in a cell */{ Vec2 sc_to_ic; int i, j; if (!sample) return; /* Center stored samples on input */ if (sample != &cells[cell].sample) center_samples(&sc_to_ic, sample, &cells[cell].sample); else vec2_set(&sc_to_ic, 0., 0.); for (i = 0; i < sample->len; i++) if (sample->strokes[i]->len <= 1 || sample->strokes[i]->spread < DOT_SPREAD) render_point(sample, cell, i, &sc_to_ic); else for (j = 0; j < sample->strokes[i]->len - 1; j++) render_segment(sample, cell, i, j, &sc_to_ic);}static int cell_offscreen(int cell){ int rows, cols; cols = cell_cols; if (show_keys) cols -= KEY_WIDGET_COLS; rows = cell_rows < cell_rows_pref ? cell_rows : cell_rows_pref; return cell < cell_row_view * cols || cell >= (cell_row_view + rows) * cols;}static void dirty_cell(int cell){ if (!cell_offscreen(cell)) cells[cell].flags |= CELL_DIRTY;}static void dirty_all(void){ int i, rows; rows = cell_row_view + cell_rows_pref > cell_rows ? cell_rows : cell_row_view + cell_rows_pref; for (i = cell_cols * cell_row_view; i < rows * cell_cols; i++) cells[i].flags |= CELL_DIRTY;}static void render_cell(int i){ cairo_pattern_t *pattern; GdkColor color, *base_color; Cell *pc; int x, y, active, cols, samples = 0; if (!cairo || !pixmap || !pixmap_gc || cell_offscreen(i)) return; pc = cells + i; cell_coords(i, &x, &y); if (training) { samples = char_trained(pc->ch); active = pc->ch && (samples > 0 || (current_cell == i && input && !invalid && input->len)); } else active = pc->ch || (current_cell == i && !inserting && !invalid && input && input->len); base_color = active ? &color_active : &color_inactive; /* Fill above baseline */ gdk_gc_set_rgb_fg_color(pixmap_gc, base_color); gdk_draw_rectangle(pixmap, pixmap_gc, TRUE, x, y, cell_width, cell_height - CELL_BASELINE); /* Fill baseline */ highlight_gdk_color(base_color, &color, 0.1); gdk_gc_set_rgb_fg_color(pixmap_gc, &color); gdk_draw_rectangle(pixmap, pixmap_gc, TRUE, x, y + cell_height - CELL_BASELINE, cell_width, CELL_BASELINE); /* Cairo clip region */ cairo_reset_clip(cairo); cairo_rectangle(cairo, x, y, cell_width, cell_height); cairo_clip(cairo); /* Separator line */ cols = cell_cols; if (show_keys) cols -= KEY_WIDGET_COLS; if ((!right_to_left && i % cell_cols) || (right_to_left && i % cell_cols != cols - 1)) { highlight_gdk_color(base_color, &color, 0.5); pattern = cairo_pattern_create_linear(x, y, x, y + cell_height); cairo_pattern_add_gdk_color_stop(pattern, 0.0, &color, 0.); cairo_pattern_add_gdk_color_stop(pattern, 0.5, &color, 1.); cairo_pattern_add_gdk_color_stop(pattern, 1.0, &color, 0.); cairo_set_source(cairo, pattern); cairo_set_line_width(cairo, 0.5); cairo_move_to(cairo, x + 0.5, y); cairo_line_to(cairo, x + 0.5, y + cell_height - 1); cairo_stroke(cairo); cairo_pattern_destroy(pattern); } /* Draw ink if shown */ if ((cells[i].ch && cells[i].flags & CELL_SHOW_INK) || (current_cell == i && input && input->len)) { int j; render_sample(&cells[i].sample, i); if (cells[i].ch) for (j = 0; j < ALTERNATES && cells[i].alts[j]; j++) if (sample_valid(cells[i].alts[j], cells[i].alt_used[j]) && cells[i].alts[j]->ch == cells[i].ch) { render_sample(cells[i].alts[j], i); break; } } /* Draw letter if recognized or training */ else if (pc->ch && (current_cell != i || !input || !input->len)) { PangoLayout *layout; PangoRectangle ink_ext, log_ext; char string[6] = { 0, 0, 0, 0, 0, 0 }; /* Training color is determined by how well a character is trained */ if (training) { if (samples) highlight_gdk_color(&color_ink, &color, 0.5 - ((double)samples) / samples_max / 2.); else highlight_gdk_color(&color_inactive, &color, 0.2); } /* Use ink color unless this is a questionable match */ else { color = color_ink; if (!(pc->flags & CELL_VERIFIED) && pc->alts[0] && pc->alts[1] && pc->ch == pc->alts[0]->ch && pc->alt_ratings[0] - pc->alt_ratings[1] <= 10) color = color_select; } cairo_set_source_gdk_color(cairo, &color, 1.); layout = pango_layout_new(pango); cairo_move_to(cairo, x, y); g_unichar_to_utf8(pc->ch, string); pango_layout_set_text(layout, string, 6); pango_layout_set_font_description(layout, pango_font_desc); pango_layout_get_pixel_extents(layout, &ink_ext, &log_ext); cairo_rel_move_to(cairo, cell_width / 2 - log_ext.width / 2, 2); pango_cairo_show_layout(cairo, layout);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -