1 /* $Id$ */
2
3 /* textwidget.cc
4 *
5 * Copyright (C) 2001-2002 The gtkmm Development Team
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free
19 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include <cstring>
23 #include "textwidget.h"
24 #include "pangomm/fontdescription.h"
25
26 using std::strstr;
27 using std::strncmp;
28 using std::strlen;
29
30
TextWidget(bool is_source)31 TextWidget::TextWidget(bool is_source)
32 {
33 set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
34 set_shadow_type (Gtk::SHADOW_IN);
35
36 m_refTextBuffer = Gtk::TextBuffer::create();
37 m_TextView.set_buffer(m_refTextBuffer);
38 m_TextView.set_editable(false);
39 m_TextView.set_cursor_visible(false);
40 add(m_TextView);
41
42 if (is_source)
43 {
44 #ifndef G_OS_WIN32
45 Pango::FontDescription fontDesc("Courier 12");
46 m_TextView.modify_font(fontDesc);
47 #endif /* G_OS_WIN32 */
48 m_TextView.set_wrap_mode (Gtk::WRAP_NONE);
49
50 Glib::RefPtr<Gtk::TextBuffer::Tag> refTag = m_refTextBuffer->create_tag("comment");
51 refTag->property_foreground() = "red";
52
53 refTag = m_refTextBuffer->create_tag("type");
54 refTag->property_foreground() = "ForestGreen";
55
56 refTag = m_refTextBuffer->create_tag("string");
57
58 refTag->property_foreground() = "RosyBrown";
59 refTag->property_weight() = Pango::WEIGHT_BOLD;
60
61
62 refTag = m_refTextBuffer->create_tag("control");
63 refTag->property_foreground() = "purple";
64
65 refTag = m_refTextBuffer->create_tag("preprocessor");
66 refTag->property_style() = Pango::STYLE_OBLIQUE;
67 refTag->property_foreground() = "burlywood4";
68
69 refTag = m_refTextBuffer->create_tag("function");
70 refTag->property_weight() = Pango::WEIGHT_BOLD;
71 refTag->property_foreground() = "DarkGoldenrod4";
72 }
73 else
74 {
75 // Make it a bit nicer for text.
76 m_TextView.set_wrap_mode (Gtk::WRAP_WORD);
77 m_TextView.set_pixels_above_lines(2);
78 m_TextView.set_pixels_below_lines(2);
79
80 Glib::RefPtr<Gtk::TextBuffer::Tag> refTag = m_refTextBuffer->create_tag("title");
81 refTag->property_font() = "Sans 18";
82
83 }
84 }
85
86
~TextWidget()87 TextWidget::~TextWidget()
88 {
89 }
90
get_buffer()91 Glib::RefPtr<Gtk::TextBuffer> TextWidget::get_buffer()
92 {
93 return m_refTextBuffer;
94 }
95
wipe()96 void TextWidget::wipe()
97 {
98 Gtk::TextBuffer::iterator start, end;
99 m_refTextBuffer->get_bounds(start, end);
100 m_refTextBuffer->erase(start, end);
101 }
102
103
104 /* Stupid syntax highlighting.
105 *
106 * No regex was used in the making of this highlighting.
107 * It should only work for simple cases. This is good, as
108 * that's all we should have in the demos.
109 */
110 /* This code should not be used elsewhere, except perhaps as an example of how
111 * to iterate through a text buffer.
112 */
113 enum enumStates
114 {
115 STATE_NORMAL,
116 STATE_IN_COMMENT
117 };
118
119 static const char* tokens[] =
120 {
121 "/*",
122 "\"",
123 NULL
124 };
125
126 static const char* types[] =
127 {
128 "static",
129 "const ",
130 "void",
131 "gint",
132 "int ",
133 "char ",
134 "gchar ",
135 "gfloat",
136 "float",
137 "gint8",
138 "gint16",
139 "gint32",
140 "guint",
141 "guint8",
142 "guint16",
143 "guint32",
144 "guchar",
145 "glong",
146 "gboolean" ,
147 "gshort",
148 "gushort",
149 "gulong",
150 "gdouble",
151 "gldouble",
152 "gpointer",
153 "NULL",
154 "GList",
155 "GSList",
156 "FALSE",
157 "TRUE",
158 "FILE ",
159 "GtkObject ",
160 "GtkColorSelection ",
161 "GtkWidget ",
162 "GtkButton ",
163 "GdkColor ",
164 "GdkRectangle ",
165 "GdkEventExpose ",
166 "GdkGC ",
167 "GdkPixbufLoader ",
168 "GdkPixbuf ",
169 "GError",
170 "size_t",
171 0
172 };
173
174 static const char* control[] =
175 {
176 " if ",
177 " while ",
178 " else",
179 " do ",
180 " for ",
181 "?",
182 ":",
183 "return ",
184 "goto ",
185 0
186 };
187
188 typedef const char* constpch;
189
190 void
parse_chars(constpch text,constpch * end_ptr,enumStates * state,constpch * tag,bool start)191 parse_chars (constpch text,
192 constpch* end_ptr,
193 enumStates* state,
194 constpch* tag,
195 bool start)
196 {
197 int i = 0;
198 const char* next_token = 0;
199
200 /* Handle comments first */
201 if(*state == STATE_IN_COMMENT)
202 {
203 *end_ptr = strstr (text, "*/");
204 if (*end_ptr)
205 {
206 *end_ptr += 2;
207 *state = STATE_NORMAL;
208 *tag = "comment";
209 }
210
211 return;
212 }
213
214 *tag = 0;
215 *end_ptr = 0;
216
217 /* check for comment */
218 if (!strncmp (text, "/*", 2))
219 {
220 *end_ptr = strstr (text, "*/");
221
222 if (*end_ptr)
223 *end_ptr += 2;
224 else
225 *state = STATE_IN_COMMENT;
226 *tag = "comment";
227
228 return;
229 }
230
231 /* check for preprocessor defines */
232 if (*text == '#' && start)
233 {
234 *end_ptr = 0;
235 *tag = "preprocessor";
236 return;
237 }
238
239 /* functions */
240 if (start && * text != '\t' && *text != ' ' && *text != '{' && *text != '}')
241 {
242 if (strstr (text, "("))
243 {
244 *end_ptr = strstr (text, "(");
245 *tag = "function";
246 return;
247 }
248 }
249
250 /* check for types */
251 for (i = 0; types[i] != 0; i++)
252 if (!strncmp (text, types[i], strlen (types[i])))
253 {
254 *end_ptr = text + strlen (types[i]);
255 *tag = "type";
256 return;
257 }
258
259 /* check for control */
260 for (i = 0; control[i] != 0; i++)
261 if (!strncmp (text, control[i], strlen (control[i])))
262 {
263 *end_ptr = text + strlen (control[i]);
264 *tag = "control";
265 return;
266 }
267
268 /* check for string */
269 if (text[0] == '"')
270 {
271 int maybe_escape = false;
272
273 *end_ptr = text + 1;
274 *tag = "string";
275
276 while (**end_ptr != '\000')
277 {
278 if (**end_ptr == '\"' && !maybe_escape)
279 {
280 *end_ptr += 1;
281 return;
282 }
283
284 if (**end_ptr == '\\')
285 maybe_escape = true;
286 else
287 maybe_escape = false;
288 *end_ptr += 1;
289 }
290 return;
291 }
292
293 /* not at the start of a tag. Find the next one. */
294 for (i = 0; tokens[i] != 0; i++)
295 {
296 next_token = strstr (text, tokens[i]);
297 if (next_token)
298 {
299 if (*end_ptr)
300 *end_ptr = (*end_ptr<next_token)?*end_ptr:next_token;
301 else
302 *end_ptr = next_token;
303 }
304 }
305
306 for (i = 0; types[i] != 0; i++)
307 {
308 next_token = strstr (text, types[i]);
309 if (next_token)
310 {
311 if (*end_ptr)
312 *end_ptr = (*end_ptr<next_token)?*end_ptr:next_token;
313 else
314 *end_ptr = next_token;
315 }
316 }
317
318 for (i = 0; control[i] != 0; i++)
319 {
320 next_token = strstr (text, control[i]);
321 if (next_token)
322 {
323 if (*end_ptr)
324 *end_ptr = (*end_ptr<next_token)?*end_ptr:next_token;
325 else
326 *end_ptr = next_token;
327 }
328 }
329
330 }
331
332
333 /* While not as cool as c-mode, this will do as a quick attempt at highlighting */
fontify()334 void TextWidget::fontify()
335 {
336 enumStates state = STATE_NORMAL;
337
338 Gtk::TextBuffer::iterator iterStart = m_refTextBuffer->get_iter_at_offset(0);
339
340 Gtk::TextBuffer::iterator iterNext = iterStart;
341 while(iterNext.forward_line())
342 {
343 bool start = true;
344 const Glib::ustring& str = iterStart.get_text(iterNext);
345 const gchar* start_ptr = str.c_str();
346
347 const gchar* end_ptr = 0;
348 const gchar* tag = 0;
349
350 do
351 {
352 parse_chars (start_ptr, &end_ptr, &state, &tag, start);
353
354 start = false;
355 Gtk::TextBuffer::iterator iterTmp;
356 if(end_ptr)
357 {
358 iterTmp = iterStart;
359 iterTmp.forward_chars(end_ptr - start_ptr);
360 }
361 else
362 {
363 iterTmp = iterNext;
364 }
365
366 if(tag)
367 m_refTextBuffer->apply_tag_by_name(tag, iterStart, iterTmp);
368
369 iterStart = iterTmp;
370 start_ptr = end_ptr;
371 }
372 while(end_ptr);
373
374 iterStart = iterNext;
375 }
376 }
377
378
379
380
381