help menu can now pop up the man page
[disorder] / disobedience / help.c
1 /*
2 * This file is part of DisOrder
3 * Copyright (C) 2007 Richard Kettlewell
4 *
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.
9 *
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.
14 *
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
18 * USA
19 */
20
21 #include "disobedience.h"
22 #include "table.h"
23 #include "html.h"
24 #include "manual.h"
25
26 VECTOR_TYPE(markstack, GtkTextMark *, xrealloc);
27
28 /** @brief Known tag type */
29 struct tag {
30 /** @brief HTML tag name */
31 const char *name;
32
33 /** @brief Called to set up the tag */
34 void (*init)(GtkTextTag *tag);
35
36 /** @brief GTK+ tag object */
37 GtkTextTag *tag;
38 };
39
40 static void init_bold(GtkTextTag *tag) {
41 g_object_set(G_OBJECT(tag), "weight", PANGO_WEIGHT_BOLD, (char *)0);
42 }
43
44 static void init_italic(GtkTextTag *tag) {
45 g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_ITALIC, (char *)0);
46 }
47
48 /** @brief Table of known tags
49 *
50 * Keep in alphabetical order
51 */
52 static struct tag tags[] = {
53 { "b", init_bold, 0 },
54 { "i", init_italic, 0 }
55 };
56
57 /** @brief Number of known tags */
58 #define NTAGS (sizeof tags / sizeof *tags)
59
60 /** @brief State structure for insert_html() */
61 struct state {
62 /** @brief The buffer to insert into */
63 GtkTextBuffer *buffer;
64
65 /** @brief True if we are inside <body> */
66 int body;
67
68 /** @brief Stack of marks corresponding to tags */
69 struct markstack marks[1];
70
71 };
72
73 /** @brief Called for an open tag */
74 static void html_open(const char *tag,
75 hash attribute((unused)) *attrs,
76 void *u) {
77 struct state *const s = u;
78 GtkTextIter iter[1];
79
80 if(!strcmp(tag, "body"))
81 s->body = 1;
82 if(!s->body)
83 return;
84 /* push a mark for the start of the region */
85 gtk_text_buffer_get_iter_at_mark(s->buffer, iter,
86 gtk_text_buffer_get_insert(s->buffer));
87 markstack_append(s->marks,
88 gtk_text_buffer_create_mark(s->buffer,
89 0/* mark_name */,
90 iter,
91 TRUE/*left_gravity*/));
92 }
93
94 /** @brief Called for a close tag */
95 static void html_close(const char *tag,
96 void *u) {
97 struct state *const s = u;
98 GtkTextIter start[1], end[1];
99 int n;
100
101 if(!strcmp(tag, "body"))
102 s->body = 0;
103 if(!s->body)
104 return;
105 /* see if this is a known tag */
106 if((n = TABLE_FIND(tags, struct tag, name, tag)) < 0)
107 return;
108 /* pop the mark at the start of the region */
109 assert(s->marks->nvec > 0);
110 gtk_text_buffer_get_iter_at_mark(s->buffer, start,
111 s->marks->vec[--s->marks->nvec]);
112 gtk_text_buffer_get_iter_at_mark(s->buffer, end,
113 gtk_text_buffer_get_insert(s->buffer));
114 /* apply a tag */
115 gtk_text_buffer_apply_tag(s->buffer, tags[n].tag, start, end);
116 /* don't need the start mark any more */
117 gtk_text_buffer_delete_mark(s->buffer, s->marks->vec[s->marks->nvec]);
118 }
119
120 /** @brief Called for text */
121 static void html_text(const char *text,
122 void *u) {
123 struct state *const s = u;
124
125 /* ignore header */
126 if(!s->body)
127 return;
128 gtk_text_buffer_insert_at_cursor(s->buffer, text, strlen(text));
129 }
130
131 /** @brief Callbacks for insert_html() */
132 static const struct html_parser_callbacks insert_html_callbacks = {
133 html_open,
134 html_close,
135 html_text
136 };
137
138 /** @brief Insert @p html into @p buffer at cursor */
139 static void insert_html(GtkTextBuffer *buffer,
140 const char *html) {
141 struct state s[1];
142 size_t n;
143 GtkTextTagTable *tagtable;
144
145 memset(s, 0, sizeof *s);
146 s->buffer = buffer;
147 markstack_init(s->marks);
148 /* initialize tags */
149 if(!tags[0].tag)
150 for(n = 0; n < NTAGS; ++n)
151 tags[n].init(tags[n].tag = gtk_text_tag_new(0));
152 /* add tags to this buffer */
153 tagtable = gtk_text_buffer_get_tag_table(s->buffer);
154 for(n = 0; n < NTAGS; ++n)
155 gtk_text_tag_table_add(tagtable, tags[n].tag);
156 /* convert the input */
157 html_parse(&insert_html_callbacks, html, s);
158 }
159
160 static GtkTextBuffer *html_buffer(const char *html) {
161 GtkTextBuffer *buffer = gtk_text_buffer_new(NULL);
162
163 insert_html(buffer, html);
164 return buffer;
165 }
166
167 static GtkWidget *help_window;
168
169 void popup_help(void) {
170 GtkWidget *view;
171
172 if(help_window) {
173 gtk_window_present(GTK_WINDOW(help_window));
174 return;
175 }
176 help_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
177 g_signal_connect(help_window, "destroy",
178 G_CALLBACK(gtk_widget_destroyed), &help_window);
179 gtk_window_set_title(GTK_WINDOW(help_window), "Disobedience Manual");
180 view = gtk_text_view_new_with_buffer(html_buffer(manual));
181 gtk_container_add(GTK_CONTAINER(help_window),
182 scroll_widget(view,
183 "help"));
184 gtk_widget_show_all(help_window);
185 }
186
187 /*
188 Local Variables:
189 c-basic-offset:2
190 comment-column:40
191 fill-column:79
192 indent-tabs-mode:nil
193 End:
194 */