diff options
author | Tor Lillqvist <tml@iki.fi> | 2000-07-15 01:06:08 +0000 |
---|---|---|
committer | Tor Lillqvist <tml@src.gnome.org> | 2000-07-15 01:06:08 +0000 |
commit | 932fe1e3da39b3d8febfca65f6ca5312f0397ed1 (patch) | |
tree | d5583d408b5af1a4ed899a78937fc76ed46c442d /examples/viewer-win32.c | |
parent | d4fb416c99d0066aafed26306a421a7bd22289e3 (diff) | |
download | pango-932fe1e3da39b3d8febfca65f6ca5312f0397ed1.tar.gz |
pango/pangowin32.h pango/pangowin32-private.h pango/pangowin32-fontcache.c
2000-07-15 Tor Lillqvist <tml@iki.fi>
* pango/pangowin32.h
* pango/pangowin32-private.h
* pango/pangowin32-fontcache.c
* pango/pangowin32-fontmap.c
* modules/basic/basic-win32.c
* examples/viewer-win32.c
* examples/pangowin32.aliases: New files. Start of a Win32
implementation. Does not work yet.
* configure.in: Chek for dirent.h and unistd.h.
* pango/pango-utils.h
* pango/pango-utils.c (pango_get_sysconf_subdirectory,
pango_get_lib_subdirectory): New functions, for better
portability, to enable installation-time choice of directory (on
Windows) instead of compile-time. Use these instead of SYSCONFDIR
"/pango" and LIBDIR "/pango".
(pango_split_file_list): Fix comment, the function splits on
searchpath separators, not commas. Use G_SEARCHPATH_SEPARATOR_S
for portability. Don't try to expand '~' as home directory on
Windows.
(read_config): Use pango_get_sysconf_subdirectory().
* pango/modules.c (read_modules): Use pango_get_sysconf_subdirectory().
Don't crash if a module file cannot be opened.
* pango/querymodules.c: Include config.h Conditionalize inclusion
of dirent.h and unistd.h. Use platform-specific shared library
extension. Use pango_get_lib_subdirectory().
Diffstat (limited to 'examples/viewer-win32.c')
-rw-r--r-- | examples/viewer-win32.c | 742 |
1 files changed, 742 insertions, 0 deletions
diff --git a/examples/viewer-win32.c b/examples/viewer-win32.c new file mode 100644 index 00000000..9304b7d2 --- /dev/null +++ b/examples/viewer-win32.c @@ -0,0 +1,742 @@ +#define PING() g_print ("%s:%d\n", __PRETTY_FUNCTION__, __LINE__) +/* Pango + * viewer-win32.c: Example program to view a UTF-8 encoding file + * using Pango to render result. + * + * Copyright (C) 1999 Red Hat Software + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include <gdk/win32/gdkwin32.h> + +#include <pango/pango.h> +#include <pango/pangowin32.h> + +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#define BUFSIZE 1024 + +typedef struct _Paragraph Paragraph; + +/* Structure representing a paragraph + */ +struct _Paragraph { + char *text; + int length; + int height; /* Height, in pixels */ + PangoLayout *layout; +}; + +GList *paragraphs; + +static PangoFontDescription font_description; +static Paragraph *highlight_para; +static int highlight_offset; + +GtkWidget *styles_combo; + +static GtkWidget *message_label; +GtkWidget *layout; + +PangoContext *context; + +static void fill_styles_combo (GtkWidget *combo); + +/* Read an entire file into a string + */ +static char * +read_file(char *name) +{ + GString *inbuf; + FILE *file; + char *text; + char buffer[BUFSIZE]; + + PING(); + file = fopen (name, "r"); + if (!file) + { + fprintf (stderr, "%s: Cannot open %s\n", g_get_prgname ()); + return NULL; + } + + inbuf = g_string_new (NULL); + while (1) + { + char *bp = fgets (buffer, BUFSIZE-1, file); + if (ferror (file)) + { + fprintf(stderr, "%s: Error reading %s\n", g_get_prgname ()); + g_string_free (inbuf, TRUE); + return NULL; + } + else if (bp == NULL) + break; + + g_string_append (inbuf, buffer); + } + + fclose (file); + + text = inbuf->str; + g_string_free (inbuf, FALSE); + + return text; +} + +/* Take a UTF8 string and break it into paragraphs on \n characters + */ +static GList * +split_paragraphs (char *text) +{ + char *p = text; + char *next; + gunichar wc; + GList *result = NULL; + char *last_para = text; + + while (*p) + { + wc = g_utf8_get_char (p); + next = g_utf8_next_char (p); + if (wc == (gunichar)-1) + { + fprintf (stderr, "%s: Invalid character in input\n", g_get_prgname ()); + g_list_foreach (result, (GFunc)g_free, NULL); + return NULL; + } + if (!*p || !wc || wc == '\n') + { + Paragraph *para = g_new (Paragraph, 1); + para->text = last_para; + para->length = p - last_para; + para->layout = pango_layout_new (context); + pango_layout_set_text (para->layout, para->text, para->length); + para->height = 0; + + last_para = next; + + result = g_list_prepend (result, para); + } + if (!wc) /* incomplete character at end */ + break; + p = next; + } + + return g_list_reverse (result); +} + +/* Given an x-y position, return the paragraph and offset + * within the paragraph of the click. + */ +gboolean +xy_to_cp (int width, int x, int y, Paragraph **para_return, int *index) +{ + GList *para_list; + int height = 0; + + *para_return = NULL; + + PING(); + para_list = paragraphs; + while (para_list && height < y) + { + Paragraph *para = para_list->data; + + if (height + para->height >= y) + { + gboolean result = pango_layout_xy_to_index (para->layout, x * 1000, (y - height) * 1000, + index, NULL); + if (result && para_return) + *para_return = para; + + return result; + } + + height += para->height; + para_list = para_list->next; + } + PING(); + + return FALSE; +} + +/* Given a paragraph and offset in that paragraph, find the + * bounding rectangle for the character at the offset. + */ +void +char_bounds (Paragraph *para, int index, int width, PangoRectangle *rect) +{ + GList *para_list; + + int height = 0; + + para_list = paragraphs; + while (para_list) + { + Paragraph *cur_para = para_list->data; + + if (cur_para == para) + { + PangoRectangle pos; + + pango_layout_index_to_pos (cur_para->layout, index, &pos); + + rect->x = MIN (pos.x, pos.x + pos.width) / 1000; + rect->width = ABS (pos.width) / 1000; + rect->y = height + pos.y / 1000; + rect->height = pos.height / 1000; + } + + height += cur_para->height; + para_list = para_list->next; + } +} + +/* XOR a rectangle over a given character + */ +void +xor_char (GtkWidget *layout, GdkRectangle *clip_rect, + Paragraph *para, int offset) +{ + static GdkGC *gc; + PangoRectangle rect; /* GdkRectangle in 1.2 is too limited + */ + if (!gc) + { + GdkGCValues values; + values.foreground = layout->style->white.pixel ? + layout->style->white : layout->style->black; + values.function = GDK_XOR; + gc = gdk_gc_new_with_values (GTK_LAYOUT (layout)->bin_window, + &values, + GDK_GC_FOREGROUND | GDK_GC_FUNCTION); + } + + gdk_gc_set_clip_rectangle (gc, clip_rect); + + char_bounds (para, offset, layout->allocation.width, &rect); + + rect.y -= GTK_LAYOUT (layout)->yoffset; + + if ((rect.y + rect.height >= 0) && (rect.y < layout->allocation.height)) + gdk_draw_rectangle (GTK_LAYOUT (layout)->bin_window, gc, TRUE, + rect.x, rect.y, rect.width, rect.height); +} + +/* Handle a size allocation by re-laying-out each paragraph to + * the new width, setting the new size for the layout and + * then queing a redraw + */ +void +size_allocate (GtkWidget *layout, GtkAllocation *allocation) +{ + GList *tmp_list; + int height = 0; + PangoDirection base_dir = pango_context_get_base_dir (context); + + PING(); + tmp_list = paragraphs; + while (tmp_list) + { + Paragraph *para = tmp_list->data; + PangoRectangle logical_rect; + + tmp_list = tmp_list->next; + + pango_layout_set_alignment (para->layout, + base_dir == PANGO_DIRECTION_LTR ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT); + pango_layout_set_width (para->layout, layout->allocation.width * 1000); + + pango_layout_get_extents (para->layout, NULL, &logical_rect); + para->height = logical_rect.height / 1000; + + height += para->height; + } + + gtk_layout_set_size (GTK_LAYOUT (layout), allocation->width, height); + + if (GTK_LAYOUT (layout)->yoffset + allocation->height > height) + gtk_adjustment_set_value (GTK_LAYOUT (layout)->vadjustment, height - allocation->height); +} + +/* Handle a draw/expose by finding the paragraphs that intersect + * the region and reexposing them. + */ +void +draw (GtkWidget *layout, GdkRectangle *area) +{ + GList *tmp_list; + int height = 0; + + PING(); + gdk_draw_rectangle (GTK_LAYOUT (layout)->bin_window, + layout->style->base_gc[layout->state], + TRUE, + area->x, area->y, + area->width, area->height); + + gdk_gc_set_clip_rectangle (layout->style->text_gc[layout->state], area); + + PING(); + tmp_list = paragraphs; + while (tmp_list && + height < area->y + area->height + GTK_LAYOUT (layout)->yoffset) + { + Paragraph *para = tmp_list->data; + tmp_list = tmp_list->next; + + PING(); + if (height + para->height >= GTK_LAYOUT (layout)->yoffset + area->y) + { + GdkGCValuesMask mask = GDK_GC_FOREGROUND|GDK_GC_BACKGROUND; + HDC hdc = gdk_win32_hdc_get (GDK_WINDOW_XWINDOW (GTK_LAYOUT (layout)->bin_window), + layout->style->text_gc[GTK_STATE_NORMAL], + mask); + + pango_win32_render_layout (hdc, para->layout, + 0, height - GTK_LAYOUT (layout)->yoffset); + + gdk_win32_hdc_release (GDK_WINDOW_XWINDOW (GTK_LAYOUT (layout)->bin_window), + layout->style->text_gc[GTK_STATE_NORMAL], + mask); + } + + height += para->height; + } + + PING(); + gdk_gc_set_clip_rectangle (layout->style->text_gc[layout->state], NULL); + + if (highlight_para) + xor_char (layout, area, highlight_para, highlight_offset); + PING(); +} + +gboolean +expose (GtkWidget *layout, GdkEventExpose *event) +{ + PING(); + if (event->window == GTK_LAYOUT (layout)->bin_window) + draw (layout, &event->area); + + return TRUE; +} + +void +button_press (GtkWidget *layout, GdkEventButton *event) +{ + Paragraph *para = NULL; + int offset; + gchar *message; + + PING(); + xy_to_cp (layout->allocation.width, + event->x, event->y + GTK_LAYOUT (layout)->yoffset, + ¶, &offset); + + if (highlight_para) + xor_char (layout, NULL, highlight_para, highlight_offset); + + highlight_para = para; + highlight_offset = offset; + + if (para) + { + gunichar wc; + + wc = g_utf8_get_char (para->text + offset); + message = g_strdup_printf ("Current char: U%04x", wc); + + xor_char (layout, NULL, highlight_para, highlight_offset); + } + else + message = g_strdup_printf ("Current char:"); + + PING(); + gtk_label_set_text (GTK_LABEL (message_label), message); + g_free (message); +} + +static void +checkbutton_toggled (GtkWidget *widget, gpointer data) +{ + GList *para_list; + + PING(); + pango_context_set_base_dir (context, GTK_TOGGLE_BUTTON (widget)->active ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR); + + para_list = paragraphs; + while (para_list) + { + Paragraph *para = para_list->data; + + pango_layout_context_changed (para->layout); + para_list = para_list->next; + } + + gtk_widget_queue_resize (layout); +} + +static void +reload_font () +{ + GList *para_list; + + PING(); + pango_context_set_font_description (context, &font_description); + + para_list = paragraphs; + while (para_list) + { + Paragraph *para = para_list->data; + + pango_layout_context_changed (para->layout); + para_list = para_list->next; + } + + if (layout) + gtk_widget_queue_resize (layout); + PING(); +} + +void +set_family (GtkWidget *entry, gpointer data) +{ + font_description.family_name = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1); + fill_styles_combo (styles_combo); +} + +void +set_style (GtkWidget *entry, gpointer data) +{ + char *str = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1); + PangoFontDescription *tmp_desc; + + PING(); + tmp_desc = pango_font_description_from_string (str); + + font_description.style = tmp_desc->style; + font_description.variant = tmp_desc->variant; + font_description.weight = tmp_desc->weight; + font_description.stretch = tmp_desc->stretch; + + pango_font_description_free (tmp_desc); + g_free (str); + + reload_font (); +} + +void +font_size_changed (GtkAdjustment *adj) +{ + PING(); + font_description.size = (int)(adj->value * PANGO_SCALE + 0.5); + reload_font(); +} + +static int +compare_font_descriptions (const PangoFontDescription *a, const PangoFontDescription *b) +{ + int val = strcmp (a->family_name, b->family_name); + if (val != 0) + return val; + + if (a->weight != b->weight) + return a->weight - b->weight; + + if (a->style != b->style) + return a->style - b->style; + + if (a->stretch != b->stretch) + return a->stretch - b->stretch; + + if (a->variant != b->variant) + return a->variant - b->variant; + + return 0; +} + +static int +font_description_sort_func (const void *a, const void *b) +{ + return compare_font_descriptions (*(PangoFontDescription **)a, *(PangoFontDescription **)b); +} + +typedef struct +{ + PangoFontDescription **descs; + int n_descs; +} FontDescInfo; + +static void +free_info (FontDescInfo *info) +{ + pango_font_descriptions_free (info->descs, info->n_descs); +} + +static void +fill_styles_combo (GtkWidget *combo) +{ + int i; + GList *style_list = NULL; + + FontDescInfo *info = g_new (FontDescInfo, 1); + PING(); + pango_context_list_fonts (context, font_description.family_name, &info->descs, &info->n_descs); + gtk_object_set_data_full (GTK_OBJECT (combo), "descs", info, (GtkDestroyNotify)free_info); + PING(); + + qsort (info->descs, info->n_descs, sizeof(PangoFontDescription *), font_description_sort_func); + + PING(); + for (i=0; i<info->n_descs; i++) + { + char *str; + + PangoFontDescription tmp_desc; + + tmp_desc = *info->descs[i]; + tmp_desc.family_name = NULL; + tmp_desc.size = 0; + + str = pango_font_description_to_string (&tmp_desc); + style_list = g_list_prepend (style_list, str); + } + + style_list = g_list_reverse (style_list); + + PING(); + gtk_combo_set_popdown_strings (GTK_COMBO (combo), style_list); + g_list_foreach (style_list, (GFunc)g_free, NULL); +} + +static GtkWidget * +make_styles_combo () +{ + GtkWidget *combo; + + PING(); + combo = gtk_combo_new (); + gtk_combo_set_value_in_list (GTK_COMBO (combo), TRUE, FALSE); + gtk_editable_set_editable (GTK_EDITABLE (GTK_COMBO (combo)->entry), FALSE); + + gtk_signal_connect (GTK_OBJECT (GTK_COMBO (combo)->entry), "changed", + GTK_SIGNAL_FUNC (set_style), NULL); + + styles_combo = combo; + fill_styles_combo (combo); + + PING(); + return combo; +} + +static int +cmp_strings (const void *a, const void *b) +{ + return strcmp (*(const char **)a, *(const char **)b); +} + +GtkWidget * +make_families_menu () +{ + GtkWidget *combo; + gchar **families; + int n_families; + GList *family_list = NULL; + int i; + + PING(); + pango_context_list_families (context, &families, &n_families); + qsort (families, n_families, sizeof(char *), cmp_strings); + + PING(); + for (i=0; i<n_families; i++) + family_list = g_list_prepend (family_list, families[i]); + + family_list = g_list_reverse (family_list); + + combo = gtk_combo_new (); + gtk_combo_set_popdown_strings (GTK_COMBO (combo), family_list); + gtk_combo_set_value_in_list (GTK_COMBO (combo), TRUE, FALSE); + gtk_editable_set_editable (GTK_EDITABLE (GTK_COMBO (combo)->entry), FALSE); + + gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (combo)->entry), font_description.family_name); + + gtk_signal_connect (GTK_OBJECT (GTK_COMBO (combo)->entry), "changed", + GTK_SIGNAL_FUNC (set_family), NULL); + + g_list_free (family_list); + pango_font_map_free_families (families, n_families); + + PING(); + return combo; +} + + +GtkWidget * +make_font_selector (void) +{ + GtkWidget *hbox; + GtkWidget *util_hbox; + GtkWidget *label; + GtkWidget *option_menu; + GtkWidget *spin_button; + GtkAdjustment *adj; + + hbox = gtk_hbox_new (FALSE, 4); + + util_hbox = gtk_hbox_new (FALSE, 2); + label = gtk_label_new ("Family:"); + gtk_box_pack_start (GTK_BOX (util_hbox), label, FALSE, FALSE, 0); + option_menu = make_families_menu (); + gtk_box_pack_start (GTK_BOX (util_hbox), option_menu, FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (hbox), util_hbox, FALSE, FALSE, 0); + + util_hbox = gtk_hbox_new (FALSE, 2); + label = gtk_label_new ("Style:"); + gtk_box_pack_start (GTK_BOX (util_hbox), label, FALSE, FALSE, 0); + option_menu = make_styles_combo (); + gtk_box_pack_start (GTK_BOX (util_hbox), option_menu, FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (hbox), util_hbox, FALSE, FALSE, 0); + + util_hbox = gtk_hbox_new (FALSE, 2); + label = gtk_label_new ("Size:"); + gtk_box_pack_start (GTK_BOX (util_hbox), label, FALSE, FALSE, 0); + spin_button = gtk_spin_button_new (NULL, 1., 0); + gtk_box_pack_start (GTK_BOX (util_hbox), spin_button, FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (hbox), util_hbox, FALSE, FALSE, 0); + + adj = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (spin_button)); + adj->value = font_description.size / 1000.; + adj->lower = 0; + adj->upper = 1024; + adj->step_increment = 1; + adj->page_size = 10; + gtk_adjustment_changed (adj); + gtk_adjustment_value_changed (adj); + + gtk_signal_connect (GTK_OBJECT (adj), "value_changed", + GTK_SIGNAL_FUNC (font_size_changed), NULL); + + return hbox; +} + +int +main (int argc, char **argv) +{ + char *text; + GtkWidget *window; + GtkWidget *scrollwin; + GtkWidget *vbox, *hbox; + GtkWidget *frame; + GtkWidget *checkbutton; + + gtk_init (&argc, &argv); + + if (argc != 2) + { + fprintf (stderr, "Usage: %s FILE\n", g_get_prgname ()); + exit(1); + } + + /* Create the list of paragraphs from the supplied file + */ + text = read_file (argv[1]); + if (!text) + exit(1); + + context = pango_win32_get_context (); + + paragraphs = split_paragraphs (text); + + pango_context_set_lang (context, "en_US"); + pango_context_set_base_dir (context, PANGO_DIRECTION_LTR); + + font_description.family_name = g_strdup ("sans"); + font_description.style = PANGO_STYLE_NORMAL; + font_description.variant = PANGO_VARIANT_NORMAL; + font_description.weight = 500; + font_description.stretch = PANGO_STRETCH_NORMAL; + font_description.size = 16000; + + pango_context_set_font_description (context, &font_description); + + /* Create the user interface + */ + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_default_size (GTK_WINDOW (window), 400, 400); + + gtk_signal_connect (GTK_OBJECT (window), "destroy", + GTK_SIGNAL_FUNC (gtk_main_quit), NULL); + + vbox = gtk_vbox_new (FALSE, 4); + gtk_container_add (GTK_CONTAINER (window), vbox); + + hbox = make_font_selector (); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + + scrollwin = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollwin), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + + gtk_box_pack_start (GTK_BOX (vbox), scrollwin, TRUE, TRUE, 0); + + layout = gtk_layout_new (NULL, NULL); + gtk_widget_set_events (layout, GDK_BUTTON_PRESS_MASK); + gtk_widget_set_app_paintable (layout, TRUE); + + gtk_signal_connect (GTK_OBJECT (layout), "size_allocate", + GTK_SIGNAL_FUNC (size_allocate), paragraphs); + gtk_signal_connect (GTK_OBJECT (layout), "expose_event", + GTK_SIGNAL_FUNC (expose), paragraphs); + gtk_signal_connect (GTK_OBJECT (layout), "draw", + GTK_SIGNAL_FUNC (draw), paragraphs); + gtk_signal_connect (GTK_OBJECT (layout), "button_press_event", + GTK_SIGNAL_FUNC (button_press), paragraphs); + + gtk_container_add (GTK_CONTAINER (scrollwin), layout); + + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); + gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); + + PING(); + message_label = gtk_label_new ("Current char:"); + gtk_misc_set_padding (GTK_MISC (message_label), 1, 1); + gtk_misc_set_alignment (GTK_MISC (message_label), 0.0, 0.5); + gtk_container_add (GTK_CONTAINER (frame), message_label); + + checkbutton = gtk_check_button_new_with_label ("Use RTL global direction"); + gtk_signal_connect (GTK_OBJECT (checkbutton), "toggled", + GTK_SIGNAL_FUNC (checkbutton_toggled), NULL); + gtk_box_pack_start (GTK_BOX (vbox), checkbutton, FALSE, FALSE, 0); + + gtk_widget_show_all (window); + + PING(); + gtk_main (); + + return 0; +} |