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