2 * This file is part of DisOrder
3 * Copyright (C) 2007 Richard Kettlewell
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 /** @file disobedience/help.c
24 #include "disobedience.h"
29 VECTOR_TYPE(markstack
, GtkTextMark
*, xrealloc
);
31 /** @brief Known tag type */
33 /** @brief HTML tag name */
36 /** @brief Called to set up the tag */
37 void (*init
)(GtkTextTag
*tag
);
39 /** @brief GTK+ tag object */
43 /** @brief Initialize the bold tag
45 * This doesn't seem to work on OS X though the italic and monospace tags are
46 * fine, and bold is OK on Linux, even connecting to the Apple X swerver.
48 static void init_bold(GtkTextTag
*tag
) {
49 g_object_set(G_OBJECT(tag
), "weight", PANGO_WEIGHT_BOLD
, (char *)0);
52 /** @brief Initialize the italic tag */
53 static void init_italic(GtkTextTag
*tag
) {
54 g_object_set(G_OBJECT(tag
), "style", PANGO_STYLE_ITALIC
, (char *)0);
57 /** @brief Initialize the pre tag */
58 static void init_pre(GtkTextTag
*tag
) {
59 g_object_set(G_OBJECT(tag
), "family", "monospace", (char *)0);
62 /** @brief Table of known tags
64 * Keep in alphabetical order
66 static struct tag tags
[] = {
67 { "b", init_bold
, 0 },
68 { "i", init_italic
, 0 },
69 { "pre", init_pre
, 0 }
72 /** @brief Number of known tags */
73 #define NTAGS (sizeof tags / sizeof *tags)
75 /** @brief State structure for insert_html() */
77 /** @brief The buffer to insert into */
78 GtkTextBuffer
*buffer
;
80 /** @brief True if we are inside <body> */
83 /** @brief True if inside <pre> */
86 /** @brief True if a space is required before any non-space */
89 /** @brief Stack of marks corresponding to tags */
90 struct markstack marks
[1];
94 /** @brief Called for an open tag */
95 static void html_open(const char *tag
,
96 hash
attribute((unused
)) *attrs
,
98 struct state
*const s
= u
;
101 if(!strcmp(tag
, "body"))
103 else if(!strcmp(tag
, "pre"))
107 /* push a mark for the start of the region */
108 gtk_text_buffer_get_iter_at_mark(s
->buffer
, iter
,
109 gtk_text_buffer_get_insert(s
->buffer
));
110 markstack_append(s
->marks
,
111 gtk_text_buffer_create_mark(s
->buffer
,
114 TRUE
/*left_gravity*/));
117 /** @brief Called for a close tag */
118 static void html_close(const char *tag
,
120 struct state
*const s
= u
;
121 GtkTextIter start
[1], end
[1];
124 if(!strcmp(tag
, "body"))
126 else if(!strcmp(tag
, "pre")) {
132 /* see if this is a known tag */
133 if((n
= TABLE_FIND(tags
, struct tag
, name
, tag
)) < 0)
135 /* pop the mark at the start of the region */
136 assert(s
->marks
->nvec
> 0);
137 gtk_text_buffer_get_iter_at_mark(s
->buffer
, start
,
138 s
->marks
->vec
[--s
->marks
->nvec
]);
139 gtk_text_buffer_get_iter_at_mark(s
->buffer
, end
,
140 gtk_text_buffer_get_insert(s
->buffer
));
142 gtk_text_buffer_apply_tag(s
->buffer
, tags
[n
].tag
, start
, end
);
143 /* don't need the start mark any more */
144 gtk_text_buffer_delete_mark(s
->buffer
, s
->marks
->vec
[s
->marks
->nvec
]);
147 /** @brief Called for text */
148 static void html_text(const char *text
,
150 struct state
*const s
= u
;
156 char *formatted
= xmalloc(strlen(text
) + 1), *t
= formatted
;
157 /* normalize spacing */
159 if(isspace((unsigned char)*text
)) {
173 gtk_text_buffer_insert_at_cursor(s
->buffer
, text
, strlen(text
));
176 /** @brief Callbacks for insert_html() */
177 static const struct html_parser_callbacks insert_html_callbacks
= {
183 /** @brief Insert @p html into @p buffer at cursor */
184 static void insert_html(GtkTextBuffer
*buffer
,
188 GtkTextTagTable
*tagtable
;
190 memset(s
, 0, sizeof *s
);
192 markstack_init(s
->marks
);
193 /* initialize tags */
195 for(n
= 0; n
< NTAGS
; ++n
)
196 tags
[n
].init(tags
[n
].tag
= gtk_text_tag_new(0));
197 /* add tags to this buffer */
198 tagtable
= gtk_text_buffer_get_tag_table(s
->buffer
);
199 for(n
= 0; n
< NTAGS
; ++n
)
200 gtk_text_tag_table_add(tagtable
, tags
[n
].tag
);
201 /* convert the input */
202 html_parse(&insert_html_callbacks
, html
, s
);
205 /** @brief Create a GtkTextBuffer with @p html rendered into it */
206 static GtkTextBuffer
*html_buffer(const char *html
) {
207 GtkTextBuffer
*buffer
= gtk_text_buffer_new(NULL
);
209 insert_html(buffer
, html
);
213 /** @brief The manual page window */
214 static GtkWidget
*help_window
;
216 /** @brief Pop up the manual page in a window */
217 void popup_help(void) {
221 gtk_window_present(GTK_WINDOW(help_window
));
224 help_window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
225 g_signal_connect(help_window
, "destroy",
226 G_CALLBACK(gtk_widget_destroyed
), &help_window
);
227 gtk_window_set_title(GTK_WINDOW(help_window
), "Disobedience Manual Page");
228 view
= gtk_text_view_new_with_buffer(html_buffer(manual
));
229 gtk_text_view_set_editable(GTK_TEXT_VIEW(view
), FALSE
);
230 gtk_container_add(GTK_CONTAINER(help_window
),
233 gtk_window_set_default_size(GTK_WINDOW(help_window
), 512, 512);
234 gtk_widget_show_all(help_window
);