]> Pileus Git - ~andy/gtk/blob - demos/gtk-demo/main.c
remove validation idle
[~andy/gtk] / demos / gtk-demo / main.c
1 #include <errno.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <ctype.h>
5
6 #include <gtk/gtk.h>
7
8 #include <demos.h>
9
10 static GtkTextBuffer *info_buffer;
11 static GtkTextBuffer *source_buffer;
12
13 static gchar *current_file = NULL;
14
15 enum {
16   TITLE_COLUMN,
17   FILENAME_COLUMN,
18   FUNC_COLUMN,
19   ITALIC_COLUMN,
20   NUM_COLUMNS
21 };
22
23 typedef struct _CallbackData CallbackData;
24 struct _CallbackData
25 {
26   GtkTreeModel *model;
27   GtkTreePath *path;
28 };
29
30 static void
31 window_closed_cb (GtkWidget *window, gpointer data)
32 {
33   CallbackData *cbdata = data;
34   GtkTreeIter iter;
35   gboolean italic;
36
37   gtk_tree_model_get_iter (cbdata->model, &iter, cbdata->path);
38   gtk_tree_store_get (GTK_TREE_STORE (cbdata->model), &iter,
39                       ITALIC_COLUMN, &italic,
40                       -1);
41   if (italic)
42     gtk_tree_store_set (GTK_TREE_STORE (cbdata->model), &iter,
43                         ITALIC_COLUMN, !italic,
44                         -1);
45
46   gtk_tree_path_free (cbdata->path);
47   g_free (cbdata);
48 }
49
50 gboolean
51 read_line (FILE *stream, GString *str)
52 {
53   int n_read = 0;
54   
55   flockfile (stream);
56
57   g_string_truncate (str, 0);
58   
59   while (1)
60     {
61       int c;
62       
63       c = getc_unlocked (stream);
64
65       if (c == EOF)
66         goto done;
67       else
68         n_read++;
69
70       switch (c)
71         {
72         case '\r':
73         case '\n':
74           {
75             int next_c = getc_unlocked (stream);
76             
77             if (!(next_c == EOF ||
78                   (c == '\r' && next_c == '\n') ||
79                   (c == '\n' && next_c == '\r')))
80               ungetc (next_c, stream);
81             
82             goto done;
83           }
84         default:
85           g_string_append_c (str, c);
86         }
87     }
88
89  done:
90
91   funlockfile (stream);
92
93   return n_read > 0;
94 }
95
96 void
97 load_file (const gchar *filename)
98 {
99   FILE *file;
100   GtkTextIter start, end;
101   GString *buffer = g_string_new (NULL);
102   int state = 0;
103   gboolean in_para = 0;
104
105   if (current_file && !strcmp (current_file, filename))
106     return;
107
108   g_free (current_file);
109   current_file = g_strdup (filename);
110   
111   gtk_text_buffer_get_bounds (info_buffer, &start, &end);
112   gtk_text_buffer_delete (info_buffer, &start, &end);
113
114   gtk_text_buffer_get_bounds (source_buffer, &start, &end);
115   gtk_text_buffer_delete (source_buffer, &start, &end);
116
117   file = fopen (filename, "r");
118
119   if (!file)
120     {
121       char *installed = g_strconcat (DEMOCODEDIR,
122                                      G_DIR_SEPARATOR_S,
123                                      filename,
124                                      NULL);
125
126       file = fopen (installed, "r");
127
128       g_free (installed);
129     }
130   
131   if (!file)
132     {
133       g_warning ("Cannot open %s: %s\n", filename, g_strerror (errno));
134       return;
135     }
136
137   gtk_text_buffer_get_iter_at_offset (info_buffer, &start, 0);
138   while (read_line (file, buffer))
139     {
140       gchar *p = buffer->str;
141       gchar *q;
142       
143       switch (state)
144         {
145         case 0:
146           /* Reading title */
147           while (*p == '/' || *p == '*' || isspace (*p))
148             p++;
149           q = p + strlen (p);
150           while (q > p && isspace (*(q - 1)))
151             q--;
152
153           if (q > p)
154             {
155               int len_chars = g_utf8_pointer_to_offset (p, q);
156
157               end = start;
158
159               g_assert (strlen (p) >= q - p);
160               gtk_text_buffer_insert (info_buffer, &end, p, q - p);
161               start = end;
162
163               gtk_text_iter_backward_chars (&start, len_chars);
164               gtk_text_buffer_apply_tag_by_name (info_buffer, "title", &start, &end);
165
166               start = end;
167               
168               state++;
169             }
170           break;
171             
172         case 1:
173           /* Reading body of info section */
174           while (isspace (*p))
175             p++;
176           if (*p == '*' && *(p + 1) == '/')
177             {
178               gtk_text_buffer_get_iter_at_offset (source_buffer, &start, 0);
179               state++;
180             }
181           else
182             {
183               int len;
184               
185               while (*p == '*' || isspace (*p))
186                 p++;
187
188               len = strlen (p);
189               while (isspace (*(p + len - 1)))
190                 len--;
191               
192               if (len > 0)
193                 {
194                   if (in_para)
195                     gtk_text_buffer_insert (info_buffer, &start, " ", 1);
196
197                   g_assert (strlen (p) >= len);
198                   gtk_text_buffer_insert (info_buffer, &start, p, len);
199                   in_para = 1;
200                 }
201               else
202                 {
203                   gtk_text_buffer_insert (info_buffer, &start, "\n", 1);
204                   in_para = 0;
205                 }
206             }
207           break;
208
209         case 2:
210           /* Skipping blank lines */
211           while (isspace (*p))
212             p++;
213           if (*p)
214             {
215               p = buffer->str;
216               state++;
217               /* Fall through */
218             }
219           else
220             break;
221           
222         case 3:
223           /* Reading program body */
224           gtk_text_buffer_insert (source_buffer, &start, p, -1);
225           gtk_text_buffer_insert (info_buffer, &start, "\n", 1);
226           break;
227         }
228     }
229
230   gtk_text_buffer_get_bounds (source_buffer, &start, &end);
231   gtk_text_buffer_apply_tag_by_name (info_buffer, "source", &start, &end);
232 }
233
234 gboolean
235 button_press_event_cb (GtkTreeView    *tree_view,
236                        GdkEventButton *event,
237                        GtkTreeModel   *model)
238 {
239   if (event->type == GDK_2BUTTON_PRESS)
240     {
241       GtkTreePath *path = NULL;
242
243       gtk_tree_view_get_path_at_pos (tree_view,
244                                      event->window,
245                                      event->x,
246                                      event->y,
247                                      &path,
248                                      NULL,
249                                      NULL,
250                                      NULL);
251
252       if (path)
253         {
254           GtkTreeIter iter;
255           gboolean italic;
256           GDoDemoFunc func;
257           GtkWidget *window;
258
259           gtk_tree_model_get_iter (model, &iter, path);
260           gtk_tree_store_get (GTK_TREE_STORE (model),
261                               &iter,
262                               FUNC_COLUMN, &func,
263                               ITALIC_COLUMN, &italic,
264                               -1);
265           gtk_tree_store_set (GTK_TREE_STORE (model),
266                               &iter,
267                               ITALIC_COLUMN, !italic,
268                               -1);
269           window = (func) ();
270           if (window != NULL)
271             {
272               CallbackData *cbdata;
273
274               cbdata = g_new (CallbackData, 1);
275               cbdata->model = model;
276               cbdata->path = path;
277
278               gtk_signal_connect (GTK_OBJECT (window),
279                                   "destroy",
280                                   window_closed_cb,
281                                   cbdata);
282             }
283           else
284             {
285               gtk_tree_path_free (path);
286             }
287         }
288
289       gtk_signal_emit_stop_by_name (GTK_OBJECT (tree_view),
290                                     "button_press_event");
291       return TRUE;
292     }
293   
294   return FALSE;
295 }
296
297 static void
298 selection_cb (GtkTreeSelection *selection,
299               GtkTreeModel     *model)
300 {
301   GtkTreeIter iter;
302   GValue value = {0, };
303
304   if (! gtk_tree_selection_get_selected (selection, NULL, &iter))
305     return;
306
307   gtk_tree_model_get_value (model, &iter,
308                             FILENAME_COLUMN,
309                             &value);
310   load_file (g_value_get_string (&value));
311   g_value_unset (&value);
312 }
313
314 static GtkWidget *
315 create_text (GtkTextBuffer **buffer,
316              gboolean        is_source)
317 {
318   GtkWidget *scrolled_window;
319   GtkWidget *text_view;
320   PangoFontDescription *font_desc;
321
322   scrolled_window = gtk_scrolled_window_new (NULL, NULL);
323   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
324                                   GTK_POLICY_AUTOMATIC,
325                                   GTK_POLICY_AUTOMATIC);
326   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
327                                        GTK_SHADOW_IN);
328   
329   text_view = gtk_text_view_new ();
330   gtk_container_add (GTK_CONTAINER (scrolled_window), text_view);
331   
332   *buffer = gtk_text_buffer_new (NULL);
333   gtk_text_view_set_buffer (GTK_TEXT_VIEW (text_view), *buffer);
334   gtk_text_view_set_editable (GTK_TEXT_VIEW (text_view), FALSE);
335   gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (text_view), FALSE);
336
337   if (is_source)
338     {
339       font_desc = pango_font_description_from_string ("Courier 10");
340       gtk_widget_modify_font (text_view, font_desc);
341       pango_font_description_free (font_desc);
342     }
343   
344   gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view), !is_source);
345   
346   return scrolled_window;
347 }
348
349 /* Technically a list, but if we do go to 80 demos, we may want to move to a tree */
350 static GtkWidget *
351 create_tree (void)
352 {
353   GtkTreeSelection *selection;
354   GtkCellRenderer *cell;
355   GtkWidget *tree_view;
356   GtkTreeViewColumn *column;
357   GtkTreeStore *model;
358   GtkTreeIter iter;
359   gint i;
360
361   model = gtk_tree_store_new_with_types (NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN);
362   tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
363   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
364
365   gtk_tree_selection_set_mode (GTK_TREE_SELECTION (selection),
366                                GTK_TREE_SELECTION_SINGLE);
367   gtk_widget_set_usize (tree_view, 200, -1);
368
369   for (i=0; i < G_N_ELEMENTS (testgtk_demos); i++)
370     {
371       gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
372
373       gtk_tree_store_set (GTK_TREE_STORE (model),
374                           &iter,
375                           TITLE_COLUMN, testgtk_demos[i].title,
376                           FILENAME_COLUMN, testgtk_demos[i].filename,
377                           FUNC_COLUMN, testgtk_demos[i].func,
378                           ITALIC_COLUMN, FALSE,
379                           -1);
380     }
381
382   cell = gtk_cell_renderer_text_new ();
383
384   g_object_set (G_OBJECT (cell),
385                 "style", PANGO_STYLE_ITALIC,
386                 NULL);
387   
388   column = gtk_tree_view_column_new_with_attributes ("Widget (double click for demo)",
389                                                      cell,
390                                                      "text", TITLE_COLUMN,
391                                                      "style_set", ITALIC_COLUMN,
392                                                      NULL);
393   
394   gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view),
395                                GTK_TREE_VIEW_COLUMN (column));
396
397   gtk_signal_connect (GTK_OBJECT (selection), "selection_changed", selection_cb, model);
398   gtk_signal_connect (GTK_OBJECT (tree_view), "button_press_event", GTK_SIGNAL_FUNC (button_press_event_cb), model);
399
400   return tree_view;
401 }
402
403 int
404 main (int argc, char **argv)
405 {
406   GtkWidget *window;
407   GtkWidget *notebook;
408   GtkWidget *hbox;
409   GtkWidget *tree;
410   GtkTextTag *tag;
411
412   gtk_init (&argc, &argv);
413   
414   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
415   gtk_signal_connect (GTK_OBJECT (window), "destroy",
416                       GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
417
418   hbox = gtk_hbox_new (FALSE, 0);
419   gtk_container_add (GTK_CONTAINER (window), hbox);
420
421   tree = create_tree ();
422   gtk_box_pack_start (GTK_BOX (hbox), tree, FALSE, FALSE, 0);
423
424   notebook = gtk_notebook_new ();
425   gtk_box_pack_start (GTK_BOX (hbox), notebook, TRUE, TRUE, 0);
426
427   gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
428                             create_text (&info_buffer, FALSE),
429                             gtk_label_new ("Info"));
430
431
432   gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
433                             create_text (&source_buffer, TRUE),
434                             gtk_label_new ("Source"));
435
436   tag = gtk_text_buffer_create_tag (info_buffer, "title");
437   g_object_set (G_OBJECT (tag),
438                 "font", "Sans 18",
439                 NULL);
440
441   tag = gtk_text_buffer_create_tag (info_buffer, "source");
442   g_object_set (G_OBJECT (tag),
443                 "font", "Courier 10",
444                 "pixels_above_lines", 0,
445                 "pixels_below_lines", 0,
446                 NULL);
447
448   gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
449   gtk_widget_show_all (window);
450   
451
452   load_file (testgtk_demos[0].filename);
453   
454   gtk_main ();
455
456   return 0;
457 }