1 /*
2 * Copyright © 2004 Noah Levitt
3 * Copyright © 2007, 2008, 2010 Christian Persch
4 *
5 * Some code copied from gtk+/gtk/gtkiconview:
6 * Copyright © 2002, 2004 Anders Carlsson <andersca@gnu.org>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 3 of the License, or (at your
11 * option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
21 */
22
23 #include <config.h>
24
25 #include <glib/gi18n-lib.h>
26 #include <gtk/gtk.h>
27
28 #include "gucharmap-marshal.h"
29 #include "gucharmap-chartable.h"
30 #include "gucharmap-unicode-info.h"
31 #include "gucharmap-private.h"
32
33 #define ENABLE_ACCESSIBLE
34
35 #ifdef ENABLE_ACCESSIBLE
36 #include "gucharmap-chartable-accessible.h"
37 #endif
38
39 enum
40 {
41 ACTIVATE,
42 STATUS_MESSAGE,
43 MOVE_CURSOR,
44 COPY_CLIPBOARD,
45 PASTE_CLIPBOARD,
46 NUM_SIGNALS
47 };
48
49 enum
50 {
51 PROP_0,
52 PROP_HADJUSTMENT,
53 PROP_VADJUSTMENT,
54 PROP_HSCROLL_POLICY,
55 PROP_VSCROLL_POLICY,
56 PROP_ACTIVE_CHAR,
57 PROP_CODEPOINT_LIST,
58 PROP_FONT_DESC,
59 PROP_FONT_FALLBACK,
60 PROP_SNAP_POW2,
61 PROP_ZOOM_ENABLED,
62 PROP_ZOOM_SHOWING
63 };
64
65 static void gucharmap_chartable_class_init (GucharmapChartableClass *klass);
66 static void gucharmap_chartable_finalize (GObject *object);
67 static void gucharmap_chartable_set_active_cell (GucharmapChartable *chartable,
68 int cell);
69
70 static guint signals[NUM_SIGNALS];
71
72 #define DEFAULT_FONT_SIZE (20.0 * (double) PANGO_SCALE)
73
74 /* These are chosen for compatibility with the older code that
75 * didn't scale the font size by resolution and used 3 and 2.5 here, resp.
76 * Where exactly these factors came from, I don't know.
77 */
78 #define FACTOR_WIDTH (2.25) /* 3 / (96 / 72) */
79 #define FACTOR_HEIGHT (1.875) /* 2.5 / (96 / 72) */
80
81 /** Notes
82 *
83 * 1. Table geometry
84 * The allocated rectangle is divided into ::rows rows and ::col columns,
85 * numbered 0..rows-1 and 0..cols-1.
86 * The available width (height) is divided evenly between all columns (rows).
87 * The remaining space is distributed among the columns (rows) so that
88 * columns cols-n_padded_columns .. cols-1 (rows rows-n_padded_rows .. rows)
89 * are 1px wider (taller) than the others.
90 */
91
92 /* ATK factory */
93
94 #ifdef ENABLE_ACCESSIBLE
95
96 typedef AtkObjectFactory GucharmapChartableAccessibleFactory;
97 typedef AtkObjectFactoryClass GucharmapChartableAccessibleFactoryClass;
98
99 static void
gucharmap_chartable_accessible_factory_init(GucharmapChartableAccessibleFactory * factory)100 gucharmap_chartable_accessible_factory_init (GucharmapChartableAccessibleFactory *factory)
101 {
102 }
103
104 static AtkObject*
gucharmap_chartable_accessible_factory_create_accessible(GObject * obj)105 gucharmap_chartable_accessible_factory_create_accessible (GObject *obj)
106 {
107 return gucharmap_chartable_accessible_new (GUCHARMAP_CHARTABLE (obj));
108 }
109
110 static GType
gucharmap_chartable_accessible_factory_get_accessible_type(void)111 gucharmap_chartable_accessible_factory_get_accessible_type (void)
112 {
113 return gucharmap_chartable_accessible_get_type ();
114 }
115
116 static void
gucharmap_chartable_accessible_factory_class_init(AtkObjectFactoryClass * klass)117 gucharmap_chartable_accessible_factory_class_init (AtkObjectFactoryClass *klass)
118 {
119 klass->create_accessible = gucharmap_chartable_accessible_factory_create_accessible;
120 klass->get_accessible_type = gucharmap_chartable_accessible_factory_get_accessible_type;
121 }
122
123 static GType gucharmap_chartable_accessible_factory_get_type (void);
G_DEFINE_TYPE(GucharmapChartableAccessibleFactory,gucharmap_chartable_accessible_factory,ATK_TYPE_OBJECT_FACTORY)124 G_DEFINE_TYPE (GucharmapChartableAccessibleFactory, gucharmap_chartable_accessible_factory, ATK_TYPE_OBJECT_FACTORY)
125
126 #endif
127
128 /* Type definition */
129
130 G_DEFINE_TYPE_WITH_CODE (GucharmapChartable, gucharmap_chartable, GTK_TYPE_DRAWING_AREA,
131 G_IMPLEMENT_INTERFACE(GTK_TYPE_SCROLLABLE, NULL))
132
133 /* utility functions */
134
135 static void
136 gucharmap_chartable_clear_pango_layout (GucharmapChartable *chartable)
137 {
138 GucharmapChartablePrivate *priv = chartable->priv;
139
140 if (priv->pango_layout == NULL)
141 return;
142 g_object_unref (priv->pango_layout);
143 priv->pango_layout = NULL;
144 }
145
146 static void
gucharmap_chartable_ensure_pango_layout(GucharmapChartable * chartable)147 gucharmap_chartable_ensure_pango_layout (GucharmapChartable *chartable)
148 {
149 GucharmapChartablePrivate *priv = chartable->priv;
150
151 if (priv->pango_layout != NULL)
152 return;
153
154 priv->pango_layout = gtk_widget_create_pango_layout (GTK_WIDGET (chartable), NULL);
155 pango_layout_set_font_description (priv->pango_layout,
156 priv->font_desc);
157
158 if (priv->font_fallback == FALSE)
159 {
160 PangoAttrList *list;
161
162 list = pango_attr_list_new ();
163 pango_attr_list_insert (list, pango_attr_fallback_new (FALSE));
164 pango_layout_set_attributes (priv->pango_layout, list);
165 pango_attr_list_unref (list);
166 }
167 }
168
169 static void
gucharmap_chartable_set_font_desc_internal(GucharmapChartable * chartable,PangoFontDescription * font_desc)170 gucharmap_chartable_set_font_desc_internal (GucharmapChartable *chartable,
171 PangoFontDescription *font_desc /* adopting */)
172 {
173 GucharmapChartablePrivate *priv = chartable->priv;
174 GtkWidget *widget;
175
176 if (priv->font_desc)
177 pango_font_description_free (priv->font_desc);
178
179 priv->font_desc = font_desc;
180
181 gucharmap_chartable_clear_pango_layout (chartable);
182
183 widget = GTK_WIDGET (chartable);
184 if (gtk_widget_get_realized (widget))
185 gtk_widget_queue_resize (widget);
186
187 g_object_notify (G_OBJECT (chartable), "font-desc");
188 }
189
190 static void
gucharmap_chartable_emit_status_message(GucharmapChartable * chartable,const char * message)191 gucharmap_chartable_emit_status_message (GucharmapChartable *chartable,
192 const char *message)
193 {
194 g_signal_emit (chartable, signals[STATUS_MESSAGE], 0, message);
195 }
196
197 typedef enum {
198 POSITION_DOWN_ALIGN_LEFT,
199 POSITION_DOWN_ALIGN_RIGHT,
200 POSITION_RIGHT_ALIGN_TOP,
201 POSITION_RIGHT_ALIGN_BOTTOM,
202 POSITION_TOP_ALIGN_LEFT,
203 POSITION_TOP_ALIGN_RIGHT,
204 POSITION_LEFT_ALIGN_TOP,
205 POSITION_LEFT_ALIGN_BOTTOM
206 } PositionType;
207
208 static const PositionType rtl_position[] = {
209 POSITION_DOWN_ALIGN_RIGHT,
210 POSITION_DOWN_ALIGN_LEFT,
211 POSITION_LEFT_ALIGN_TOP,
212 POSITION_LEFT_ALIGN_BOTTOM,
213 POSITION_TOP_ALIGN_RIGHT,
214 POSITION_TOP_ALIGN_LEFT,
215 POSITION_RIGHT_ALIGN_TOP,
216 POSITION_RIGHT_ALIGN_BOTTOM
217 };
218
219 /**
220 * position_rectangle:
221 * @rect: the rectangle to position. Inout; width and height must be initialised
222 * @target_rect: the rectangle to position @rect on
223 * @bounding_rect: the bounding rectangle
224 * @position: how to position the rectangle
225 * @direction: the text direction
226 *
227 * Returns: %TRUE if @rect could be positioned on @reference_point
228 * with positioning according to @gravity inside @bounding_rect
229 */
230 static gboolean
position_rectangle(GdkRectangle * position_rect,GdkRectangle * target_rect,GdkRectangle * bounding_rect,PositionType position,GtkTextDirection direction)231 position_rectangle (GdkRectangle *position_rect,
232 GdkRectangle *target_rect,
233 GdkRectangle *bounding_rect,
234 PositionType position,
235 GtkTextDirection direction)
236 {
237 GdkRectangle rect;
238
239 if (direction == GTK_TEXT_DIR_RTL) {
240 position = rtl_position[position];
241 }
242
243 rect.x = target_rect->x;
244 rect.y = target_rect->y;
245 rect.width = position_rect->width;
246 rect.height = position_rect->height;
247
248 switch (position) {
249 case POSITION_DOWN_ALIGN_RIGHT:
250 rect.x -= rect.width - target_rect->width;
251 /* fall-through */
252 case POSITION_DOWN_ALIGN_LEFT:
253 rect.y += target_rect->height;
254 break;
255
256 case POSITION_RIGHT_ALIGN_BOTTOM:
257 rect.y -= rect.height - target_rect->height;
258 /* fall-through */
259 case POSITION_RIGHT_ALIGN_TOP:
260 rect.x += target_rect->width;
261 break;
262
263 case POSITION_TOP_ALIGN_RIGHT:
264 rect.x -= rect.width - target_rect->width;
265 /* fall-through */
266 case POSITION_TOP_ALIGN_LEFT:
267 rect.y -= rect.height;
268 break;
269
270 case POSITION_LEFT_ALIGN_BOTTOM:
271 rect.y -= rect.height - target_rect->height;
272 /* fall-through */
273 case POSITION_LEFT_ALIGN_TOP:
274 rect.x -= rect.width;
275 break;
276 }
277
278 *position_rect = rect;
279
280 return rect.x >= bounding_rect->x &&
281 rect.y >= bounding_rect->y &&
282 rect.x + rect.width <= bounding_rect->x + bounding_rect->width &&
283 rect.y + rect.height <= bounding_rect->y + bounding_rect->height;
284 }
285
286 static gboolean
position_rectangle_on_screen(GtkWidget * widget,GdkRectangle * rectangle,GdkRectangle * target_rect)287 position_rectangle_on_screen (GtkWidget *widget,
288 GdkRectangle *rectangle,
289 GdkRectangle *target_rect)
290 {
291 GtkTextDirection direction;
292 GdkRectangle monitor;
293 int monitor_num;
294 GdkScreen *screen;
295 static const PositionType positions[] = {
296 POSITION_DOWN_ALIGN_LEFT,
297 POSITION_TOP_ALIGN_LEFT,
298 POSITION_RIGHT_ALIGN_TOP,
299 POSITION_LEFT_ALIGN_TOP,
300 POSITION_DOWN_ALIGN_RIGHT,
301 POSITION_TOP_ALIGN_RIGHT,
302 POSITION_RIGHT_ALIGN_BOTTOM,
303 POSITION_LEFT_ALIGN_BOTTOM
304 };
305 guint i;
306
307 direction = gtk_widget_get_direction (widget);
308 screen = gtk_widget_get_screen (widget);
309 monitor_num = gdk_screen_get_monitor_at_window (screen, gtk_widget_get_window (widget));
310 if (monitor_num < 0)
311 monitor_num = 0;
312 #if GTK_CHECK_VERSION (3, 3, 5)
313 gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
314 #else
315 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
316 #endif
317
318 for (i = 0; i < G_N_ELEMENTS (positions); ++i) {
319 if (position_rectangle (rectangle, target_rect, &monitor, positions[i], direction))
320 return TRUE;
321 }
322
323 return FALSE;
324 }
325
326 static void
get_root_coords_at_active_char(GucharmapChartable * chartable,gint * x_root,gint * y_root)327 get_root_coords_at_active_char (GucharmapChartable *chartable,
328 gint *x_root,
329 gint *y_root)
330 {
331 GucharmapChartablePrivate *priv = chartable->priv;
332 GtkWidget *widget = GTK_WIDGET (chartable);
333 gint x, y;
334 gint row, col;
335
336 gdk_window_get_origin (gtk_widget_get_window (widget), &x, &y);
337
338 row = (priv->active_cell - priv->page_first_cell) / priv->cols;
339 col = _gucharmap_chartable_cell_column (chartable, priv->active_cell);
340
341 *x_root = x + _gucharmap_chartable_x_offset (chartable, col);
342 *y_root = y + _gucharmap_chartable_y_offset (chartable, row);
343 }
344
345 static void
get_active_cell_rect(GucharmapChartable * chartable,GdkRectangle * rect)346 get_active_cell_rect (GucharmapChartable *chartable, GdkRectangle *rect)
347 {
348 GucharmapChartablePrivate *priv = chartable->priv;
349 int row, col;
350
351 get_root_coords_at_active_char (chartable, &rect->x, &rect->y);
352
353 row = (priv->active_cell - priv->page_first_cell) / priv->cols;
354 col = _gucharmap_chartable_cell_column (chartable, priv->active_cell);
355
356 rect->width = _gucharmap_chartable_column_width (chartable, col);
357 rect->height = _gucharmap_chartable_row_height (chartable, row);
358 }
359
360 static void
get_appropriate_upper_left_xy(GucharmapChartable * chartable,gint width,gint height,gint x_root,gint y_root,gint * x,gint * y)361 get_appropriate_upper_left_xy (GucharmapChartable *chartable,
362 gint width, gint height,
363 gint x_root, gint y_root,
364 gint *x, gint *y)
365 {
366 GucharmapChartablePrivate *priv = chartable->priv;
367 gint row, col;
368
369 row = (priv->active_cell - priv->page_first_cell) / priv->cols;
370 col = _gucharmap_chartable_cell_column (chartable, priv->active_cell);
371
372 *x = x_root;
373 *y = y_root;
374
375 if (row >= priv->rows / 2)
376 *y -= height;
377
378 if (col >= priv->cols / 2)
379 *x -= width;
380 }
381
382 /* depends on directionality */
383 static guint
get_cell_at_rowcol(GucharmapChartable * chartable,gint row,gint col)384 get_cell_at_rowcol (GucharmapChartable *chartable,
385 gint row,
386 gint col)
387 {
388 GucharmapChartablePrivate *priv = chartable->priv;
389 GtkWidget *widget = GTK_WIDGET (chartable);
390
391 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
392 return priv->page_first_cell + row * priv->cols + (priv->cols - col - 1);
393 else
394 return priv->page_first_cell + row * priv->cols + col;
395 }
396
397 /* Depends on directionality. Column 0 is the furthest left. */
398 gint
_gucharmap_chartable_cell_column(GucharmapChartable * chartable,guint cell)399 _gucharmap_chartable_cell_column (GucharmapChartable *chartable,
400 guint cell)
401 {
402 GucharmapChartablePrivate *priv = chartable->priv;
403 GtkWidget *widget = GTK_WIDGET (chartable);
404
405 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
406 return priv->cols - (cell - priv->page_first_cell) % priv->cols - 1;
407 else
408 return (cell - priv->page_first_cell) % priv->cols;
409 }
410
411 /* not all columns are necessarily the same width because of padding */
412 gint
_gucharmap_chartable_column_width(GucharmapChartable * chartable,gint col)413 _gucharmap_chartable_column_width (GucharmapChartable *chartable, gint col)
414 {
415 GucharmapChartablePrivate *priv = chartable->priv;
416 int num_padded_columns = priv->n_padded_columns;
417 int min_col_w = priv->minimal_column_width;
418
419 if (priv->cols - col <= num_padded_columns)
420 return min_col_w + 1;
421 else
422 return min_col_w;
423 }
424
425 /* calculates the position of the left end of the column (just to the right
426 * of the left border) */
427 /* XXX: calling this repeatedly is not the most efficient, but it probably
428 * is the most readable */
429 gint
_gucharmap_chartable_x_offset(GucharmapChartable * chartable,gint col)430 _gucharmap_chartable_x_offset (GucharmapChartable *chartable, gint col)
431 {
432 gint c, x;
433
434 for (c = 0, x = 1; c < col; c++)
435 x += _gucharmap_chartable_column_width (chartable, c);
436
437 return x;
438 }
439
440 /* not all rows are necessarily the same height because of padding */
441 gint
_gucharmap_chartable_row_height(GucharmapChartable * chartable,gint row)442 _gucharmap_chartable_row_height (GucharmapChartable *chartable, gint row)
443 {
444 GucharmapChartablePrivate *priv = chartable->priv;
445 int num_padded_rows = priv->n_padded_rows;
446 int min_row_h = priv->minimal_row_height;
447
448 if (priv->rows - row <= num_padded_rows)
449 return min_row_h + 1;
450 else
451 return min_row_h;
452 }
453
454 /* calculates the position of the top end of the row (just below the top
455 * border) */
456 /* XXX: calling this repeatedly is not the most efficient, but it probably
457 * is the most readable */
458 gint
_gucharmap_chartable_y_offset(GucharmapChartable * chartable,gint row)459 _gucharmap_chartable_y_offset (GucharmapChartable *chartable, gint row)
460 {
461 gint r, y;
462
463 for (r = 0, y = 1; r < row; r++)
464 y += _gucharmap_chartable_row_height (chartable, r);
465
466 return y;
467 }
468
469 /* returns the font family of the last glyph item in the first line of the
470 * layout; should be freed by caller */
471 static gchar *
get_font(PangoLayout * layout)472 get_font (PangoLayout *layout)
473 {
474 PangoLayoutLine *line;
475 PangoGlyphItem *glyph_item;
476 PangoFont *font;
477 GSList *run_node;
478 gchar *family;
479 PangoFontDescription *font_desc;
480
481 line = pango_layout_get_line (layout, 0);
482
483 /* get to the last glyph_item (the one with the character we're drawing */
484 for (run_node = line->runs;
485 run_node && run_node->next;
486 run_node = run_node->next);
487
488 if (run_node)
489 {
490 glyph_item = run_node->data;
491 font = glyph_item->item->analysis.font;
492 font_desc = pango_font_describe (font);
493
494 family = g_strdup (pango_font_description_get_family (font_desc));
495
496 pango_font_description_free (font_desc);
497 }
498 else
499 family = NULL;
500
501 return family;
502 }
503
504 /* font_family (if not null) gets filled in with the actual font family
505 * used to draw the character */
506 static PangoLayout *
layout_scaled_glyph(GucharmapChartable * chartable,gunichar uc,double font_factor,char ** font_family)507 layout_scaled_glyph (GucharmapChartable *chartable,
508 gunichar uc,
509 double font_factor,
510 char **font_family)
511 {
512 GucharmapChartablePrivate *priv = chartable->priv;
513 PangoFontDescription *font_desc;
514 PangoLayout *layout;
515 gchar buf[11];
516
517 font_desc = pango_font_description_copy (priv->font_desc);
518
519 if (pango_font_description_get_size_is_absolute (priv->font_desc))
520 pango_font_description_set_absolute_size (font_desc,
521 font_factor * pango_font_description_get_size (priv->font_desc));
522 else
523 pango_font_description_set_size (font_desc,
524 font_factor * pango_font_description_get_size (priv->font_desc));
525
526 gucharmap_chartable_ensure_pango_layout (chartable);
527 layout = pango_layout_new (pango_layout_get_context (priv->pango_layout));
528
529 pango_layout_set_font_description (layout, font_desc);
530
531 buf[gucharmap_unichar_to_printable_utf8 (uc, buf)] = '\0';
532 pango_layout_set_text (layout, buf, -1);
533
534 if (priv->font_fallback == FALSE)
535 {
536 PangoAttrList *list;
537
538 list = pango_attr_list_new ();
539 pango_attr_list_insert (list, pango_attr_fallback_new (FALSE));
540 pango_layout_set_attributes (layout, list);
541 pango_attr_list_unref (list);
542 }
543
544 if (font_family)
545 *font_family = get_font (layout);
546
547 pango_font_description_free (font_desc);
548
549 return layout;
550 }
551
552 static cairo_surface_t *
create_glyph_surface(GucharmapChartable * chartable,gunichar wc,double font_factor,gboolean draw_font_family,int * zoom_surface_width,int * zoom_surface_height)553 create_glyph_surface (GucharmapChartable *chartable,
554 gunichar wc,
555 double font_factor,
556 gboolean draw_font_family,
557 int *zoom_surface_width,
558 int *zoom_surface_height)
559 {
560 GtkWidget *widget = GTK_WIDGET (chartable);
561 enum { PADDING = 8 };
562
563 PangoLayout *pango_layout, *pango_layout2 = NULL;
564 PangoRectangle char_rect, family_rect;
565 gint width, height;
566 GtkStyle *style;
567 char *family;
568 cairo_surface_t *surface;
569 cairo_t *cr;
570
571 /* Apply the scaling. Unfortunately not all fonts seem to be scalable.
572 * We could fall back to GdkPixbuf scaling, but that looks butt ugly :-/
573 */
574 pango_layout = layout_scaled_glyph (chartable, wc,
575 font_factor, &family);
576 pango_layout_get_pixel_extents (pango_layout, &char_rect, NULL);
577
578 if (draw_font_family)
579 {
580 if (family == NULL)
581 family = g_strdup (_("[not a printable character]"));
582
583 pango_layout2 = gtk_widget_create_pango_layout (GTK_WIDGET (chartable), family);
584 pango_layout_get_pixel_extents (pango_layout2, NULL, &family_rect);
585
586 /* Make the GdkPixmap large enough to account for possible offsets in the
587 * ink extents of the glyph. */
588 width = MAX (char_rect.width, family_rect.width) + 2 * PADDING;
589 height = family_rect.height + char_rect.height + 4 * PADDING;
590 }
591 else
592 {
593 width = char_rect.width + 2 * PADDING;
594 height = char_rect.height + 2 * PADDING;
595 }
596
597 style = gtk_widget_get_style (widget);
598
599 surface = gdk_window_create_similar_surface (gtk_widget_get_window (widget),
600 CAIRO_CONTENT_COLOR,
601 width, height);
602 cr = cairo_create (surface);
603
604 gdk_cairo_set_source_color (cr, &style->base[GTK_STATE_NORMAL]);
605 cairo_rectangle (cr, 0, 0, width, height);
606 cairo_fill (cr);
607
608 gdk_cairo_set_source_color (cr, &style->fg[GTK_STATE_INSENSITIVE]);
609 cairo_set_line_width (cr, 1);
610 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
611 cairo_rectangle (cr, 1.5, 1.5, width - 3, height - 3);
612 cairo_stroke (cr);
613
614 /* Now draw the glyph. The coordinates are adapted
615 * in order to compensate negative char_rect offsets.
616 */
617 gdk_cairo_set_source_color (cr, &style->text[GTK_STATE_NORMAL]);
618 cairo_move_to (cr, -char_rect.x + PADDING, -char_rect.y + PADDING);
619 pango_cairo_show_layout (cr, pango_layout);
620 g_object_unref (pango_layout);
621
622 if (draw_font_family)
623 {
624 cairo_set_line_width (cr, 1);
625 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
626 gdk_cairo_set_source_color (cr, &style->dark[GTK_STATE_NORMAL]);
627 cairo_move_to (cr, 6 + 1 + .5, char_rect.height + 2 * PADDING + .5);
628 cairo_line_to (cr, width - 3 - 6 - .5, char_rect.height + 2 * PADDING + .5);
629 cairo_stroke (cr);
630
631 gdk_cairo_set_source_color (cr, &style->text[GTK_STATE_NORMAL]);
632 cairo_move_to (cr, PADDING, height - PADDING - family_rect.height);
633 /* FIXME: clip here? */
634 pango_cairo_show_layout (cr, pango_layout2);
635
636 g_object_unref (pango_layout2);
637 }
638
639 g_free (family);
640
641 cairo_destroy (cr);
642
643 if (zoom_surface_width)
644 *zoom_surface_width = width;
645 if (zoom_surface_height)
646 *zoom_surface_height = height;
647
648 return surface;
649 }
650
651 /* places the zoom window toward the inside of the coordinates */
652 static void
place_zoom_window(GucharmapChartable * chartable,gint x_root,gint y_root)653 place_zoom_window (GucharmapChartable *chartable, gint x_root, gint y_root)
654 {
655 GucharmapChartablePrivate *priv = chartable->priv;
656 int x, y;
657
658 if (!priv->zoom_window)
659 return;
660
661 get_appropriate_upper_left_xy (chartable,
662 priv->zoom_image_width,
663 priv->zoom_image_height,
664 x_root, y_root, &x, &y);
665 gtk_window_move (GTK_WINDOW (priv->zoom_window), x, y);
666 }
667
668 static void
place_zoom_window_on_active_cell(GucharmapChartable * chartable)669 place_zoom_window_on_active_cell (GucharmapChartable *chartable)
670 {
671 GucharmapChartablePrivate *priv = chartable->priv;
672 GdkRectangle rect, keepout_rect;
673
674 if (!priv->zoom_window)
675 return;
676
677 get_active_cell_rect (chartable, &keepout_rect);
678
679 rect.x = rect.y = 0;
680 rect.width = priv->zoom_image_width;
681 rect.height = priv->zoom_image_height;
682
683 position_rectangle_on_screen (GTK_WIDGET (chartable),
684 &rect,
685 &keepout_rect);
686 gtk_window_move (GTK_WINDOW (priv->zoom_window), rect.x, rect.y);
687 }
688
689 static int
get_font_size_px(GucharmapChartable * chartable)690 get_font_size_px (GucharmapChartable *chartable)
691 {
692 GucharmapChartablePrivate *priv = chartable->priv;
693 GtkWidget *widget = GTK_WIDGET (chartable);
694 GdkScreen *screen;
695 double resolution;
696 int font_size;
697
698 g_assert (priv->font_desc != NULL);
699
700 screen = gtk_widget_get_screen (widget);
701 resolution = gdk_screen_get_resolution (screen);
702 if (resolution < 0.0) /* will be -1 if the resolution is not defined in the GdkScreen */
703 resolution = 96.0;
704
705 if (pango_font_description_get_size_is_absolute (priv->font_desc))
706 font_size = pango_font_description_get_size (priv->font_desc);
707 else
708 font_size = ((double) pango_font_description_get_size (priv->font_desc)) * resolution / 72.0;
709
710 if (PANGO_PIXELS (font_size) <= 0)
711 font_size = DEFAULT_FONT_SIZE * resolution / 72.0;
712
713 return PANGO_PIXELS (font_size);
714 }
715
716 static void
update_zoom_window(GucharmapChartable * chartable)717 update_zoom_window (GucharmapChartable *chartable)
718 {
719 GucharmapChartablePrivate *priv = chartable->priv;
720 GtkWidget *widget = GTK_WIDGET (chartable);
721 double scale;
722 int font_size_px, screen_height;
723 cairo_surface_t *surface;
724
725 if (priv->zoom_window == NULL)
726 return;
727
728 font_size_px = get_font_size_px (chartable);
729 screen_height = gdk_screen_get_height (gtk_widget_get_screen (widget));
730
731 scale = (0.3 * screen_height) / (FACTOR_WIDTH * font_size_px);
732 scale = CLAMP (scale, 1.0, 12.0);
733
734 surface = create_glyph_surface (chartable,
735 gucharmap_chartable_get_active_character (chartable),
736 scale, TRUE,
737 &priv->zoom_image_width,
738 &priv->zoom_image_height);
739 gtk_image_set_from_surface (GTK_IMAGE (gtk_bin_get_child (GTK_BIN (priv->zoom_window))),
740 surface);
741 cairo_surface_destroy (surface);
742
743 gtk_window_resize (GTK_WINDOW (priv->zoom_window),
744 priv->zoom_image_width, priv->zoom_image_height);
745 }
746
747 static void
make_zoom_window(GucharmapChartable * chartable)748 make_zoom_window (GucharmapChartable *chartable)
749 {
750 GucharmapChartablePrivate *priv = chartable->priv;
751 GtkWidget *widget = GTK_WIDGET (chartable);
752 GtkWidget *image;
753
754 /* if there is already a zoom window, do nothing */
755 if (priv->zoom_window || !priv->zoom_mode_enabled)
756 return;
757
758 priv->zoom_window = gtk_window_new (GTK_WINDOW_POPUP);
759 /* For wayland, we need to "attach" the popup to the toplevel */
760 gtk_window_set_transient_for (GTK_WINDOW (priv->zoom_window),
761 GTK_WINDOW (gtk_widget_get_toplevel (widget)));
762 gtk_window_set_resizable (GTK_WINDOW (priv->zoom_window), FALSE);
763 gtk_window_set_screen (GTK_WINDOW (priv->zoom_window),
764 gtk_widget_get_screen (widget));
765
766 image = gtk_image_new ();
767 gtk_container_add (GTK_CONTAINER (priv->zoom_window), image);
768 gtk_widget_show (image);
769 }
770
771 static void
destroy_zoom_window(GucharmapChartable * chartable)772 destroy_zoom_window (GucharmapChartable *chartable)
773 {
774 GucharmapChartablePrivate *priv = chartable->priv;
775
776 if (priv->zoom_window)
777 {
778 GtkWidget *widget = GTK_WIDGET (chartable);
779 GtkWidget *zoom_window;
780
781 zoom_window = priv->zoom_window;
782 priv->zoom_window = NULL;
783
784 gdk_window_set_cursor (gtk_widget_get_window (widget), NULL);
785 gtk_widget_destroy (zoom_window);
786 }
787 }
788
789 static void
gucharmap_chartable_show_zoom(GucharmapChartable * chartable)790 gucharmap_chartable_show_zoom (GucharmapChartable *chartable)
791 {
792 GucharmapChartablePrivate *priv = chartable->priv;
793
794 if (!priv->zoom_mode_enabled)
795 return;
796
797 make_zoom_window (chartable);
798 update_zoom_window (chartable);
799
800 place_zoom_window_on_active_cell (chartable);
801
802 gtk_widget_show (priv->zoom_window);
803
804 g_object_notify (G_OBJECT (chartable), "zoom-showing");
805 }
806
807 static void
gucharmap_chartable_hide_zoom(GucharmapChartable * chartable)808 gucharmap_chartable_hide_zoom (GucharmapChartable *chartable)
809 {
810 destroy_zoom_window (chartable);
811
812 g_object_notify (G_OBJECT (chartable), "zoom-showing");
813 }
814
815 static gunichar
get_cell_at_xy(GucharmapChartable * chartable,gint x,gint y)816 get_cell_at_xy (GucharmapChartable *chartable,
817 gint x,
818 gint y)
819 {
820 GucharmapChartablePrivate *priv = chartable->priv;
821 gint r, c, x0, y0;
822 guint cell;
823
824 for (c = 0, x0 = 0; x0 <= x && c < priv->cols; c++)
825 x0 += _gucharmap_chartable_column_width (chartable, c);
826
827 for (r = 0, y0 = 0; y0 <= y && r < priv->rows; r++)
828 y0 += _gucharmap_chartable_row_height (chartable, r);
829
830 /* cell = rowcol_to_unichar (chartable, r-1, c-1); */
831 cell = get_cell_at_rowcol (chartable, r-1, c-1);
832
833 /* XXX: check this somewhere else? */
834 if (cell > priv->last_cell)
835 return priv->last_cell;
836
837 return cell;
838 }
839
840 static void
draw_character(GucharmapChartable * chartable,cairo_t * cr,cairo_rectangle_int_t * rect,gint row,gint col)841 draw_character (GucharmapChartable *chartable,
842 cairo_t *cr,
843 cairo_rectangle_int_t *rect,
844 gint row,
845 gint col)
846 {
847 GucharmapChartablePrivate *priv = chartable->priv;
848 GtkWidget *widget = GTK_WIDGET (chartable);
849 int n, char_width, char_height;
850 gunichar wc;
851 guint cell;
852 GtkStyle *style;
853 GdkColor *color;
854 gchar buf[10];
855
856 cell = get_cell_at_rowcol (chartable, row, col);
857 wc = gucharmap_codepoint_list_get_char (priv->codepoint_list, cell);
858
859 if (wc > UNICHAR_MAX ||
860 !gucharmap_unichar_validate (wc) ||
861 !gucharmap_unichar_isdefined (wc))
862 return;
863
864 n = gucharmap_unichar_to_printable_utf8 (wc, buf);
865 pango_layout_set_text (priv->pango_layout, buf, n);
866
867 /* Keep the square empty if font fallback is disabled and the
868 * font has no glyph for this cell.
869 */
870 if (!priv->font_fallback &&
871 pango_layout_get_unknown_glyphs_count (priv->pango_layout) > 0)
872 return;
873
874 cairo_save (cr);
875
876 style = gtk_widget_get_style (widget);
877
878 if (gtk_widget_has_focus (widget) && (gint)cell == priv->active_cell)
879 color = &style->text[GTK_STATE_SELECTED];
880 else if ((gint)cell == priv->active_cell)
881 color = &style->text[GTK_STATE_ACTIVE];
882 else
883 color = &style->text[GTK_STATE_NORMAL];
884
885 gdk_cairo_set_source_color (cr, color);
886
887 cairo_rectangle (cr,
888 rect->x + 1, rect->y + 1,
889 rect->width - 2, rect->height - 2);
890 cairo_clip (cr);
891
892 pango_layout_get_pixel_size (priv->pango_layout, &char_width, &char_height);
893 cairo_move_to (cr,
894 rect->x + (rect->width - char_width - 2 + 1) / 2,
895 rect->y + (rect->height - char_height - 2 + 1) / 2);
896 pango_cairo_show_layout (cr, priv->pango_layout);
897
898 cairo_restore (cr);
899 }
900
901 static void
expose_square(GucharmapChartable * chartable,gint row,gint col)902 expose_square (GucharmapChartable *chartable, gint row, gint col)
903 {
904 GtkWidget *widget = GTK_WIDGET (chartable);
905
906 gtk_widget_queue_draw_area (widget,
907 _gucharmap_chartable_x_offset (chartable, col),
908 _gucharmap_chartable_y_offset (chartable, row),
909 _gucharmap_chartable_column_width (chartable, col) - 1,
910 _gucharmap_chartable_row_height (chartable, row) - 1);
911 }
912
913 static void
expose_cell(GucharmapChartable * chartable,guint cell)914 expose_cell (GucharmapChartable *chartable,
915 guint cell)
916 {
917 GucharmapChartablePrivate *priv = chartable->priv;
918
919 gint row = (cell - priv->page_first_cell) / priv->cols;
920 gint col = _gucharmap_chartable_cell_column (chartable, cell);
921
922 if (row >= 0 && row < priv->rows && col >= 0 && col < priv->cols)
923 expose_square (chartable, row, col);
924 }
925
926 static void
draw_square_bg(GucharmapChartable * chartable,cairo_t * cr,cairo_rectangle_int_t * rect,gint row,gint col)927 draw_square_bg (GucharmapChartable *chartable,
928 cairo_t *cr,
929 cairo_rectangle_int_t *rect,
930 gint row,
931 gint col)
932 {
933 GucharmapChartablePrivate *priv = chartable->priv;
934 GtkWidget *widget = GTK_WIDGET (chartable);
935 GdkColor *untinted;
936 GtkStyle *style;
937 guint cell;
938 gunichar wc;
939
940 cairo_save (cr);
941
942 cell = get_cell_at_rowcol (chartable, row, col);
943 wc = gucharmap_codepoint_list_get_char (priv->codepoint_list, cell);
944
945 style = gtk_widget_get_style (widget);
946
947 if (gtk_widget_has_focus (widget) && (gint)cell == priv->active_cell)
948 untinted = &style->base[GTK_STATE_SELECTED];
949 else if ((gint)cell == priv->active_cell)
950 untinted = &style->base[GTK_STATE_ACTIVE];
951 else if ((gint)cell > priv->last_cell)
952 untinted = &style->dark[GTK_STATE_NORMAL];
953 else if (! gucharmap_unichar_validate (wc))
954 untinted = &style->fg[GTK_STATE_INSENSITIVE];
955 else if (! gucharmap_unichar_isdefined (wc))
956 untinted = &style->bg[GTK_STATE_INSENSITIVE];
957 else
958 untinted = &style->base[GTK_STATE_NORMAL];
959
960 gdk_cairo_set_source_color (cr, untinted);
961 cairo_set_line_width (cr, 1);
962 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
963
964 cairo_rectangle (cr, rect->x, rect->y, rect->width, rect->height);
965 cairo_fill (cr);
966
967 cairo_restore (cr);
968 }
969
970 static void
draw_borders(GucharmapChartable * chartable,cairo_t * cr)971 draw_borders (GucharmapChartable *chartable,
972 cairo_t *cr)
973 {
974 GucharmapChartablePrivate *priv = chartable->priv;
975 GtkWidget *widget = GTK_WIDGET (chartable);
976 GtkAllocation *allocation;
977 GtkStyle *style;
978 gint x, y, col, row;
979 GtkAllocation widget_allocation;
980
981 gtk_widget_get_allocation (widget, &widget_allocation);
982 allocation = &widget_allocation;
983
984 cairo_save (cr);
985
986 /* dark_gc[GTK_STATE_NORMAL] seems to be what is used to draw the borders
987 * around widgets, so we use it for the lines */
988
989 style = gtk_widget_get_style (widget);
990 gdk_cairo_set_source_color (cr, &style->dark[GTK_STATE_NORMAL]);
991
992 cairo_set_line_width (cr, 1); /* FIXME themeable? */
993 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
994
995 /* vertical lines */
996 cairo_move_to (cr, .5, .5);
997 cairo_line_to (cr, .5, allocation->height - .5);
998
999 for (col = 0, x = 0; col < priv->cols; col++)
1000 {
1001 x += _gucharmap_chartable_column_width (chartable, col);
1002 cairo_move_to (cr, x + .5, .5);
1003 cairo_line_to (cr, x + .5, allocation->height - .5);
1004 }
1005
1006 /* horizontal lines */
1007 cairo_move_to (cr, .5, .5);
1008 cairo_line_to (cr, allocation->width - .5, .5);
1009
1010 for (row = 0, y = 0; row < priv->rows; row++)
1011 {
1012 y += _gucharmap_chartable_row_height (chartable, row);
1013
1014 cairo_move_to (cr, .5, y + .5);
1015 cairo_line_to (cr, allocation->width - .5, y + .5);
1016 }
1017
1018 cairo_stroke (cr);
1019 cairo_restore (cr);
1020 }
1021
1022 static void
update_scrollbar_adjustment(GucharmapChartable * chartable)1023 update_scrollbar_adjustment (GucharmapChartable *chartable)
1024 {
1025 GucharmapChartablePrivate *priv = chartable->priv;
1026 GtkAdjustment *vadjustment = priv->vadjustment;
1027
1028 if (!vadjustment)
1029 return;
1030
1031 gtk_adjustment_configure (vadjustment,
1032 priv->page_first_cell / priv->cols,
1033 0 /* lower */,
1034 priv->last_cell / priv->cols + 1 /* upper */,
1035 3 /* step increment */,
1036 priv->rows /* page increment */,
1037 priv->rows);
1038 }
1039
1040 static void
gucharmap_chartable_set_active_cell(GucharmapChartable * chartable,int cell)1041 gucharmap_chartable_set_active_cell (GucharmapChartable *chartable,
1042 int cell)
1043 {
1044 GtkWidget *widget = GTK_WIDGET (chartable);
1045 GucharmapChartablePrivate *priv = chartable->priv;
1046 int old_active_cell, old_page_first_cell;
1047
1048 if (cell == priv->active_cell)
1049 return;
1050
1051 if (cell < 0)
1052 cell = 0;
1053 else if (cell > priv->last_cell)
1054 cell = priv->last_cell;
1055
1056 old_active_cell = priv->active_cell;
1057 old_page_first_cell = priv->page_first_cell;
1058
1059 priv->active_cell = cell;
1060
1061 if (cell < priv->page_first_cell || cell >= priv->page_first_cell + priv->page_size)
1062 {
1063 int old_row = old_active_cell / priv->cols;
1064 int new_row = cell / priv->cols;
1065 int new_page_first_cell = old_page_first_cell + (new_row - old_row) * priv->cols;
1066 int last_page_first_cell = (priv->last_cell / priv->cols - priv->rows + 1) * priv->cols;
1067
1068 priv->page_first_cell = CLAMP (new_page_first_cell, 0, last_page_first_cell);
1069
1070 if (priv->vadjustment)
1071 gtk_adjustment_set_value (priv->vadjustment, priv->page_first_cell / priv->cols);
1072 }
1073 else if (gtk_widget_get_realized (widget)) {
1074 expose_cell (chartable, old_active_cell);
1075 expose_cell (chartable, cell);
1076 }
1077
1078 g_object_notify (G_OBJECT (chartable), "active-character");
1079
1080 update_zoom_window (chartable);
1081 place_zoom_window_on_active_cell (chartable);
1082 }
1083
1084 static void
set_active_char(GucharmapChartable * chartable,gunichar wc)1085 set_active_char (GucharmapChartable *chartable,
1086 gunichar wc)
1087 {
1088 GucharmapChartablePrivate *priv = chartable->priv;
1089
1090 guint cell = gucharmap_codepoint_list_get_index (priv->codepoint_list, wc);
1091 if (cell == -1) {
1092 gtk_widget_error_bell (GTK_WIDGET (chartable));
1093 return;
1094 }
1095
1096 gucharmap_chartable_set_active_cell (chartable, cell);
1097 }
1098
1099 static void
vadjustment_value_changed_cb(GtkAdjustment * vadjustment,GucharmapChartable * chartable)1100 vadjustment_value_changed_cb (GtkAdjustment *vadjustment,
1101 GucharmapChartable *chartable)
1102 {
1103 GucharmapChartablePrivate *priv = chartable->priv;
1104 int row, r, c, old_page_first_cell, old_active_cell, first_cell;
1105
1106 row = (int) gtk_adjustment_get_value (vadjustment);
1107
1108 if (row < 0 || row > priv->last_cell / priv->cols)
1109 row = 0;
1110
1111 first_cell = row * priv->cols;
1112
1113 gtk_widget_queue_draw (GTK_WIDGET (chartable));
1114
1115 old_page_first_cell = priv->page_first_cell;
1116 old_active_cell = priv->active_cell;
1117
1118 priv->page_first_cell = first_cell;
1119
1120 /* character is still on the visible page */
1121 if (priv->active_cell - priv->page_first_cell >= 0
1122 && priv->active_cell - priv->page_first_cell < priv->page_size)
1123 return;
1124
1125 c = old_active_cell % priv->cols;
1126
1127 if (priv->page_first_cell < old_page_first_cell)
1128 r = priv->rows - 1;
1129 else
1130 r = 0;
1131
1132 gucharmap_chartable_set_active_cell (chartable, priv->page_first_cell + r * priv->cols + c);
1133 }
1134
1135 /* GtkWidget class methods */
1136
1137 /* - single click with left button: activate character under pointer
1138 * - double-click with left button: add active character to text_to_copy
1139 * - single-click with middle button: jump to selection_primary
1140 */
1141 static gboolean
gucharmap_chartable_button_press(GtkWidget * widget,GdkEventButton * event)1142 gucharmap_chartable_button_press (GtkWidget *widget,
1143 GdkEventButton *event)
1144 {
1145 GucharmapChartable *chartable = GUCHARMAP_CHARTABLE (widget);
1146 GucharmapChartablePrivate *priv = chartable->priv;
1147
1148 /* in case we lost keyboard focus and are clicking to get it back */
1149 gtk_widget_grab_focus (widget);
1150
1151 if (event->button == 1)
1152 {
1153 priv->click_x = event->x;
1154 priv->click_y = event->y;
1155 }
1156
1157 /* double-click */
1158 if (event->button == 1 && event->type == GDK_2BUTTON_PRESS)
1159 {
1160 g_signal_emit (chartable, signals[ACTIVATE], 0);
1161 }
1162 /* single-click */
1163 else if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
1164 {
1165 gucharmap_chartable_set_active_cell (chartable, get_cell_at_xy (chartable, event->x, event->y));
1166 }
1167 else if (event->button == 3)
1168 {
1169 gucharmap_chartable_set_active_cell (chartable, get_cell_at_xy (chartable, event->x, event->y));
1170 gucharmap_chartable_show_zoom (chartable);
1171 }
1172
1173 /* XXX: [need to return false so it gets drag events] */
1174 /* actually return true because we handle drag_begin because of
1175 * http://bugzilla.gnome.org/show_bug.cgi?id=114534 */
1176 return TRUE;
1177 }
1178
1179 static gboolean
gucharmap_chartable_button_release(GtkWidget * widget,GdkEventButton * event)1180 gucharmap_chartable_button_release (GtkWidget *widget,
1181 GdkEventButton *event)
1182 {
1183 GucharmapChartable *chartable = GUCHARMAP_CHARTABLE (widget);
1184 gboolean (* button_press_event) (GtkWidget *, GdkEventButton *) =
1185 GTK_WIDGET_CLASS (gucharmap_chartable_parent_class)->button_release_event;
1186
1187 if (event->button == 3)
1188 gucharmap_chartable_hide_zoom (chartable);
1189
1190 if (button_press_event)
1191 return button_press_event (widget, event);
1192 return FALSE;
1193 }
1194
1195 static void
gucharmap_chartable_drag_begin(GtkWidget * widget,GdkDragContext * context)1196 gucharmap_chartable_drag_begin (GtkWidget *widget,
1197 GdkDragContext *context)
1198 {
1199 GucharmapChartable *chartable = GUCHARMAP_CHARTABLE (widget);
1200 double scale;
1201 int font_size_px, screen_height;
1202 cairo_surface_t *drag_surface;
1203
1204 font_size_px = get_font_size_px (chartable);
1205 screen_height = gdk_screen_get_height (gtk_widget_get_screen (widget));
1206
1207 scale = (0.3 * screen_height) / (FACTOR_WIDTH * font_size_px);
1208 scale = CLAMP (scale, 1.0, 5.0);
1209
1210 drag_surface = create_glyph_surface (chartable,
1211 gucharmap_chartable_get_active_character (chartable),
1212 scale,
1213 FALSE, NULL, NULL);
1214 gtk_drag_set_icon_surface (context, drag_surface);
1215 cairo_surface_destroy (drag_surface);
1216
1217 /* no need to chain up */
1218 }
1219
1220 static void
gucharmap_chartable_drag_data_get(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * selection_data,guint info,guint time)1221 gucharmap_chartable_drag_data_get (GtkWidget *widget,
1222 GdkDragContext *context,
1223 GtkSelectionData *selection_data,
1224 guint info,
1225 guint time)
1226
1227 {
1228 GucharmapChartable *chartable = GUCHARMAP_CHARTABLE (widget);
1229 GucharmapChartablePrivate *priv = chartable->priv;
1230 gchar buf[7];
1231 gint n;
1232
1233 n = g_unichar_to_utf8 (gucharmap_codepoint_list_get_char (priv->codepoint_list, priv->active_cell), buf);
1234 gtk_selection_data_set_text (selection_data, buf, n);
1235
1236 /* no need to chain up */
1237 }
1238
1239 static void
gucharmap_chartable_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint time)1240 gucharmap_chartable_drag_data_received (GtkWidget *widget,
1241 GdkDragContext *context,
1242 gint x, gint y,
1243 GtkSelectionData *selection_data,
1244 guint info,
1245 guint time)
1246 {
1247 GucharmapChartable *chartable = GUCHARMAP_CHARTABLE (widget);
1248 GucharmapChartablePrivate *priv = chartable->priv;
1249 gchar *text;
1250 gunichar wc;
1251
1252 if (gtk_selection_data_get_length (selection_data) <= 0 ||
1253 gtk_selection_data_get_data (selection_data) == NULL)
1254 return;
1255
1256 text = (gchar *) gtk_selection_data_get_text (selection_data);
1257
1258 if (text == NULL) /* XXX: say something in the statusbar? */
1259 return;
1260
1261 wc = g_utf8_get_char_validated (text, -1);
1262
1263 if (wc == (gunichar)(-2) || wc == (gunichar)(-1) || wc > UNICHAR_MAX)
1264 gucharmap_chartable_emit_status_message (chartable, _("Unknown character, unable to identify."));
1265 else if (gucharmap_codepoint_list_get_index (priv->codepoint_list, wc) == (guint)(-1))
1266 gucharmap_chartable_emit_status_message (chartable, _("Not found."));
1267 else
1268 {
1269 gucharmap_chartable_emit_status_message (chartable, _("Character found."));
1270 set_active_char (chartable, wc);
1271 place_zoom_window_on_active_cell (chartable);
1272 }
1273
1274 g_free (text);
1275
1276 /* no need to chain up */
1277 }
1278
1279 static gboolean
gucharmap_chartable_draw(GtkWidget * widget,cairo_t * cr)1280 gucharmap_chartable_draw (GtkWidget *widget,
1281 cairo_t *cr)
1282 {
1283 GucharmapChartable *chartable = GUCHARMAP_CHARTABLE (widget);
1284 GucharmapChartablePrivate *priv = chartable->priv;
1285 GtkStyle *style;
1286 int row, col;
1287 cairo_rectangle_int_t clip_rect;
1288 cairo_region_t *region;
1289
1290 if (!gdk_cairo_get_clip_rectangle (cr, &clip_rect))
1291 return FALSE;
1292
1293 region = cairo_region_create_rectangle (&clip_rect);
1294
1295 if (cairo_region_is_empty (region)) {
1296 cairo_region_destroy (region);
1297 return FALSE;
1298 }
1299
1300 #if 0
1301 {
1302 int i, n_rects;
1303
1304 n_rects = cairo_region_num_rectangles (event->region);
1305
1306 g_print ("Exposing area %d:%d@(%d,%d) with %d rects ", event->area.width, event->area.height,
1307 event->area.x, event->area.y, n_rects);
1308 for (i = 0; i < n_rects; ++i) {
1309 g_print ("[Rect %d:%d@(%d,%d)] ", rects[i].width, rects[i].height, rects[i].x, rects[i].y);
1310 }
1311 g_print ("\n");
1312 }
1313 #endif
1314
1315 style = gtk_widget_get_style (widget);
1316 gdk_cairo_set_source_color (cr, &style->bg[GTK_STATE_NORMAL]);
1317 gdk_cairo_region (cr, region);
1318 cairo_fill (cr);
1319
1320 if (priv->codepoint_list == NULL)
1321 goto expose_done;
1322
1323 gucharmap_chartable_ensure_pango_layout (chartable);
1324
1325 for (row = priv->rows - 1; row >= 0; --row)
1326 {
1327 for (col = priv->cols - 1; col >= 0; --col)
1328 {
1329 cairo_rectangle_int_t rect;
1330
1331 rect.x = _gucharmap_chartable_x_offset (chartable, col);
1332 rect.y = _gucharmap_chartable_y_offset (chartable, row);
1333 rect.width = _gucharmap_chartable_column_width (chartable, col);
1334 rect.height = _gucharmap_chartable_row_height (chartable, row);
1335
1336 if (cairo_region_contains_rectangle (region, &rect) == CAIRO_REGION_OVERLAP_OUT)
1337 continue;
1338
1339 draw_square_bg (chartable, cr, &rect, row, col);
1340 draw_character (chartable, cr, &rect, row, col);
1341 }
1342 }
1343
1344 draw_borders (chartable, cr);
1345
1346 expose_done:
1347
1348 cairo_region_destroy (region);
1349
1350 /* no need to chain up */
1351 return FALSE;
1352 }
1353
1354 static gboolean
gucharmap_chartable_focus_in_event(GtkWidget * widget,GdkEventFocus * event)1355 gucharmap_chartable_focus_in_event (GtkWidget *widget,
1356 GdkEventFocus *event)
1357 {
1358 GucharmapChartable *chartable = GUCHARMAP_CHARTABLE (widget);
1359 GucharmapChartablePrivate *priv = chartable->priv;
1360
1361 expose_cell (chartable, priv->active_cell);
1362
1363 return GTK_WIDGET_CLASS (gucharmap_chartable_parent_class)->focus_in_event (widget, event);
1364 }
1365
1366 static gboolean
gucharmap_chartable_focus_out_event(GtkWidget * widget,GdkEventFocus * event)1367 gucharmap_chartable_focus_out_event (GtkWidget *widget,
1368 GdkEventFocus *event)
1369 {
1370 GucharmapChartable *chartable = GUCHARMAP_CHARTABLE (widget);
1371 GucharmapChartablePrivate *priv = chartable->priv;
1372
1373 gucharmap_chartable_hide_zoom (chartable);
1374
1375 expose_cell (chartable, priv->active_cell);
1376
1377 /* FIXME: the parent's handler already does a draw... */
1378
1379 return GTK_WIDGET_CLASS (gucharmap_chartable_parent_class)->focus_out_event (widget, event);
1380 }
1381
1382 static gboolean
gucharmap_chartable_key_press_event(GtkWidget * widget,GdkEventKey * event)1383 gucharmap_chartable_key_press_event (GtkWidget *widget,
1384 GdkEventKey *event)
1385 {
1386 GucharmapChartable *chartable = GUCHARMAP_CHARTABLE (widget);
1387
1388 if (event->state & (GDK_MOD1_MASK | GDK_CONTROL_MASK))
1389 return GTK_WIDGET_CLASS (gucharmap_chartable_parent_class)->key_press_event (widget, event);
1390
1391 switch (event->keyval)
1392 {
1393 case GDK_KEY_Shift_L: case GDK_KEY_Shift_R:
1394 gucharmap_chartable_show_zoom (chartable);
1395 break;
1396
1397 /* pass on other keys, like tab and stuff that shifts focus */
1398 default:
1399 break;
1400 }
1401
1402 return GTK_WIDGET_CLASS (gucharmap_chartable_parent_class)->key_press_event (widget, event);
1403 }
1404
1405 static gboolean
gucharmap_chartable_key_release_event(GtkWidget * widget,GdkEventKey * event)1406 gucharmap_chartable_key_release_event (GtkWidget *widget,
1407 GdkEventKey *event)
1408 {
1409 GucharmapChartable *chartable = GUCHARMAP_CHARTABLE (widget);
1410
1411 switch (event->keyval)
1412 {
1413 /* XXX: If the group(shift_toggle) Xkb option is set, then releasing
1414 * the shift key gives either ISO_Next_Group or ISO_Prev_Group. Is
1415 * there a better way to handle this case? */
1416 case GDK_KEY_Shift_L:
1417 case GDK_KEY_Shift_R:
1418 case GDK_KEY_ISO_Next_Group:
1419 case GDK_KEY_ISO_Prev_Group:
1420 gucharmap_chartable_hide_zoom (chartable);
1421 break;
1422 }
1423
1424 return GTK_WIDGET_CLASS (gucharmap_chartable_parent_class)->key_release_event (widget, event);
1425 }
1426
1427 static gboolean
gucharmap_chartable_motion_notify(GtkWidget * widget,GdkEventMotion * event)1428 gucharmap_chartable_motion_notify (GtkWidget *widget,
1429 GdkEventMotion *event)
1430 {
1431 GucharmapChartable *chartable = GUCHARMAP_CHARTABLE (widget);
1432 GucharmapChartablePrivate *priv = chartable->priv;
1433 gboolean (* motion_notify_event) (GtkWidget *, GdkEventMotion *) =
1434 GTK_WIDGET_CLASS (gucharmap_chartable_parent_class)->motion_notify_event;
1435
1436 if ((event->state & GDK_BUTTON1_MASK) &&
1437 gtk_drag_check_threshold (widget,
1438 priv->click_x,
1439 priv->click_y,
1440 event->x,
1441 event->y) &&
1442 gucharmap_unichar_validate (gucharmap_chartable_get_active_character (chartable)))
1443 {
1444 gtk_drag_begin (widget, priv->target_list,
1445 GDK_ACTION_COPY, 1, (GdkEvent *) event);
1446 }
1447
1448 if ((event->state & GDK_BUTTON3_MASK) != 0 &&
1449 priv->zoom_window)
1450 {
1451 guint cell = get_cell_at_xy (chartable, MAX (0, event->x), MAX (0, event->y));
1452
1453 if ((gint)cell != priv->active_cell)
1454 {
1455 gtk_widget_hide (priv->zoom_window);
1456 gucharmap_chartable_set_active_cell (chartable, cell);
1457 }
1458
1459 place_zoom_window (chartable, event->x_root, event->y_root);
1460 gtk_widget_show (priv->zoom_window);
1461 }
1462
1463 if (motion_notify_event)
1464 motion_notify_event (widget, event);
1465 return FALSE;
1466 }
1467
1468 #define FIRST_CELL_IN_SAME_ROW(x) ((x) - ((x) % priv->cols))
1469
1470 static void
gucharmap_chartable_size_allocate(GtkWidget * widget,GtkAllocation * allocation)1471 gucharmap_chartable_size_allocate (GtkWidget *widget,
1472 GtkAllocation *allocation)
1473 {
1474 GucharmapChartable *chartable = GUCHARMAP_CHARTABLE (widget);
1475 GucharmapChartablePrivate *priv = chartable->priv;
1476 int old_rows, old_cols;
1477 int total_extra_pixels;
1478 int new_first_cell;
1479 int bare_minimal_column_width, bare_minimal_row_height;
1480 int font_size_px;
1481 GtkAllocation widget_allocation;
1482
1483 GTK_WIDGET_CLASS (gucharmap_chartable_parent_class)->size_allocate (widget, allocation);
1484
1485 gtk_widget_get_allocation (widget, &widget_allocation);
1486 allocation = &widget_allocation;
1487
1488 old_rows = priv->rows;
1489 old_cols = priv->cols;
1490
1491 font_size_px = get_font_size_px (chartable);
1492
1493 /* FIXMEchpe bug 329481 */
1494 bare_minimal_column_width = FACTOR_WIDTH * font_size_px;
1495 bare_minimal_row_height = FACTOR_HEIGHT * font_size_px;
1496
1497 if (priv->snap_pow2_enabled)
1498 priv->cols = (1 << g_bit_nth_msf ((allocation->width - 1) / bare_minimal_column_width, -1));
1499 else
1500 priv->cols = (allocation->width - 1) / bare_minimal_column_width;
1501
1502 priv->rows = (allocation->height - 1) / bare_minimal_row_height;
1503
1504 /* avoid a horrible floating point exception crash */
1505 if (priv->rows < 1)
1506 priv->rows = 1;
1507 if (priv->cols < 1)
1508 priv->cols = 1;
1509
1510 priv->page_size = priv->rows * priv->cols;
1511
1512 total_extra_pixels = allocation->width - (priv->cols * bare_minimal_column_width + 1);
1513 priv->minimal_column_width = bare_minimal_column_width + total_extra_pixels / priv->cols;
1514 priv->n_padded_columns = allocation->width - (priv->minimal_column_width * priv->cols + 1);
1515
1516 total_extra_pixels = allocation->height - (priv->rows * bare_minimal_row_height + 1);
1517 priv->minimal_row_height = bare_minimal_row_height + total_extra_pixels / priv->rows;
1518 priv->n_padded_rows = allocation->height - (priv->minimal_row_height * priv->rows + 1);
1519
1520 if (priv->rows == old_rows && priv->cols == old_cols)
1521 return;
1522
1523 /* Need to recalculate the first cell, see bug #517188 */
1524 new_first_cell = FIRST_CELL_IN_SAME_ROW (priv->active_cell);
1525 if ((new_first_cell + priv->rows*priv->cols) > (priv->last_cell))
1526 {
1527 /* last cell is visible, so make sure it is in the last row */
1528 new_first_cell = FIRST_CELL_IN_SAME_ROW (priv->last_cell) - priv->page_size + priv->cols;
1529
1530 if (new_first_cell < 0)
1531 new_first_cell = 0;
1532 }
1533 priv->page_first_cell = new_first_cell;
1534
1535 update_scrollbar_adjustment (chartable);
1536 }
1537
1538 static void
gucharmap_chartable_get_preferred_width(GtkWidget * widget,gint * minimum,gint * natural)1539 gucharmap_chartable_get_preferred_width (GtkWidget *widget,
1540 gint *minimum,
1541 gint *natural)
1542 {
1543 GucharmapChartable *chartable = GUCHARMAP_CHARTABLE (widget);
1544 int font_size_px;
1545
1546 font_size_px = get_font_size_px (chartable);
1547
1548 *minimum = *natural = FACTOR_WIDTH * font_size_px;
1549 }
1550
1551 static void
gucharmap_chartable_get_preferred_height(GtkWidget * widget,gint * minimum,gint * natural)1552 gucharmap_chartable_get_preferred_height (GtkWidget *widget,
1553 gint *minimum,
1554 gint *natural)
1555 {
1556 GucharmapChartable *chartable = GUCHARMAP_CHARTABLE (widget);
1557 int font_size_px;
1558
1559 font_size_px = get_font_size_px (chartable);
1560
1561 *minimum = *natural = FACTOR_HEIGHT * font_size_px;
1562 }
1563
1564 static void
gucharmap_chartable_style_set(GtkWidget * widget,GtkStyle * previous_style)1565 gucharmap_chartable_style_set (GtkWidget *widget,
1566 GtkStyle *previous_style)
1567 {
1568 GucharmapChartable *chartable = GUCHARMAP_CHARTABLE (widget);
1569 GucharmapChartablePrivate *priv = chartable->priv;
1570
1571 GTK_WIDGET_CLASS (gucharmap_chartable_parent_class)->style_set (widget, previous_style);
1572
1573 gucharmap_chartable_clear_pango_layout (chartable);
1574
1575 if (priv->font_desc == NULL) {
1576 GtkStyle *style;
1577 PangoFontDescription *font_desc;
1578
1579 style = gtk_widget_get_style (widget);
1580 font_desc = pango_font_description_copy (style->font_desc);
1581
1582 /* Use twice the size of the style's font */
1583 if (pango_font_description_get_size_is_absolute (font_desc))
1584 pango_font_description_set_absolute_size (font_desc,
1585 2 * pango_font_description_get_size (font_desc));
1586 else
1587 pango_font_description_set_size (font_desc,
1588 2 * pango_font_description_get_size (font_desc));
1589
1590 gucharmap_chartable_set_font_desc_internal (chartable, font_desc /* adopts */);
1591 g_assert (priv->font_desc != NULL);
1592 }
1593
1594 /* FIXME: necessary? */
1595 /* gtk_widget_queue_draw (widget); */
1596 gtk_widget_queue_resize (widget);
1597 }
1598
1599 #ifdef ENABLE_ACCESSIBLE
1600
1601 static AtkObject *
gucharmap_chartable_get_accessible(GtkWidget * widget)1602 gucharmap_chartable_get_accessible (GtkWidget *widget)
1603 {
1604 static gboolean first_time = TRUE;
1605
1606 if (first_time)
1607 {
1608 AtkObjectFactory *factory;
1609 AtkRegistry *registry;
1610 GType derived_type;
1611 GType derived_atk_type;
1612
1613 /*
1614 * Figure out whether accessibility is enabled by looking at the
1615 * type of the accessible object which would be created for
1616 * the parent type of GucharmapChartable.
1617 */
1618 derived_type = g_type_parent (GUCHARMAP_TYPE_CHARTABLE);
1619
1620 registry = atk_get_default_registry ();
1621 factory = atk_registry_get_factory (registry,
1622 derived_type);
1623 derived_atk_type = atk_object_factory_get_accessible_type (factory);
1624 if (g_type_is_a (derived_atk_type, GTK_TYPE_ACCESSIBLE))
1625 atk_registry_set_factory_type (registry,
1626 GUCHARMAP_TYPE_CHARTABLE,
1627 gucharmap_chartable_accessible_factory_get_type ());
1628 first_time = FALSE;
1629 }
1630
1631 return GTK_WIDGET_CLASS (gucharmap_chartable_parent_class)->get_accessible (widget);
1632 }
1633
1634 #endif
1635
1636 /* GucharmapChartable class methods */
1637
1638 static void
gucharmap_chartable_set_hadjustment(GucharmapChartable * chartable,GtkAdjustment * hadjustment)1639 gucharmap_chartable_set_hadjustment (GucharmapChartable *chartable,
1640 GtkAdjustment *hadjustment)
1641 {
1642 GucharmapChartablePrivate *priv = chartable->priv;
1643
1644 if (hadjustment == priv->hadjustment)
1645 return;
1646
1647 if (priv->hadjustment)
1648 g_object_unref (priv->hadjustment);
1649
1650 priv->hadjustment = hadjustment ? g_object_ref_sink (hadjustment) : NULL;
1651 }
1652
1653 static void
gucharmap_chartable_set_vadjustment(GucharmapChartable * chartable,GtkAdjustment * vadjustment)1654 gucharmap_chartable_set_vadjustment (GucharmapChartable *chartable,
1655 GtkAdjustment *vadjustment)
1656 {
1657 GucharmapChartablePrivate *priv = chartable->priv;
1658
1659 if (vadjustment)
1660 g_return_if_fail (GTK_IS_ADJUSTMENT (vadjustment));
1661 else
1662 vadjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
1663
1664 if (priv->vadjustment)
1665 {
1666 g_signal_handler_disconnect (priv->vadjustment,
1667 priv->vadjustment_changed_handler_id);
1668 priv->vadjustment_changed_handler_id = 0;
1669 g_object_unref (priv->vadjustment);
1670 priv->vadjustment = NULL;
1671 }
1672
1673 if (vadjustment)
1674 {
1675 priv->vadjustment = g_object_ref_sink (vadjustment);
1676 priv->vadjustment_changed_handler_id =
1677 g_signal_connect (vadjustment, "value-changed",
1678 G_CALLBACK (vadjustment_value_changed_cb),
1679 chartable);
1680 }
1681
1682 update_scrollbar_adjustment (chartable);
1683 }
1684
1685 static void
gucharmap_chartable_add_move_binding(GtkBindingSet * binding_set,guint keyval,guint modmask,GtkMovementStep step,gint count)1686 gucharmap_chartable_add_move_binding (GtkBindingSet *binding_set,
1687 guint keyval,
1688 guint modmask,
1689 GtkMovementStep step,
1690 gint count)
1691 {
1692 gtk_binding_entry_add_signal (binding_set, keyval, modmask,
1693 "move-cursor", 2,
1694 G_TYPE_ENUM, step,
1695 G_TYPE_INT, count);
1696
1697 gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
1698 "move-cursor", 2,
1699 G_TYPE_ENUM, step,
1700 G_TYPE_INT, count);
1701
1702 if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
1703 return;
1704
1705 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
1706 "move-cursor", 2,
1707 G_TYPE_ENUM, step,
1708 G_TYPE_INT, count);
1709
1710 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
1711 "move-cursor", 2,
1712 G_TYPE_ENUM, step,
1713 G_TYPE_INT, count);
1714 }
1715
1716 static void
gucharmap_chartable_move_cursor_left_right(GucharmapChartable * chartable,int count)1717 gucharmap_chartable_move_cursor_left_right (GucharmapChartable *chartable,
1718 int count)
1719 {
1720 GucharmapChartablePrivate *priv = chartable->priv;
1721 GtkWidget *widget = GTK_WIDGET (chartable);
1722 gboolean is_rtl;
1723 int offset;
1724
1725 is_rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1726 offset = is_rtl ? -count : count;
1727 gucharmap_chartable_set_active_cell (chartable, priv->active_cell + offset);
1728 }
1729
1730 static void
gucharmap_chartable_move_cursor_up_down(GucharmapChartable * chartable,int count)1731 gucharmap_chartable_move_cursor_up_down (GucharmapChartable *chartable,
1732 int count)
1733 {
1734 GucharmapChartablePrivate *priv = chartable->priv;
1735
1736 gucharmap_chartable_set_active_cell (chartable,
1737 priv->active_cell + priv->cols * count);
1738 }
1739
1740 static void
gucharmap_chartable_move_cursor_page_up_down(GucharmapChartable * chartable,int count)1741 gucharmap_chartable_move_cursor_page_up_down (GucharmapChartable *chartable,
1742 int count)
1743 {
1744 GucharmapChartablePrivate *priv = chartable->priv;
1745
1746 gucharmap_chartable_set_active_cell (chartable,
1747 priv->active_cell + priv->page_size * count);
1748 }
1749
1750 static void
gucharmap_chartable_move_cursor_start_end(GucharmapChartable * chartable,int count)1751 gucharmap_chartable_move_cursor_start_end (GucharmapChartable *chartable,
1752 int count)
1753 {
1754 GucharmapChartablePrivate *priv = chartable->priv;
1755 int new_cell;
1756
1757 if (count > 0)
1758 new_cell = priv->last_cell;
1759 else
1760 new_cell = 0;
1761
1762 gucharmap_chartable_set_active_cell (chartable, new_cell);
1763 }
1764
1765 static gboolean
gucharmap_chartable_move_cursor(GucharmapChartable * chartable,GtkMovementStep step,int count)1766 gucharmap_chartable_move_cursor (GucharmapChartable *chartable,
1767 GtkMovementStep step,
1768 int count)
1769 {
1770 g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
1771 step == GTK_MOVEMENT_VISUAL_POSITIONS ||
1772 step == GTK_MOVEMENT_DISPLAY_LINES ||
1773 step == GTK_MOVEMENT_PAGES ||
1774 step == GTK_MOVEMENT_BUFFER_ENDS, FALSE);
1775
1776 switch (step)
1777 {
1778 case GTK_MOVEMENT_LOGICAL_POSITIONS:
1779 case GTK_MOVEMENT_VISUAL_POSITIONS:
1780 gucharmap_chartable_move_cursor_left_right (chartable, count);
1781 break;
1782 case GTK_MOVEMENT_DISPLAY_LINES:
1783 gucharmap_chartable_move_cursor_up_down (chartable, count);
1784 break;
1785 case GTK_MOVEMENT_PAGES:
1786 gucharmap_chartable_move_cursor_page_up_down (chartable, count);
1787 break;
1788 case GTK_MOVEMENT_BUFFER_ENDS:
1789 gucharmap_chartable_move_cursor_start_end (chartable, count);
1790 break;
1791 default:
1792 g_assert_not_reached ();
1793 }
1794
1795 return TRUE;
1796 }
1797
1798 static void
gucharmap_chartable_copy_clipboard(GucharmapChartable * chartable)1799 gucharmap_chartable_copy_clipboard (GucharmapChartable *chartable)
1800 {
1801 GtkClipboard *clipboard;
1802 gunichar wc;
1803 gchar utf8[7];
1804 gsize len;
1805
1806 wc = gucharmap_chartable_get_active_character (chartable);
1807 if (!gucharmap_unichar_validate (wc))
1808 return;
1809
1810 len = g_unichar_to_utf8 (wc, utf8);
1811
1812 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (chartable),
1813 GDK_SELECTION_CLIPBOARD);
1814 gtk_clipboard_set_text (clipboard, utf8, len);
1815 }
1816
1817 static void
gucharmap_chartable_paste_received_cb(GtkClipboard * clipboard,const char * text,gpointer user_data)1818 gucharmap_chartable_paste_received_cb (GtkClipboard *clipboard,
1819 const char *text,
1820 gpointer user_data)
1821 {
1822 gpointer *data = (gpointer *) user_data;
1823 GucharmapChartable *chartable = *data;
1824 gunichar wc;
1825
1826 g_slice_free (gpointer, data);
1827
1828 if (!chartable)
1829 return;
1830
1831 g_object_remove_weak_pointer (G_OBJECT (chartable), data);
1832
1833 if (!text)
1834 return;
1835
1836 wc = g_utf8_get_char_validated (text, -1);
1837 if (wc == 0 ||
1838 !gucharmap_unichar_validate (wc)) {
1839 gtk_widget_error_bell (GTK_WIDGET (chartable));
1840 return;
1841 }
1842
1843 gucharmap_chartable_set_active_character (chartable, wc);
1844 }
1845
1846 static void
gucharmap_chartable_paste_clipboard(GucharmapChartable * chartable)1847 gucharmap_chartable_paste_clipboard (GucharmapChartable *chartable)
1848 {
1849 GtkClipboard *clipboard;
1850 gpointer *data;
1851
1852 if (!gtk_widget_get_realized (GTK_WIDGET (chartable)))
1853 return;
1854
1855 data = g_slice_new (gpointer);
1856 *data = chartable;
1857 g_object_add_weak_pointer (G_OBJECT (chartable), data);
1858
1859 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (chartable),
1860 GDK_SELECTION_CLIPBOARD);
1861 gtk_clipboard_request_text (clipboard,
1862 gucharmap_chartable_paste_received_cb,
1863 data);
1864 }
1865
1866 /* does all the initial construction */
1867 static void
gucharmap_chartable_init(GucharmapChartable * chartable)1868 gucharmap_chartable_init (GucharmapChartable *chartable)
1869 {
1870 GtkWidget *widget = GTK_WIDGET (chartable);
1871 GucharmapChartablePrivate *priv;
1872
1873 priv = chartable->priv = G_TYPE_INSTANCE_GET_PRIVATE (chartable, GUCHARMAP_TYPE_CHARTABLE, GucharmapChartablePrivate);
1874
1875 priv->page_first_cell = 0;
1876 priv->active_cell = 0;
1877 priv->rows = 1;
1878 priv->cols = 1;
1879 priv->zoom_mode_enabled = TRUE;
1880 priv->zoom_window = NULL;
1881 priv->snap_pow2_enabled = FALSE;
1882 priv->font_fallback = TRUE;
1883
1884 priv->vadjustment = NULL;
1885 priv->hadjustment = NULL;
1886 priv->hscroll_policy = GTK_SCROLL_NATURAL;
1887 priv->vscroll_policy = GTK_SCROLL_NATURAL;
1888
1889 /* This didn't fix the slow expose events either: */
1890 /* gtk_widget_set_double_buffered (widget, FALSE); */
1891
1892 gtk_widget_set_events (widget,
1893 GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK | GDK_BUTTON_PRESS_MASK |
1894 GDK_BUTTON_RELEASE_MASK | GDK_BUTTON3_MOTION_MASK |
1895 GDK_BUTTON1_MOTION_MASK | GDK_FOCUS_CHANGE_MASK |
1896 GDK_SCROLL_MASK | GDK_SMOOTH_SCROLL_MASK);
1897
1898 priv->target_list = gtk_target_list_new (NULL, 0);
1899 gtk_target_list_add_text_targets (priv->target_list, 0);
1900
1901 gtk_drag_dest_set (widget, GTK_DEST_DEFAULT_ALL,
1902 NULL, 0,
1903 GDK_ACTION_COPY);
1904 gtk_drag_dest_add_text_targets (widget);
1905
1906 /* this is required to get key_press events */
1907 gtk_widget_set_can_focus (widget, TRUE);
1908
1909 gucharmap_chartable_set_codepoint_list (chartable, NULL);
1910
1911 gtk_widget_show_all (GTK_WIDGET (chartable));
1912 }
1913
1914 static void
gucharmap_chartable_finalize(GObject * object)1915 gucharmap_chartable_finalize (GObject *object)
1916 {
1917 GucharmapChartable *chartable = GUCHARMAP_CHARTABLE (object);
1918 GucharmapChartablePrivate *priv = chartable->priv;
1919
1920 if (priv->font_desc)
1921 pango_font_description_free (priv->font_desc);
1922
1923 gucharmap_chartable_clear_pango_layout (chartable);
1924
1925 gtk_target_list_unref (priv->target_list);
1926
1927 if (priv->codepoint_list)
1928 g_object_unref (priv->codepoint_list);
1929
1930 destroy_zoom_window (chartable);
1931
1932 G_OBJECT_CLASS (gucharmap_chartable_parent_class)->finalize (object);
1933 }
1934
1935 static void
gucharmap_chartable_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1936 gucharmap_chartable_set_property (GObject *object,
1937 guint prop_id,
1938 const GValue *value,
1939 GParamSpec *pspec)
1940 {
1941 GucharmapChartable *chartable = GUCHARMAP_CHARTABLE (object);
1942 GucharmapChartablePrivate *priv = chartable->priv;
1943
1944 switch (prop_id) {
1945 case PROP_HADJUSTMENT:
1946 gucharmap_chartable_set_hadjustment (chartable, g_value_get_object (value));
1947 break;
1948 case PROP_VADJUSTMENT:
1949 gucharmap_chartable_set_vadjustment (chartable, g_value_get_object (value));
1950 break;
1951 case PROP_HSCROLL_POLICY:
1952 priv->hscroll_policy = g_value_get_enum (value);
1953 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (chartable));
1954 break;
1955 case PROP_VSCROLL_POLICY:
1956 priv->vscroll_policy = g_value_get_enum (value);
1957 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (chartable));
1958 break;
1959 case PROP_ACTIVE_CHAR:
1960 gucharmap_chartable_set_active_character (chartable, g_value_get_uint (value));
1961 break;
1962 case PROP_CODEPOINT_LIST:
1963 gucharmap_chartable_set_codepoint_list (chartable, g_value_get_object (value));
1964 break;
1965 case PROP_FONT_DESC:
1966 gucharmap_chartable_set_font_desc (chartable, g_value_get_boxed (value));
1967 break;
1968 case PROP_FONT_FALLBACK:
1969 gucharmap_chartable_set_font_fallback (chartable, g_value_get_boolean (value));
1970 break;
1971 case PROP_SNAP_POW2:
1972 gucharmap_chartable_set_snap_pow2 (chartable, g_value_get_boolean (value));
1973 break;
1974 case PROP_ZOOM_ENABLED:
1975 gucharmap_chartable_set_zoom_enabled (chartable, g_value_get_boolean (value));
1976 break;
1977 case PROP_ZOOM_SHOWING:
1978 /* not writable */
1979 break;
1980 default:
1981 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1982 break;
1983 }
1984 }
1985
1986 static void
gucharmap_chartable_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1987 gucharmap_chartable_get_property (GObject *object,
1988 guint prop_id,
1989 GValue *value,
1990 GParamSpec *pspec)
1991 {
1992 GucharmapChartable *chartable = GUCHARMAP_CHARTABLE (object);
1993 GucharmapChartablePrivate *priv = chartable->priv;
1994
1995 switch (prop_id) {
1996 case PROP_HADJUSTMENT:
1997 g_value_set_object (value, NULL);
1998 break;
1999 case PROP_VADJUSTMENT:
2000 g_value_set_object (value, priv->vadjustment);
2001 break;
2002 case PROP_HSCROLL_POLICY:
2003 g_value_set_enum (value, priv->hscroll_policy);
2004 break;
2005 case PROP_VSCROLL_POLICY:
2006 g_value_set_enum (value, priv->vscroll_policy);
2007 break;
2008 case PROP_ACTIVE_CHAR:
2009 g_value_set_uint (value, gucharmap_chartable_get_active_character (chartable));
2010 break;
2011 case PROP_CODEPOINT_LIST:
2012 g_value_set_object (value, gucharmap_chartable_get_codepoint_list (chartable));
2013 break;
2014 case PROP_FONT_DESC:
2015 g_value_set_boxed (value, gucharmap_chartable_get_font_desc (chartable));
2016 break;
2017 case PROP_FONT_FALLBACK:
2018 g_value_set_boolean (value, gucharmap_chartable_get_font_fallback (chartable));
2019 break;
2020 case PROP_SNAP_POW2:
2021 g_value_set_boolean (value, priv->snap_pow2_enabled);
2022 break;
2023 case PROP_ZOOM_ENABLED:
2024 g_value_set_boolean (value, priv->zoom_mode_enabled);
2025 break;
2026 case PROP_ZOOM_SHOWING:
2027 g_value_set_boolean (value, priv->zoom_window != NULL);
2028 break;
2029 default:
2030 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2031 break;
2032 }
2033 }
2034
2035 static void
gucharmap_chartable_class_init(GucharmapChartableClass * klass)2036 gucharmap_chartable_class_init (GucharmapChartableClass *klass)
2037 {
2038 GObjectClass *object_class = G_OBJECT_CLASS (klass);
2039 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
2040 GtkBindingSet *binding_set;
2041
2042 g_type_class_add_private (object_class, sizeof (GucharmapChartablePrivate));
2043
2044 object_class->finalize = gucharmap_chartable_finalize;
2045 object_class->get_property = gucharmap_chartable_get_property;
2046 object_class->set_property = gucharmap_chartable_set_property;
2047
2048 widget_class->drag_begin = gucharmap_chartable_drag_begin;
2049 widget_class->drag_data_get = gucharmap_chartable_drag_data_get;
2050 widget_class->drag_data_received = gucharmap_chartable_drag_data_received;
2051 widget_class->button_press_event = gucharmap_chartable_button_press;
2052 widget_class->button_release_event = gucharmap_chartable_button_release;
2053 widget_class->get_preferred_width = gucharmap_chartable_get_preferred_width;
2054 widget_class->get_preferred_height = gucharmap_chartable_get_preferred_height;
2055 widget_class->draw = gucharmap_chartable_draw;
2056 widget_class->focus_in_event = gucharmap_chartable_focus_in_event;
2057 widget_class->focus_out_event = gucharmap_chartable_focus_out_event;
2058 widget_class->key_press_event = gucharmap_chartable_key_press_event;
2059 widget_class->key_release_event = gucharmap_chartable_key_release_event;
2060 widget_class->motion_notify_event = gucharmap_chartable_motion_notify;
2061 widget_class->size_allocate = gucharmap_chartable_size_allocate;
2062 widget_class->style_set = gucharmap_chartable_style_set;
2063 #ifdef ENABLE_ACCESSIBLE
2064 widget_class->get_accessible = gucharmap_chartable_get_accessible;
2065 #endif
2066
2067 klass->move_cursor = gucharmap_chartable_move_cursor;
2068 klass->activate = NULL;
2069 klass->copy_clipboard = gucharmap_chartable_copy_clipboard;
2070 klass->paste_clipboard = gucharmap_chartable_paste_clipboard;
2071 klass->set_active_char = NULL;
2072
2073 widget_class->activate_signal = signals[ACTIVATE] =
2074 g_signal_new (I_("activate"),
2075 G_TYPE_FROM_CLASS (object_class),
2076 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
2077 G_STRUCT_OFFSET (GucharmapChartableClass, activate),
2078 NULL, NULL,
2079 g_cclosure_marshal_VOID__VOID,
2080 G_TYPE_NONE,
2081 0);
2082
2083 /* GtkScrollable interface properties */
2084 g_object_class_override_property (object_class, PROP_HADJUSTMENT, "hadjustment");
2085 g_object_class_override_property (object_class, PROP_VADJUSTMENT, "vadjustment");
2086 g_object_class_override_property (object_class, PROP_HSCROLL_POLICY, "hscroll-policy");
2087 g_object_class_override_property (object_class, PROP_VSCROLL_POLICY, "vscroll-policy");
2088
2089 signals[STATUS_MESSAGE] =
2090 g_signal_new (I_("status-message"), gucharmap_chartable_get_type (), G_SIGNAL_RUN_FIRST,
2091 G_STRUCT_OFFSET (GucharmapChartableClass, status_message),
2092 NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE,
2093 1, G_TYPE_STRING);
2094
2095 signals[MOVE_CURSOR] =
2096 g_signal_new (I_("move-cursor"),
2097 G_TYPE_FROM_CLASS (object_class),
2098 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
2099 G_STRUCT_OFFSET (GucharmapChartableClass, move_cursor),
2100 NULL, NULL,
2101 _gucharmap_marshal_BOOLEAN__ENUM_INT,
2102 G_TYPE_BOOLEAN, 2,
2103 GTK_TYPE_MOVEMENT_STEP,
2104 G_TYPE_INT);
2105
2106 signals[COPY_CLIPBOARD] =
2107 g_signal_new (I_("copy-clipboard"),
2108 G_OBJECT_CLASS_TYPE (object_class),
2109 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
2110 G_STRUCT_OFFSET (GucharmapChartableClass, copy_clipboard),
2111 NULL, NULL,
2112 g_cclosure_marshal_VOID__VOID,
2113 G_TYPE_NONE, 0);
2114
2115 signals[PASTE_CLIPBOARD] =
2116 g_signal_new (I_("paste-clipboard"),
2117 G_OBJECT_CLASS_TYPE (object_class),
2118 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
2119 G_STRUCT_OFFSET (GucharmapChartableClass, paste_clipboard),
2120 NULL, NULL,
2121 g_cclosure_marshal_VOID__VOID,
2122 G_TYPE_NONE, 0);
2123
2124 /* Not using g_param_spec_unichar on purpose, since it disallows certain values
2125 * we want (it's performing a g_unichar_validate).
2126 */
2127 g_object_class_install_property
2128 (object_class,
2129 PROP_ACTIVE_CHAR,
2130 g_param_spec_uint ("active-character", NULL, NULL,
2131 0,
2132 UNICHAR_MAX,
2133 0,
2134 G_PARAM_READWRITE |
2135 G_PARAM_STATIC_NAME |
2136 G_PARAM_STATIC_NICK |
2137 G_PARAM_STATIC_BLURB));
2138
2139 g_object_class_install_property
2140 (object_class,
2141 PROP_CODEPOINT_LIST,
2142 g_param_spec_object ("codepoint-list", NULL, NULL,
2143 gucharmap_codepoint_list_get_type (),
2144 G_PARAM_READWRITE |
2145 G_PARAM_STATIC_NAME |
2146 G_PARAM_STATIC_NICK |
2147 G_PARAM_STATIC_BLURB));
2148
2149 g_object_class_install_property
2150 (object_class,
2151 PROP_FONT_DESC,
2152 g_param_spec_boxed ("font-desc", NULL, NULL,
2153 PANGO_TYPE_FONT_DESCRIPTION,
2154 G_PARAM_READWRITE |
2155 G_PARAM_STATIC_NAME |
2156 G_PARAM_STATIC_NICK |
2157 G_PARAM_STATIC_BLURB));
2158
2159 /**
2160 * GucharmapChartable:font-fallback:
2161 *
2162 * Whether font fallback is enabled.
2163 *
2164 * Since: 2.34
2165 */
2166 g_object_class_install_property
2167 (object_class,
2168 PROP_FONT_FALLBACK,
2169 g_param_spec_boolean ("font-fallback", NULL, NULL,
2170 TRUE,
2171 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2172
2173 g_object_class_install_property
2174 (object_class,
2175 PROP_SNAP_POW2,
2176 g_param_spec_boolean ("snap-power-2", NULL, NULL,
2177 FALSE,
2178 G_PARAM_READWRITE |
2179 G_PARAM_STATIC_NAME |
2180 G_PARAM_STATIC_NICK |
2181 G_PARAM_STATIC_BLURB));
2182
2183 g_object_class_install_property
2184 (object_class,
2185 PROP_ZOOM_ENABLED,
2186 g_param_spec_boolean ("zoom-enabled", NULL, NULL,
2187 FALSE,
2188 G_PARAM_READWRITE |
2189 G_PARAM_STATIC_NAME |
2190 G_PARAM_STATIC_NICK |
2191 G_PARAM_STATIC_BLURB));
2192
2193 g_object_class_install_property
2194 (object_class,
2195 PROP_ZOOM_SHOWING,
2196 g_param_spec_boolean ("zoom-showing", NULL, NULL,
2197 FALSE,
2198 G_PARAM_READABLE |
2199 G_PARAM_STATIC_NAME |
2200 G_PARAM_STATIC_NICK |
2201 G_PARAM_STATIC_BLURB));
2202
2203 /* Keybindings */
2204 binding_set = gtk_binding_set_by_class (klass);
2205
2206 /* Cursor movement */
2207 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_Up, 0,
2208 GTK_MOVEMENT_DISPLAY_LINES, -1);
2209 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_KP_Up, 0,
2210 GTK_MOVEMENT_DISPLAY_LINES, -1);
2211
2212 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_Down, 0,
2213 GTK_MOVEMENT_DISPLAY_LINES, 1);
2214 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_KP_Down, 0,
2215 GTK_MOVEMENT_DISPLAY_LINES, 1);
2216
2217 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_p, GDK_CONTROL_MASK,
2218 GTK_MOVEMENT_DISPLAY_LINES, -1);
2219
2220 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_n, GDK_CONTROL_MASK,
2221 GTK_MOVEMENT_DISPLAY_LINES, 1);
2222
2223 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_Home, 0,
2224 GTK_MOVEMENT_BUFFER_ENDS, -1);
2225 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_KP_Home, 0,
2226 GTK_MOVEMENT_BUFFER_ENDS, -1);
2227
2228 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_End, 0,
2229 GTK_MOVEMENT_BUFFER_ENDS, 1);
2230 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_KP_End, 0,
2231 GTK_MOVEMENT_BUFFER_ENDS, 1);
2232
2233 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_Page_Up, 0,
2234 GTK_MOVEMENT_PAGES, -1);
2235 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_KP_Page_Up, 0,
2236 GTK_MOVEMENT_PAGES, -1);
2237
2238 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_Page_Down, 0,
2239 GTK_MOVEMENT_PAGES, 1);
2240 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_KP_Page_Down, 0,
2241 GTK_MOVEMENT_PAGES, 1);
2242
2243 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_Left, 0,
2244 GTK_MOVEMENT_VISUAL_POSITIONS, -1);
2245 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_KP_Left, 0,
2246 GTK_MOVEMENT_VISUAL_POSITIONS, -1);
2247
2248 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_Right, 0,
2249 GTK_MOVEMENT_VISUAL_POSITIONS, 1);
2250 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_KP_Right, 0,
2251 GTK_MOVEMENT_VISUAL_POSITIONS, 1);
2252
2253 /* Activate */
2254 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, 0,
2255 "activate", 0);
2256 gtk_binding_entry_add_signal (binding_set, GDK_KEY_ISO_Enter, 0,
2257 "activate", 0);
2258 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Enter, 0,
2259 "activate", 0);
2260 gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, 0,
2261 "activate", 0);
2262
2263 /* Clipboard actions */
2264 gtk_binding_entry_add_signal (binding_set, GDK_KEY_c, GDK_CONTROL_MASK,
2265 "copy-clipboard", 0);
2266 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Insert, GDK_CONTROL_MASK,
2267 "copy-clipboard", 0);
2268 gtk_binding_entry_add_signal (binding_set, GDK_KEY_v, GDK_CONTROL_MASK,
2269 "paste-clipboard", 0);
2270 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Insert, GDK_SHIFT_MASK,
2271 "paste-clipboard", 0);
2272
2273 #if 0
2274 /* VI keybindings */
2275 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_k, 0,
2276 GTK_MOVEMENT_DISPLAY_LINES, -1);
2277 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_K, 0,
2278 GTK_MOVEMENT_DISPLAY_LINES, -1);
2279
2280 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_j, 0,
2281 GTK_MOVEMENT_DISPLAY_LINES, 1);
2282 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_J, 0,
2283 GTK_MOVEMENT_DISPLAY_LINES, 1);
2284
2285 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_b, 0,
2286 GTK_MOVEMENT_PAGES, -1);
2287 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_B, 0,
2288 GTK_MOVEMENT_PAGES, -1);
2289
2290 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_h, 0,
2291 GTK_MOVEMENT_VISUAL_POSITIONS, -1);
2292 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_H, 0,
2293 GTK_MOVEMENT_VISUAL_POSITIONS, -1);
2294
2295 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_l, 0,
2296 GTK_MOVEMENT_VISUAL_POSITIONS, 1);
2297 gucharmap_chartable_add_move_binding (binding_set, GDK_KEY_L, 0,
2298 GTK_MOVEMENT_VISUAL_POSITIONS, 1);
2299 #endif
2300 }
2301
2302 /* public API */
2303
2304 /**
2305 * gucharmap_chartable_new:
2306 *
2307 * Returns: a new #GucharmapChartable
2308 */
2309 GtkWidget *
gucharmap_chartable_new(void)2310 gucharmap_chartable_new (void)
2311 {
2312 return GTK_WIDGET (g_object_new (gucharmap_chartable_get_type (), NULL));
2313 }
2314
2315 /**
2316 * gucharmap_chartable_set_zoom_enabled:
2317 * @chartable: a #GucharmapChartable
2318 * @enabled: whether to enable zoom mode
2319 *
2320 * Enables or disables the zoom popup.
2321 */
2322 void
gucharmap_chartable_set_zoom_enabled(GucharmapChartable * chartable,gboolean enabled)2323 gucharmap_chartable_set_zoom_enabled (GucharmapChartable *chartable,
2324 gboolean enabled)
2325 {
2326 GucharmapChartablePrivate *priv;
2327 GObject *object;
2328
2329 g_return_if_fail (GUCHARMAP_IS_CHARTABLE (chartable));
2330
2331 priv = chartable->priv;
2332
2333 enabled = enabled != FALSE;
2334 if (priv->zoom_mode_enabled == enabled)
2335 return;
2336
2337 object = G_OBJECT (chartable);
2338 g_object_freeze_notify (object);
2339
2340 priv->zoom_mode_enabled = enabled;
2341 if (!enabled)
2342 gucharmap_chartable_hide_zoom (chartable);
2343
2344 g_object_notify (object, "zoom-enabled");
2345 g_object_thaw_notify (object);
2346 }
2347
2348 /**
2349 * gucharmap_chartable_get_zoom_enabled:
2350 * @chartable: a #GucharmapChartable
2351 *
2352 * Returns: whether zooming is enabled
2353 */
2354 gboolean
gucharmap_chartable_get_zoom_enabled(GucharmapChartable * chartable)2355 gucharmap_chartable_get_zoom_enabled (GucharmapChartable *chartable)
2356 {
2357 g_return_val_if_fail (GUCHARMAP_IS_CHARTABLE (chartable), FALSE);
2358
2359 return chartable->priv->zoom_mode_enabled;
2360 }
2361
2362 /**
2363 * gucharmap_chartable_set_font_desc:
2364 * @chartable: a #GucharmapChartable
2365 * @font_desc: a #PangoFontDescription
2366 *
2367 * Sets @font_desc as the font to use to display the character table.
2368 */
2369 void
gucharmap_chartable_set_font_desc(GucharmapChartable * chartable,PangoFontDescription * font_desc)2370 gucharmap_chartable_set_font_desc (GucharmapChartable *chartable,
2371 PangoFontDescription *font_desc)
2372 {
2373 GucharmapChartablePrivate *priv;
2374
2375 g_return_if_fail (GUCHARMAP_IS_CHARTABLE (chartable));
2376 g_return_if_fail (font_desc != NULL);
2377
2378 priv = chartable->priv;
2379
2380 if (priv->font_desc &&
2381 pango_font_description_equal (font_desc, priv->font_desc))
2382 return;
2383
2384 gucharmap_chartable_set_font_desc_internal (chartable,
2385 pango_font_description_copy (font_desc));
2386 }
2387
2388 /**
2389 * gucharmap_chartable_get_font_desc:
2390 * @chartable: a #GucharmapChartable
2391 *
2392 * Returns: the #PangoFontDescription used to display the character table.
2393 * The returned object is owned by @chartable and must not be modified or freed.
2394 */
2395 PangoFontDescription *
gucharmap_chartable_get_font_desc(GucharmapChartable * chartable)2396 gucharmap_chartable_get_font_desc (GucharmapChartable *chartable)
2397 {
2398 g_return_val_if_fail (GUCHARMAP_IS_CHARTABLE (chartable), NULL);
2399
2400 return chartable->priv->font_desc;
2401 }
2402
2403 /**
2404 * gucharmap_chartable_set_font_fallback:
2405 * @chartable: a #GucharmapChartable
2406 * @enable_font_fallback: whether to enable font fallback
2407 *
2408 * Since: 2.34
2409 */
2410 void
gucharmap_chartable_set_font_fallback(GucharmapChartable * chartable,gboolean enable_font_fallback)2411 gucharmap_chartable_set_font_fallback (GucharmapChartable *chartable,
2412 gboolean enable_font_fallback)
2413 {
2414 GucharmapChartablePrivate *priv;
2415 GtkWidget *widget;
2416
2417 g_return_if_fail (GUCHARMAP_IS_CHARTABLE (chartable));
2418
2419 priv = chartable->priv;
2420 enable_font_fallback = enable_font_fallback != FALSE;
2421 if (enable_font_fallback == priv->font_fallback)
2422 return;
2423
2424 priv->font_fallback = enable_font_fallback;
2425 g_object_notify (G_OBJECT (chartable), "font-fallback");
2426
2427 gucharmap_chartable_clear_pango_layout (chartable);
2428
2429 widget = GTK_WIDGET (chartable);
2430 if (gtk_widget_get_realized (widget))
2431 gtk_widget_queue_draw (widget);
2432 }
2433
2434 /**
2435 * gucharmap_chartable_get_font_fallback:
2436 * @chartable: a #GucharmapChartable
2437 *
2438 * Returns: whether font fallback is enabled
2439 *
2440 * Since: 2.34
2441 */
2442 gboolean
gucharmap_chartable_get_font_fallback(GucharmapChartable * chartable)2443 gucharmap_chartable_get_font_fallback (GucharmapChartable *chartable)
2444 {
2445 g_return_val_if_fail (GUCHARMAP_IS_CHARTABLE (chartable), FALSE);
2446
2447 return chartable->priv->font_fallback;
2448 }
2449
2450 /**
2451 * gucharmap_chartable_get_active_character:
2452 * @chartable: a #GucharmapChartable
2453 *
2454 * Returns: the currently selected character
2455 */
2456 gunichar
gucharmap_chartable_get_active_character(GucharmapChartable * chartable)2457 gucharmap_chartable_get_active_character (GucharmapChartable *chartable)
2458 {
2459 GucharmapChartablePrivate *priv = chartable->priv;
2460
2461 if (!priv->codepoint_list)
2462 return 0;
2463
2464 return gucharmap_codepoint_list_get_char (priv->codepoint_list, priv->active_cell);
2465 }
2466
2467 /**
2468 * gucharmap_chartable_set_active_character:
2469 * @chartable: a #GucharmapChartable
2470 * @wc: a unicode character (UTF-32)
2471 *
2472 * Sets @wc as the selected character.
2473 */
2474 void
gucharmap_chartable_set_active_character(GucharmapChartable * chartable,gunichar wc)2475 gucharmap_chartable_set_active_character (GucharmapChartable *chartable,
2476 gunichar wc)
2477 {
2478 set_active_char (chartable, wc);
2479 }
2480
2481 /**
2482 * gucharmap_chartable_set_snap_pow2:
2483 * @chartable: a #GucharmapChartable
2484 * @snap: whether to enable or disable snapping
2485 *
2486 * Sets whether the number columns the character table shows should
2487 * always be a power of 2.
2488 */
2489 void
gucharmap_chartable_set_snap_pow2(GucharmapChartable * chartable,gboolean snap)2490 gucharmap_chartable_set_snap_pow2 (GucharmapChartable *chartable,
2491 gboolean snap)
2492 {
2493 GucharmapChartablePrivate *priv = chartable->priv;
2494
2495 snap = snap != FALSE;
2496
2497 if (snap != priv->snap_pow2_enabled)
2498 {
2499 priv->snap_pow2_enabled = snap;
2500
2501 gtk_widget_queue_resize (GTK_WIDGET (chartable));
2502
2503 g_object_notify (G_OBJECT (chartable), "snap-power-2");
2504 }
2505 }
2506
2507 /**
2508 * gucharmap_chartable_get_snap_pow2:
2509 * @chartable: a #GucharmapChartable
2510 *
2511 * Returns whether the number of columns the character table shows is
2512 * always a power of 2.
2513 */
2514 gboolean
gucharmap_chartable_get_snap_pow2(GucharmapChartable * chartable)2515 gucharmap_chartable_get_snap_pow2 (GucharmapChartable *chartable)
2516 {
2517 GucharmapChartablePrivate *priv = chartable->priv;
2518
2519 return priv->snap_pow2_enabled;
2520 }
2521
2522 /**
2523 * gucharmap_chartable_set_codepoint_list:
2524 * @chartable: a #GucharmapChartable
2525 * @codepoint_list: a #GucharmapCodepointList
2526 *
2527 * Sets the codepoint list to show in the character table.
2528 */
2529 void
gucharmap_chartable_set_codepoint_list(GucharmapChartable * chartable,GucharmapCodepointList * codepoint_list)2530 gucharmap_chartable_set_codepoint_list (GucharmapChartable *chartable,
2531 GucharmapCodepointList *codepoint_list)
2532 {
2533 GucharmapChartablePrivate *priv = chartable->priv;
2534 GObject *object = G_OBJECT (chartable);
2535 GtkWidget *widget = GTK_WIDGET (chartable);
2536
2537 g_object_freeze_notify (object);
2538
2539 if (codepoint_list)
2540 g_object_ref (codepoint_list);
2541 if (priv->codepoint_list)
2542 g_object_unref (priv->codepoint_list);
2543 priv->codepoint_list = codepoint_list;
2544 priv->codepoint_list_changed = TRUE;
2545
2546 priv->active_cell = 0;
2547 priv->page_first_cell = 0;
2548 if (codepoint_list)
2549 priv->last_cell = gucharmap_codepoint_list_get_last_index (codepoint_list);
2550 else
2551 priv->last_cell = 0;
2552
2553 g_object_notify (object, "codepoint-list");
2554 g_object_notify (object, "active-character");
2555
2556 update_scrollbar_adjustment (chartable);
2557
2558 gtk_widget_queue_draw (widget);
2559
2560 g_object_thaw_notify (object);
2561 }
2562
2563 /**
2564 * gucharmap_chartable_get_codepoint_list:
2565 * @chartable: a #GucharmapChartable
2566 *
2567 * Returns: (transfer none): the current codepoint list
2568 */
2569 GucharmapCodepointList *
gucharmap_chartable_get_codepoint_list(GucharmapChartable * chartable)2570 gucharmap_chartable_get_codepoint_list (GucharmapChartable *chartable)
2571 {
2572 GucharmapChartablePrivate *priv = chartable->priv;
2573
2574 return priv->codepoint_list;
2575 }
2576