📄 viewer-win32.c
字号:
/* 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 <config.h>#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 1024typedef 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]; file = fopen (name, "r"); if (!file) { fprintf (stderr, "%s: Cannot open %s\n", g_get_prgname (), name); 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 (), name); 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. */gbooleanxy_to_cp (int width, int x, int y, Paragraph **para_return, int *index){ GList *para_list; int height = 0; *para_return = NULL; 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 * PANGO_SCALE, (y - height) * PANGO_SCALE, index, NULL); if (result && para_return) *para_return = para; return result; } height += para->height; para_list = para_list->next; } return FALSE;}/* Given a paragraph and offset in that paragraph, find the * bounding rectangle for the character at the offset. */voidchar_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 = PANGO_PIXELS (MIN (pos.x, pos.x + pos.width)); rect->width = PANGO_PIXELS (ABS (pos.width)); rect->y = height + PANGO_PIXELS (pos.y); rect->height = PANGO_PIXELS (pos.height); } height += cur_para->height; para_list = para_list->next; }}/* XOR a rectangle over a given character */voidxor_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 */voidsize_allocate (GtkWidget *layout, GtkAllocation *allocation){ GList *tmp_list; guint height = 0; PangoDirection base_dir = pango_context_get_base_dir (context); 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 * PANGO_SCALE); pango_layout_get_extents (para->layout, NULL, &logical_rect); para->height = PANGO_PIXELS (logical_rect.height); 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, (float)(height - allocation->height));}/* Handle a draw/expose by finding the paragraphs that intersect * the region and reexposing them. */voiddraw (GtkWidget *layout, GdkRectangle *area){ GList *tmp_list; guint height = 0; HDC hdc; const GdkGCValuesMask mask = GDK_GC_FOREGROUND|GDK_GC_BACKGROUND|GDK_GC_FONT; 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); hdc = gdk_win32_hdc_get (GTK_LAYOUT (layout)->bin_window, layout->style->text_gc[GTK_STATE_NORMAL], mask); 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; if (height + para->height >= GTK_LAYOUT (layout)->yoffset + area->y) pango_win32_render_layout (hdc, para->layout, 0, height - GTK_LAYOUT (layout)->yoffset); height += para->height; } gdk_win32_hdc_release (GTK_LAYOUT (layout)->bin_window, layout->style->text_gc[GTK_STATE_NORMAL], mask); gdk_gc_set_clip_rectangle (layout->style->text_gc[layout->state], NULL); if (highlight_para) xor_char (layout, area, highlight_para, highlight_offset);}gbooleanexpose (GtkWidget *layout, GdkEventExpose *event){ if (event->window == GTK_LAYOUT (layout)->bin_window) draw (layout, &event->area); return TRUE;}voidbutton_press (GtkWidget *layout, GdkEventButton *event){ Paragraph *para = NULL; int offset; gchar *message; 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:"); gtk_label_set_text (GTK_LABEL (message_label), message); g_free (message);}static voidcheckbutton_toggled (GtkWidget *widget, gpointer data){ GList *para_list; 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -