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