aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--TODO30
-rw-r--r--char-table.c269
-rw-r--r--char-table.h11
-rw-r--r--gcmap.c196
-rw-r--r--logical-scroll-pane.c24
5 files changed, 477 insertions, 53 deletions
diff --git a/TODO b/TODO
index 07fc25d..071b4bc 100644
--- a/TODO
+++ b/TODO
@@ -1,3 +1,20 @@
+Add drag-and-drop support, both in to find a character and out to copy a character
+
+Scroll with wheel, page up/down, or scroll bar shall adjust the character selection to remain in view
+
+Show font and upscaled (based on same font size) glyph when Shift is held
+(note: selection change via gcmap_char_table_key_press may require update)
+
+Show font and upscaled (based on same font size) glyph when mouse button 3 (right)
+is held down; this shall also select the character and do so continuously while
+moving the pointer
+
+Middle-clicking should find the character stored in PRIMARY (SECONDARY if Mod1 is held down)
+
+Keep selected character in view when resizing the chart or the cells
+
+Optionally show metrics
+
Add bookmarks expander under the tabbed pane
Mark whenever a fallback font is used for a glyph
@@ -17,9 +34,20 @@ Add support for command line operands: select listed fonts by path
Add editor to font drop-down to make it easier to select a font
-Scrolling inside a GtkScrolledWindow or LogicalScrollPane shall steal focus
+Scrolling inside a GtkScrolledWindow or LogicalScrollPane shall steal focus (gtk_widget_grab_focus)
Scrolling with Control held down shall change zoom. The hovered cell shall
remain visible, preferably at the same Y position as the pointer. The pointer
shall jump to the new position of the cell so account for it being impossible
to ensure that cell remains at the same X position as the pointer.
+
+Under "Go" add option to go to the same character in:
+ - All
+ - Block
+ - Primary script
+ - Next secondary script
+ - Previous secondary script
+
+Add command line options for opening into a specific character or codepoint
+
+Fix XXX's
diff --git a/char-table.c b/char-table.c
index 4aec952..7534600 100644
--- a/char-table.c
+++ b/char-table.c
@@ -54,6 +54,22 @@ apply_font_size(CharTable *this, int size, int *text_w_out, int *text_h_out)
}
+PURE static uint32_t
+get_codepoint(CharTable *this, uint32_t index)
+{
+ size_t i = 0u;
+ uint32_t count;
+
+ while (index >= (count = (uint32_t)LIBCMAP_RANGE_SIZE(&this->ranges[i]))) {
+ index -= count;
+ if (++i == this->nranges)
+ abort();
+ }
+
+ return this->ranges[i].first + index;
+}
+
+
static void
fit_text(CharTable *this, const char *text, int *text_w_out, int *text_h_out)
{
@@ -110,18 +126,20 @@ get_exposed_cells(GtkWidget *widget, GdkEventExpose *event, struct cells *cells_
GdkRectangle area = event->area;
LogicalScrollPane *scrolls = LOGICAL_SCROLL_PANE(gtk_widget_get_parent(widget));
GtkAdjustment *vadjustment = logical_scroll_pane_get_vadjustment(scrolls);
+ double y;
- cells_out->r0 = area.y * this->nrows / this->viewport_h;
- cells_out->c0 = area.x * this->ncols / this->viewport_w;
- cells_out->r1 = (area.y + area.height - 1) * this->nrows / this->viewport_h + 1;
- cells_out->c1 = (area.x + area.width - 1) * this->ncols / this->viewport_w + 1;
+ cells_out->r0 = area.y * this->nrows / (this->viewport_h + 1);
+ cells_out->c0 = area.x * this->ncols / (this->viewport_w + 1);
+ cells_out->r1 = (area.y + area.height - 1) * this->nrows / (this->viewport_h + 1) + 1;
+ cells_out->c1 = (area.x + area.width - 1) * this->ncols / (this->viewport_w + 1) + 1;
cells_out->r0 = MAX(cells_out->r0, 0);
cells_out->c0 = MAX(cells_out->c0, 0);
cells_out->r1 = MIN(cells_out->r1, cells_out->r0 + this->nrows);
cells_out->c1 = MIN(cells_out->c1, cells_out->c0 + this->ncols);
- cells_out->r_off = MAX((int)vadjustment->value, 0);
+ y = round(vadjustment->value);
+ cells_out->r_off = MAX((int)y, 0);
}
@@ -136,7 +154,8 @@ gcmap_char_table_expose(GtkWidget *widget, GdkEventExpose *event)
char text[16];
int r, c, x0, x1, y0, y1, text_w, text_h;
size_t skip, range, row_skip, max_jump, jump;
- uint32_t cp;
+ uint32_t cp, index;
+ GtkStateType selectedState = gtk_widget_has_focus(widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE;
get_exposed_cells(widget, event, &cells);
@@ -166,8 +185,8 @@ gcmap_char_table_expose(GtkWidget *widget, GdkEventExpose *event)
x1 = (this->viewport_w + 1) * cells.c1 / this->ncols;
for (r = cells.r0 + (area.y > 0); r < cells.r1; r++, y0 = y1) {
y1 = (this->viewport_h + 1) * (r + 1) / this->nrows;
- cairo_move_to(g, x0, y0 + 0.5);
- cairo_line_to(g, x1, y0 + 0.5);
+ cairo_move_to(g, x0, y0 - 0.5);
+ cairo_line_to(g, x1, y0 - 0.5);
cairo_stroke(g);
}
/* vertical */
@@ -176,8 +195,8 @@ gcmap_char_table_expose(GtkWidget *widget, GdkEventExpose *event)
y1 = (this->viewport_h + 1) * cells.r1 / this->nrows;
for (c = cells.c0 + (area.x > 0); c < cells.c1; c++, x0 = x1) {
x1 = (this->viewport_w + 1) * (c + 1) / this->ncols;
- cairo_move_to(g, x0 + 0.5, y0);
- cairo_line_to(g, x0 + 0.5, y1);
+ cairo_move_to(g, x0 - 0.5, y0);
+ cairo_line_to(g, x0 - 0.5, y1);
cairo_stroke(g);
}
@@ -188,8 +207,9 @@ gcmap_char_table_expose(GtkWidget *widget, GdkEventExpose *event)
cp = this->ranges[range].first;
skip = (size_t)cells.r0 + (size_t)cells.r_off;
skip = skip * (size_t)this->ncols + (size_t)cells.c0;
+ index = (uint32_t)skip;
row_skip = (size_t)this->ncols - (size_t)(cells.c1 - cells.c0);
- for (r = cells.r0; r < cells.r1; r++, y0 = y1, skip = row_skip) {
+ for (r = cells.r0; r < cells.r1; r++, y0 = y1, skip = row_skip, index += (uint32_t)row_skip) {
while (skip) {
max_jump = (size_t)(this->ranges[range].last - cp + 1u);
jump = MIN(skip, max_jump);
@@ -203,7 +223,7 @@ gcmap_char_table_expose(GtkWidget *widget, GdkEventExpose *event)
}
x0 = (this->viewport_w + 1) * cells.c0 / this->ncols;
y1 = (this->viewport_h + 1) * (r + 1) / this->nrows;
- for (c = cells.c0; c < cells.c1; c++, cp++, x0 = x1) {
+ for (c = cells.c0; c < cells.c1; c++, cp++, index++, x0 = x1) {
if (cp > this->ranges[range].last) {
if (++range == this->nranges)
goto out;
@@ -211,10 +231,20 @@ gcmap_char_table_expose(GtkWidget *widget, GdkEventExpose *event)
}
x1 = (this->viewport_w + 1) * (c + 1) / this->ncols;
+ if (index == this->selected) {
+ set_source_colour(g, &style->base[selectedState]);
+ cairo_rectangle(g, x0, y0, x1 - x0 - 1.0, y1 - y0 - 1.0);
+ cairo_fill(g);
+ set_source_colour(g, &style->text[selectedState]);
+ }
+
format_character((gunichar)cp, text);
fit_text(this, text, &text_w, &text_h);
cairo_move_to(g, (x0 + x1 - text_w) / 2, (y0 + y1 - text_h) / 2);
pango_cairo_show_layout(g, this->layout);
+
+ if (index == this->selected)
+ set_source_colour(g, &style->text[GTK_STATE_ACTIVE]);
}
}
@@ -257,6 +287,13 @@ update_cell_size(GtkWidget *widget, int ranges_changed)
this->ncols = MAX(this->ncols, 1);
this->nrows = MAX(this->nrows, 1);
+ if (this->snap_cols_to_2pow)
+ while (this->ncols & (this->ncols - 1))
+ this->ncols &= this->ncols - 1;
+ if (this->snap_rows_to_2pow)
+ while (this->nrows & (this->nrows - 1))
+ this->nrows &= this->nrows - 1;
+
cell_w = (this->viewport_w + 1) / this->ncols - 1;
cell_h = (this->viewport_h + 1) / this->nrows - 1;
cell_w = MAX(cell_w, 1);
@@ -268,9 +305,9 @@ update_cell_size(GtkWidget *widget, int ranges_changed)
this->cell_h = cell_h;
if (ranges_changed) {
- nchars = (uint32_t)this->nranges;
+ nchars = 0u;
for (i = 0u; i < this->nranges; i++)
- nchars += this->ranges[i].last - this->ranges[i].first;
+ nchars += (uint32_t)LIBCMAP_RANGE_SIZE(&this->ranges[i]);
this->nchars = nchars;
}
}
@@ -298,14 +335,94 @@ gcmap_char_table_size_request(GtkWidget *widget, GtkRequisition *requisition)
requisition->height = 1;
}
+
+static void
+select_clicked_character(GtkWidget *widget, int meta, unsigned click_count, double x, double y)
+{
+ CharTable *this = GCMAP_CHAR_TABLE(widget);
+ LogicalScrollPane *scrolls = LOGICAL_SCROLL_PANE(gtk_widget_get_parent(widget));
+ GtkAdjustment *vadjustment = logical_scroll_pane_get_vadjustment(scrolls);
+ double yadj = round(vadjustment->value);
+ int r0 = (int)yadj;
+ int r = (int)y * this->nrows / (this->viewport_h + 1);
+ int c = (int)x * this->ncols / (this->viewport_w + 1);
+ uint32_t selected;
+ r = MIN(MAX(0, r), this->nrows - 1);
+ c = MIN(MAX(0, c), this->ncols - 1);
+ selected = ((uint32_t)r + (uint32_t)r0) * (uint32_t)this->ncols + (uint32_t)c;
+ if (selected >= this->nchars) {
+ this->multiclick_count = 0u;
+ } else if (selected != this->selected) {
+ click_count = 1u;
+ clicked:
+ this->selected = selected;
+ if (this->selection_callback)
+ (*this->selection_callback)(selected, get_codepoint(this, selected), meta, click_count >= 2u);
+ this->multiclick_count = click_count == 1u ? 1u : 0u;
+ gtk_widget_queue_draw(GTK_WIDGET(this)); /* XXX just redraw the affected cells */
+ } else if (click_count > this->multiclick_count) {
+ goto clicked;
+ } else {
+ this->multiclick_count = 0u;
+ }
+}
+
+
static gboolean
-gcmap_char_table_button_press(GtkWidget *widget, GdkEventButton *event)
+gcmap_char_table_button_press(GtkWidget *widget, GdkEventButton *event, void *user_data)
{
- (void) event;
+ unsigned click_count;
+ (void) user_data;
gtk_widget_grab_focus(widget);
- return FALSE;
+ if (event->button == 1 || event->button == 3) {
+ switch ((unsigned)event->type) {
+ default:
+ case GDK_BUTTON_PRESS: click_count = 1u; break;
+ case GDK_2BUTTON_PRESS: click_count = 2u; break;
+ case GDK_3BUTTON_PRESS: click_count = 3u; break;
+ }
+ select_clicked_character(widget, !!(event->state & GDK_MOD1_MASK), click_count, event->x, event->y);
+ } else {
+ GCMAP_CHAR_TABLE(widget)->multiclick_count = 0u;
+ }
+ return TRUE;
+}
+
+
+static gboolean
+gcmap_char_table_key_press(GtkWidget *widget, GdkEventKey *event, void *user_data)
+{
+ CharTable *this = GCMAP_CHAR_TABLE(widget);
+ int meta;
+
+ (void) user_data;
+
+ if (event->state & GDK_CONTROL_MASK)
+ return FALSE;
+
+ meta = !!(event->state & GDK_MOD1_MASK);
+
+ switch (event->keyval) {
+ case GDK_KEY_Left: case GDK_KEY_KP_Left: gcmap_char_table_adjust_cell_selection(this, -1, 0, meta); break;
+ case GDK_KEY_Right: case GDK_KEY_KP_Right: gcmap_char_table_adjust_cell_selection(this, +1, 0, meta); break;
+ case GDK_KEY_Up: case GDK_KEY_KP_Up: gcmap_char_table_adjust_cell_selection(this, -this->ncols, 0, meta); break;
+ case GDK_KEY_Down: case GDK_KEY_KP_Down: gcmap_char_table_adjust_cell_selection(this, +this->ncols, 0, meta); break;
+ case GDK_KEY_Home: case GDK_KEY_KP_Home: gcmap_char_table_select_cell(this, 0, 0, meta); break;
+ case GDK_KEY_End: case GDK_KEY_KP_End: gcmap_char_table_select_cell(this, INT32_MAX, 1, meta); break;
+ case GDK_KEY_Return: case GDK_KEY_KP_Enter:
+ case GDK_KEY_Begin: case GDK_KEY_KP_Begin:
+ case GDK_KEY_space:
+ if (this->selection_callback)
+ (*this->selection_callback)(this->selected, get_codepoint(this, this->selected), meta, 1);
+ break;
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
}
+
static void
gcmap_char_table_init(CharTable *this)
{
@@ -317,10 +434,15 @@ gcmap_char_table_init(CharTable *this)
this->max_h = 1;
this->cell_h = 1;
this->nchars = 0u;
+ this->selected = 0u;
this->ranges = NULL;
this->nranges = 0u;
this->viewport_w = 1;
this->viewport_h = 1;
+ this->snap_cols_to_2pow = 0;
+ this->snap_rows_to_2pow = 0;
+ this->multiclick_count = 0u;
+ this->selection_callback = NULL;
GTK_WIDGET_SET_FLAGS(widget, GTK_CAN_FOCUS);
gtk_widget_set_sensitive(widget, TRUE);
@@ -330,8 +452,8 @@ gcmap_char_table_init(CharTable *this)
GDK_KEY_PRESS_MASK |
GDK_FOCUS_CHANGE_MASK);
- g_signal_connect(widget, "button-press-event",
- G_CALLBACK(&gcmap_char_table_button_press), NULL);
+ g_signal_connect(widget, "button-press-event", G_CALLBACK(&gcmap_char_table_button_press), NULL);
+ g_signal_connect(widget, "key-press-event", G_CALLBACK(&gcmap_char_table_key_press), NULL);
}
@@ -421,6 +543,28 @@ gcmap_char_table_set_font_size(CharTable *this, int size)
void
+gcmap_char_table_set_snap_cols_to_2pow(CharTable *this, int active)
+{
+ GtkWidget *widget = GTK_WIDGET(this);
+ this->snap_cols_to_2pow = active;
+ update_cell_size(widget, 0);
+ update_content_size(this);
+ gtk_widget_queue_draw(widget);
+}
+
+
+void
+gcmap_char_table_set_snap_rows_to_2pow(CharTable *this, int active)
+{
+ GtkWidget *widget = GTK_WIDGET(this);
+ this->snap_rows_to_2pow = active;
+ update_cell_size(widget, 0);
+ update_content_size(this);
+ gtk_widget_queue_draw(widget);
+}
+
+
+void
gcmap_char_table_set_ranges(CharTable *this, const struct libcmap_range *ranges, size_t nranges)
{
GtkWidget *widget;
@@ -428,9 +572,12 @@ gcmap_char_table_set_ranges(CharTable *this, const struct libcmap_range *ranges,
return;
this->ranges = ranges;
this->nranges = nranges;
+ this->selected = 0u;
widget = GTK_WIDGET(this);
update_cell_size(widget, 1);
update_content_size(this);
+ if (this->selection_callback)
+ (*this->selection_callback)(this->selected, get_codepoint(this, this->selected), 0, 0);
gtk_widget_queue_draw(widget);
}
@@ -469,3 +616,87 @@ gcmap_char_table_new(const char *font_family, int font_size, PangoWeight font_we
return widget;
}
+
+
+void
+gcmap_char_table_select_cell(CharTable *this, int32_t selected, int clamp, int meta)
+{
+ GtkWidget *widget = GTK_WIDGET(this);
+ LogicalScrollPane *scrolls = LOGICAL_SCROLL_PANE(gtk_widget_get_parent(widget));
+ GtkAdjustment *vadjustment = logical_scroll_pane_get_vadjustment(scrolls);
+ double yadj = round(vadjustment->value);
+ int r0 = MAX((int)yadj, 0);
+ int r = r0;
+ int32_t first, last;
+
+ if (selected < 0) {
+ if (!clamp || !this->nchars)
+ return;
+ selected = 0;
+ } else if ((uint32_t)selected >= this->nchars) {
+ if (!clamp)
+ return;
+ selected = (int32_t)(this->nchars - 1u);
+ }
+
+ if ((uint32_t)selected == this->selected)
+ return;
+
+ if (selected == 0) {
+ r = 0;
+ } else if (selected == (int32_t)this->nchars - 1) {
+ goto scroll_down;
+ } else if ((uint32_t)selected < this->selected) {
+ first = (int32_t)r0 * (int32_t)this->ncols;
+ if (selected < first)
+ r = (int)((uint32_t)selected / (uint32_t)this->ncols);
+ } else {
+ last = ((int32_t)r0 + (int32_t)this->nrows) * (int32_t)this->ncols - 1;
+ if (selected > last) {
+ scroll_down:
+ r = (int)((uint32_t)selected / (uint32_t)this->ncols);
+ r -= this->nrows - 1;
+ r = MAX(r, 0);
+ }
+ }
+
+ this->selected = (uint32_t)selected;
+ if (this->selection_callback)
+ (*this->selection_callback)(this->selected, get_codepoint(this, this->selected), meta, 0);
+
+ if (r == r0) {
+ gtk_widget_queue_draw(widget); /* XXX just redraw the affected cells */
+ } else {
+ yadj = (double)r;
+ gtk_adjustment_set_value(vadjustment, yadj);
+ }
+}
+
+
+void
+gcmap_char_table_adjust_cell_selection(CharTable *this, int32_t adjustment, int clamp, int meta)
+{
+ int32_t selected = (int32_t)this->selected;
+ gcmap_char_table_select_cell(this, selected + adjustment, clamp, meta);
+}
+
+
+int
+gcmap_char_table_select_codepoint(CharTable *this, uint32_t codepoint)
+{
+ size_t i = 0u;
+ uint32_t index = 0u;
+
+ while (i < this->nranges && codepoint > this->ranges[i].last) {
+ index += (uint32_t)LIBCMAP_RANGE_SIZE(&this->ranges[i]);
+ i += 1u;
+ }
+
+ if (i >= this->nranges || codepoint < this->ranges[i].first)
+ return 0;
+
+ index += codepoint - this->ranges[i].first;
+
+ gcmap_char_table_select_cell(this, (int32_t)index, 0, 0);
+ return 1;
+}
diff --git a/char-table.h b/char-table.h
index 1663c9c..6976156 100644
--- a/char-table.h
+++ b/char-table.h
@@ -15,15 +15,19 @@ typedef struct _CharTable {
int nrows, ncols;
int max_w, max_h;
int cell_h;
- uint32_t nchars;
+ uint32_t nchars, selected;
int viewport_w, viewport_h;
PangoFontDescription *font;
PangoLayout *layout;
int font_size;
+ int snap_cols_to_2pow;
+ int snap_rows_to_2pow;
+ unsigned multiclick_count;
PangoWeight font_weight;
PangoStyle font_style;
const struct libcmap_range *ranges;
size_t nranges;
+ void (*selection_callback)(uint32_t index, uint32_t codepoint, int meta, int copy_to_text_field);
} CharTable;
typedef struct _CharTableClass {
@@ -38,5 +42,10 @@ void gcmap_char_table_set_font_family(CharTable *this, const char *family);
void gcmap_char_table_set_font_weight(CharTable *this, PangoWeight weight);
void gcmap_char_table_set_font_style(CharTable *this, PangoStyle style);
void gcmap_char_table_set_font_size(CharTable *this, int size);
+void gcmap_char_table_set_snap_cols_to_2pow(CharTable *this, int active);
+void gcmap_char_table_set_snap_rows_to_2pow(CharTable *this, int active);
void gcmap_char_table_set_ranges(CharTable *this, const struct libcmap_range *ranges, size_t nranges);
void gcmap_char_table_viewport_updated(CharTable *this, int viewport_w, int viewport_h);
+void gcmap_char_table_select_cell(CharTable *this, int32_t selected, int clamp, int meta);
+void gcmap_char_table_adjust_cell_selection(CharTable *this, int32_t adjustment, int clamp, int meta);
+int gcmap_char_table_select_codepoint(CharTable *this, uint32_t codepoint);
diff --git a/gcmap.c b/gcmap.c
index 816d83e..25a9ef6 100644
--- a/gcmap.c
+++ b/gcmap.c
@@ -44,11 +44,16 @@ static GtkWidget *nextgroup;
static GtkWidget *prevgroup;
static GtkWidget *bylisting[NLISTINGS];
static GtkWidget *vtabtext;
+static GtkWidget *snap2_cols;
+static GtkWidget *snap2_rows;
static GtkWidget *chartable;
static GtkListStore *fontcombo_store;
static GtkAdjustment *fontsize_adjustment;
static GtkAccelGroup *accelgroup;
+static uint32_t selected_codepoint = 0u;
+static int switching_groups = 0;
+
#ifdef HAVE_ITEM_TOOLTIPS
/* relying on initialisation to NULL and 0 */
static const char **group_tooltips[NLISTINGS];
@@ -58,6 +63,76 @@ static size_t ngroups_tooltips;
#endif
+static size_t
+format_statusbar_message(char *buffer, size_t size)
+{
+ const struct libcmap_codepoint_data *data;
+ int r;
+ size_t len = 0u;
+
+ r = snprintf(buffer, size, "U+%04"PRIX32, selected_codepoint);
+ if (r < 0)
+ eprintf("snprintf");
+ len += (size_t)r;
+ buffer = &buffer[MIN(size, (size_t)r)];
+ size -= MIN(size, (size_t)r);
+
+ data = libcmap_find_codepoint_data(selected_codepoint);
+ if (!data)
+ return len;
+
+ r = snprintf(buffer, size, " %s", data->name);
+ if (r < 0)
+ eprintf("snprintf");
+ len += (size_t)r;
+ buffer = &buffer[MIN(size, (size_t)r)];
+ size -= MIN(size, (size_t)r);
+
+ return len;
+}
+
+
+static void
+update_statusbar(void)
+{
+ size_t len = format_statusbar_message(NULL, 0u);
+ char *message = emalloc(++len);
+ format_statusbar_message(message, len);
+ gtk_statusbar_pop(GTK_STATUSBAR(statusbar), 0u);
+ gtk_statusbar_push(GTK_STATUSBAR(statusbar), 0u, message);
+ free(message);
+}
+
+
+static void
+character_cell_selected(uint32_t index, uint32_t codepoint, int meta, int copy_to_text_field)
+{
+ GtkEditable *editable;
+ char text[16];
+ int len, pos;
+
+ (void) index;
+
+ text[len = g_unichar_to_utf8(codepoint, text)] = '\0';
+ selected_codepoint = codepoint;
+
+ if (copy_to_text_field) {
+ editable = GTK_EDITABLE(copytext);
+ pos = gtk_editable_get_position(editable);
+ gtk_editable_insert_text(editable, text, len, &pos);
+ gtk_editable_set_position(editable, pos);
+ } else if (meta) {
+ gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_SECONDARY), text, len);
+ /* XXX add setting to swap SECONDARY and PRIMARY */
+ } else {
+ gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY), text, len);
+ /* XXX add setting to disable */
+ }
+
+ update_statusbar();
+}
+
+
static int
populate_font_families(GtkWidget *widget, GtkListStore *store, const char **selectedp)
{
@@ -100,6 +175,18 @@ populate_font_families(GtkWidget *widget, GtkListStore *store, const char **sele
}
+static void
+select_group(int index)
+{
+ GtkTreeView *treeview = GTK_TREE_VIEW(grouplist);
+ GtkTreePath *path = gtk_tree_path_new_from_indices(index, -1);
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
+ gtk_tree_selection_select_path(selection, path);
+ gtk_tree_view_set_cursor(treeview, path, NULL, FALSE);
+ gtk_tree_path_free(path);
+}
+
+
#ifdef HAVE_ITEM_TOOLTIPS
@@ -226,6 +313,9 @@ grouplist_selection_changed(GtkTreeSelection *selection, void *user_data)
(void) user_data;
+ if (switching_groups)
+ return;
+
if (!gtk_tree_selection_get_selected(selection, &model, &iter))
return;
@@ -388,8 +478,17 @@ static void
listcombo_changed(GtkComboBox *combo, GtkListStore *store)
{
static int recursion_guard = 0;
+
GtkWidget *label;
int listing;
+ int all_selected = 1;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ const struct libcmap_block *block;
+ const struct libcmap_script *script;
+ uint32_t codepoint = selected_codepoint;
if (recursion_guard)
return;
@@ -397,6 +496,15 @@ listcombo_changed(GtkComboBox *combo, GtkListStore *store)
listing = gtk_combo_box_get_active(combo);
+ switching_groups++;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(grouplist));
+ if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
+ path = gtk_tree_model_get_path(model, &iter);
+ all_selected = !gtk_tree_path_get_indices(path)[0u];
+ gtk_tree_path_free(path);
+ }
+
switch (listing) {
#define X(ENUM, TITLE, TYPE, SELFUN, POPFUN, ACCEL)\
case ENUM:\
@@ -416,7 +524,24 @@ listcombo_changed(GtkComboBox *combo, GtkListStore *store)
if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(bylisting[listing])))
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(bylisting[listing]), TRUE);
- /* TODO select appropriate list item to keep select character */
+ switching_groups--;
+
+ if (all_selected) {
+ select_group(0);
+ } else {
+ switch ((enum listing)listing) {
+ case BY_BLOCK:
+ block = libcmap_find_block(codepoint, NULL);
+ select_group((block ? (int)(block - libcmap_block_list) : (int)libcmap_block_list_size) + 1);
+ break;
+ case BY_SCRIPT:
+ script = libcmap_find_script(codepoint, NULL, NULL);
+ select_group((script ? (int)(script - libcmap_script_list) : (int)libcmap_script_list_size) + 1);
+ break;
+ default:
+ abort();
+ }
+ }
recursion_guard--;
}
@@ -544,6 +669,24 @@ zoom_original(GtkWidget *menu_item, void *user_data)
}
+static void
+snap2_cols_changed(GtkWidget *menu_item, void *user_data)
+{
+ (void) user_data;
+ gcmap_char_table_set_snap_cols_to_2pow(GCMAP_CHAR_TABLE(chartable),
+ gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu_item)));
+}
+
+
+static void
+snap2_rows_changed(GtkWidget *menu_item, void *user_data)
+{
+ (void) user_data;
+ gcmap_char_table_set_snap_rows_to_2pow(GCMAP_CHAR_TABLE(chartable),
+ gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu_item)));
+}
+
+
static int
zoom_original_accel(GtkAccelGroup *accel_group, GObject *acceleratable, unsigned int keyval,
GdkModifierType modifier, void *user_data)
@@ -557,18 +700,20 @@ zoom_original_accel(GtkAccelGroup *accel_group, GObject *acceleratable, unsigned
}
static void
-go_next(GtkWidget *menu_item, void *user_data) /* TODO */
+go_next(GtkWidget *menu_item, void *user_data)
{
(void) menu_item;
(void) user_data;
+ gcmap_char_table_adjust_cell_selection(GCMAP_CHAR_TABLE(chartable), +1, 0, 0);
}
static void
-go_prev(GtkWidget *menu_item, void *user_data) /* TODO */
+go_prev(GtkWidget *menu_item, void *user_data)
{
(void) menu_item;
(void) user_data;
+ gcmap_char_table_adjust_cell_selection(GCMAP_CHAR_TABLE(chartable), -1, 0, 0);
}
@@ -778,8 +923,8 @@ create_window(const char *default_font, PangoWeight default_weight, PangoStyle d
gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
vtabtext = add_check_menu_item(menu, "_Vertical tab text", 1, 0, 0, &vtabtext_changed);
gtk_widget_set_sensitive(vtabtext, FALSE);
- /* TODO add options to put some tabs in side-pane on the right */
- /* TODO add option for shorter tab texts */
+ /* XXX add options to put some tabs in a side-pane on the right */
+ /* XXX add option for shorter tab texts */
menu = add_menu(menubar, "_View");
group = NULL;
@@ -787,7 +932,7 @@ create_window(const char *default_font, PangoWeight default_weight, PangoStyle d
bylisting[ENUM] = add_radio_menu_item(menu, TITLE, &group, ACCEL, GDK_CONTROL_MASK, &SELFUN)
LIST_LISTINGS(X, ;);
#undef X
- /* TODO add option to use character list (show glyphs with names in rows) instead of character grid */
+ /* XXX add option to use character list (show glyphs with names in rows) instead of character grid */
gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
add_menu_item(menu, "_Larger glyphs", "zoom-in", GDK_plus, GDK_CONTROL_MASK, &zoom_in);
add_menu_item(menu, "S_maller glyphs", "zoom-out", GDK_minus, GDK_CONTROL_MASK, &zoom_out);
@@ -796,6 +941,9 @@ create_window(const char *default_font, PangoWeight default_weight, PangoStyle d
g_cclosure_new(G_CALLBACK(&zoom_original_accel), NULL, NULL));
gtk_accel_group_connect(accelgroup, GDK_1, GDK_CONTROL_MASK, 0,
g_cclosure_new(G_CALLBACK(&zoom_original_accel), NULL, NULL));
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
+ snap2_cols = add_check_menu_item(menu, "Snap _columns to power of 2", 0, GDK_KEY_2, GDK_CONTROL_MASK, &snap2_cols_changed);
+ snap2_rows = add_check_menu_item(menu, "Snap _rows to power of 2", 0, GDK_KEY_3, GDK_CONTROL_MASK, &snap2_rows_changed);
menu = add_menu(menubar, "_Go");
add_menu_item(menu, "_Next character", "go-next", GDK_n, GDK_CONTROL_MASK, &go_next);
@@ -844,7 +992,7 @@ create_window(const char *default_font, PangoWeight default_weight, PangoStyle d
small_font_size_increment, big_font_size_increment, 0);
fontsize_adjustment = GTK_ADJUSTMENT(obj);
sizespinner = gtk_spin_button_new(fontsize_adjustment, 0, 0);
- g_signal_connect(fontsize_adjustment, "value-changed", G_CALLBACK(fontsize_changed), NULL);
+ g_signal_connect(fontsize_adjustment, "value-changed", G_CALLBACK(&fontsize_changed), NULL);
gtk_box_pack_start(GTK_BOX(hbox), sizespinner, FALSE, FALSE, 0);
accessible = gtk_widget_get_accessible(sizespinner);
atk_object_set_name(accessible, "Font size");
@@ -880,7 +1028,6 @@ create_window(const char *default_font, PangoWeight default_weight, PangoStyle d
gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_TOP);
- /* TODO character map tab pane (should have focus: gtk_widget_grab_focus(GTK_WIDGET(...)) */
scrolled = logical_scroll_pane_new();
logical_scroll_pane_set_hscrollbar_visible(LOGICAL_SCROLL_PANE(scrolled), FALSE);
chartable = gcmap_char_table_new(default_font, (int)default_font_size, default_weight, default_style);
@@ -892,7 +1039,7 @@ create_window(const char *default_font, PangoWeight default_weight, PangoStyle d
scrolled = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_NONE);
- gtk_container_add(GTK_CONTAINER(scrolled), gtk_label_new("TODO"));
+ gtk_container_add(GTK_CONTAINER(scrolled), gtk_label_new("todo"));
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, gtk_label_new_with_mnemonic("Character _details"));
#endif
@@ -900,7 +1047,7 @@ create_window(const char *default_font, PangoWeight default_weight, PangoStyle d
scrolled = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_NONE);
- gtk_container_add(GTK_CONTAINER(scrolled), gtk_label_new("TODO"));
+ gtk_container_add(GTK_CONTAINER(scrolled), gtk_label_new("todo"));
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, gtk_label_new_with_mnemonic("Glyph v_ariants"));
#endif
@@ -908,7 +1055,7 @@ create_window(const char *default_font, PangoWeight default_weight, PangoStyle d
scrolled = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_NONE);
- gtk_container_add(GTK_CONTAINER(scrolled), gtk_label_new("TODO"));
+ gtk_container_add(GTK_CONTAINER(scrolled), gtk_label_new("todo"));
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled, gtk_label_new_with_mnemonic("Font c_omparison"));
#endif
@@ -1017,6 +1164,9 @@ main(int argc, char *argv[])
int min_font_size_set = 0;
int max_font_size_set = 0;
const char *font_family = DEFAULT_FONT_FAMILY;
+ const struct libcmap_block *block;
+ const struct libcmap_script *script;
+ char *message;
ARGBEGIN {
case 'B':
@@ -1100,8 +1250,30 @@ main(int argc, char *argv[])
groups_tooltips = group_tooltips[listing];
ngroups_tooltips = ngroup_tooltips[listing];
- /* TODO select character (U+0000) and group (locate the one containing it, go to All if not found) */
+ switch (listing) {
+ case BY_BLOCK:
+ block = libcmap_find_block(selected_codepoint, NULL);
+ select_group(block ? (int)(block - libcmap_block_list) + 1 : 0);
+ break;
+ case BY_SCRIPT:
+ script = libcmap_find_script(selected_codepoint, NULL, NULL);
+ select_group(script ? (int)(script - libcmap_script_list) + 1 : 0);
+ break;
+ default:
+ abort();
+ }
+ GCMAP_CHAR_TABLE(chartable)->selection_callback = &character_cell_selected;
+ gcmap_char_table_select_codepoint(GCMAP_CHAR_TABLE(chartable), selected_codepoint);
+
+ if (libsimple_asprintf(&message, "Using Unicode %s", libcmap_unicode_version) >= 0 && message) {
+ gtk_statusbar_pop(GTK_STATUSBAR(statusbar), 0u);
+ gtk_statusbar_push(GTK_STATUSBAR(statusbar), 0u, message);
+ free(message);
+ } else {
+ update_statusbar();
+ }
gtk_window_present(GTK_WINDOW(window));
+ gtk_widget_grab_focus(GTK_WIDGET(chartable));
gtk_main();
}
diff --git a/logical-scroll-pane.c b/logical-scroll-pane.c
index 207d34e..dd856a6 100644
--- a/logical-scroll-pane.c
+++ b/logical-scroll-pane.c
@@ -5,25 +5,9 @@ G_DEFINE_TYPE(LogicalScrollPane, logical_scroll_pane, GTK_TYPE_BIN)
static void
logical_scroll_pane_adjustment_value_changed(GtkAdjustment *adjustment, LogicalScrollPane *this)
{
-#if defined(__GNUC__)
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
-
- double value;
- if (this->viewport_size_is_logical) {
- value = round(adjustment->value);
- if (value != adjustment->value) {
- gtk_adjustment_set_value(adjustment, value);
- return;
- }
- }
+ (void) adjustment;
if (this->child)
gtk_widget_queue_draw(this->child);
-
-#if defined(__GNUC__)
-# pragma GCC diagnostic pop
-#endif
}
static void
@@ -33,7 +17,7 @@ logical_scroll_pane_configure_adjustment(LogicalScrollPane *this, GtkAdjustment
page_size = MAX(page_size, 1);
pg = (double)page_size;
upper = (double)MAX(content_size - page_size, 0);
- value = this->viewport_size_is_logical ? round(adjustment->value) : adjustment->value;
+ value = adjustment->value;
value = MAX(value, 0.0);
value = MIN(value, upper);
upper = (double)MAX(content_size, page_size);
@@ -203,7 +187,7 @@ logical_scroll_pane_child_scroll(GtkWidget *widget, GdkEventScroll *event, Logic
(void) widget;
- switch (event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_META_MASK)) {
+ switch (event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK)) {
case 0:
switch (event->direction) {
case GDK_SCROLL_LEFT:
@@ -244,7 +228,7 @@ logical_scroll_pane_child_key_press(GtkWidget *widget, GdkEventKey *event, Logic
(void) widget;
- switch (event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_META_MASK)) {
+ switch (event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK)) {
case 0:
break;
case GDK_SHIFT_MASK: