]> Pileus Git - ~andy/gtk/blob - demos/gtk-demo/main.c
handle mnemonics in the stock item label
[~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 gboolean
303 row_activated_cb (GtkTreeView       *tree_view,
304                   GtkTreePath       *path,
305                   GtkTreeViewColumn *column,
306                   GtkTreeModel      *model)
307 {
308   GtkTreeIter iter;
309   gboolean italic;
310   GDoDemoFunc func;
311   GtkWidget *window;
312   
313   gtk_tree_model_get_iter (model, &iter, path);
314   gtk_tree_model_get (GTK_TREE_MODEL (model),
315                       &iter,
316                       FUNC_COLUMN, &func,
317                       ITALIC_COLUMN, &italic,
318                       -1);
319   gtk_tree_store_set (GTK_TREE_STORE (model),
320                       &iter,
321                       ITALIC_COLUMN, !italic,
322                       -1);
323   window = (func) ();
324   if (window != NULL)
325     {
326       CallbackData *cbdata;
327       
328       cbdata = g_new (CallbackData, 1);
329       cbdata->model = model;
330       cbdata->path = gtk_tree_path_copy (path);
331       
332       gtk_signal_connect (GTK_OBJECT (window),
333                           "destroy",
334                           window_closed_cb,
335                           cbdata);
336     }
337   else
338     {
339       gtk_tree_path_free (path);
340     }
341 }
342
343 static void
344 selection_cb (GtkTreeSelection *selection,
345               GtkTreeModel     *model)
346 {
347   GtkTreeIter iter;
348   GValue value = {0, };
349
350   if (! gtk_tree_selection_get_selected (selection, NULL, &iter))
351     return;
352
353   gtk_tree_model_get_value (model, &iter,
354                             FILENAME_COLUMN,
355                             &value);
356   load_file (g_value_get_string (&value));
357   g_value_unset (&value);
358 }
359
360 static GtkWidget *
361 create_text (GtkTextBuffer **buffer,
362              gboolean        is_source)
363 {
364   GtkWidget *scrolled_window;
365   GtkWidget *text_view;
366   PangoFontDescription *font_desc;
367
368   scrolled_window = gtk_scrolled_window_new (NULL, NULL);
369   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
370                                   GTK_POLICY_AUTOMATIC,
371                                   GTK_POLICY_AUTOMATIC);
372   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
373                                        GTK_SHADOW_IN);
374   
375   text_view = gtk_text_view_new ();
376   gtk_container_add (GTK_CONTAINER (scrolled_window), text_view);
377   
378   *buffer = gtk_text_buffer_new (NULL);
379   gtk_text_view_set_buffer (GTK_TEXT_VIEW (text_view), *buffer);
380   gtk_text_view_set_editable (GTK_TEXT_VIEW (text_view), FALSE);
381   gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (text_view), FALSE);
382
383   if (is_source)
384     {
385       font_desc = pango_font_description_from_string ("Courier 10");
386       gtk_widget_modify_font (text_view, font_desc);
387       pango_font_description_free (font_desc);
388     }
389   
390   gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view), !is_source);
391   
392   return scrolled_window;
393 }
394
395 /* Technically a list, but if we do go to 80 demos, we may want to move to a tree */
396 static GtkWidget *
397 create_tree (void)
398 {
399   GtkTreeSelection *selection;
400   GtkCellRenderer *cell;
401   GtkWidget *tree_view;
402   GtkTreeViewColumn *column;
403   GtkTreeStore *model;
404   GtkTreeIter iter;
405   gint i;
406
407   model = gtk_tree_store_new_with_types (NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN);
408   tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
409   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
410
411   gtk_tree_selection_set_mode (GTK_TREE_SELECTION (selection),
412                                GTK_TREE_SELECTION_SINGLE);
413   gtk_widget_set_usize (tree_view, 200, -1);
414
415   for (i=0; i < G_N_ELEMENTS (testgtk_demos); i++)
416     {
417       gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
418
419       gtk_tree_store_set (GTK_TREE_STORE (model),
420                           &iter,
421                           TITLE_COLUMN, testgtk_demos[i].title,
422                           FILENAME_COLUMN, testgtk_demos[i].filename,
423                           FUNC_COLUMN, testgtk_demos[i].func,
424                           ITALIC_COLUMN, FALSE,
425                           -1);
426     }
427
428   cell = gtk_cell_renderer_text_new ();
429
430   g_object_set (G_OBJECT (cell),
431                 "style", PANGO_STYLE_ITALIC,
432                 NULL);
433   
434   column = gtk_tree_view_column_new_with_attributes ("Widget (double click for demo)",
435                                                      cell,
436                                                      "text", TITLE_COLUMN,
437                                                      "style_set", ITALIC_COLUMN,
438                                                      NULL);
439   
440   gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view),
441                                GTK_TREE_VIEW_COLUMN (column));
442
443   gtk_signal_connect (GTK_OBJECT (selection), "selection_changed", selection_cb, model);
444   gtk_signal_connect (GTK_OBJECT (tree_view), "row_activated", GTK_SIGNAL_FUNC (row_activated_cb), model);
445
446   return tree_view;
447 }
448
449 int
450 main (int argc, char **argv)
451 {
452   GtkWidget *window;
453   GtkWidget *notebook;
454   GtkWidget *hbox;
455   GtkWidget *tree;
456   GtkTextTag *tag;
457
458   gtk_init (&argc, &argv);
459   
460   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
461   gtk_signal_connect (GTK_OBJECT (window), "destroy",
462                       GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
463
464   hbox = gtk_hbox_new (FALSE, 0);
465   gtk_container_add (GTK_CONTAINER (window), hbox);
466
467   tree = create_tree ();
468   gtk_box_pack_start (GTK_BOX (hbox), tree, FALSE, FALSE, 0);
469
470   notebook = gtk_notebook_new ();
471   gtk_box_pack_start (GTK_BOX (hbox), notebook, TRUE, TRUE, 0);
472
473   gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
474                             create_text (&info_buffer, FALSE),
475                             gtk_label_new_with_mnemonic ("_Info"));
476
477   gtk_notebook_append_page (GTK_NOTEBOOK (notebook),
478                             create_text (&source_buffer, TRUE),
479                             gtk_label_new_with_mnemonic ("_Source"));
480
481   tag = gtk_text_buffer_create_tag (info_buffer, "title",
482                                     "font", "Sans 18",
483                                     NULL);
484
485   tag = gtk_text_buffer_create_tag (info_buffer, "source",
486                                     "font", "Courier 10",
487                                     "pixels_above_lines", 0,
488                                     "pixels_below_lines", 0,
489                                     NULL);
490
491   gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
492   gtk_widget_show_all (window);
493   
494
495   load_file (testgtk_demos[0].filename);
496   
497   gtk_main ();
498
499   return 0;
500 }