1 | #include "config.h" |
2 | |
3 | #include <gtk/gtk.h> |
4 | |
5 | typedef GtkApplication DemoApplication; |
6 | typedef GtkApplicationClass DemoApplicationClass; |
7 | |
8 | static GType demo_application_get_type (void); |
9 | G_DEFINE_TYPE (DemoApplication, demo_application, GTK_TYPE_APPLICATION) |
10 | |
11 | typedef struct { |
12 | GtkApplicationWindow parent_instance; |
13 | |
14 | GtkWidget *message; |
15 | GtkWidget *infobar; |
16 | GtkWidget *status; |
17 | GtkWidget *; |
18 | GMenuModel *; |
19 | GtkTextBuffer *buffer; |
20 | |
21 | int width; |
22 | int height; |
23 | gboolean maximized; |
24 | gboolean fullscreen; |
25 | } DemoApplicationWindow; |
26 | typedef GtkApplicationWindowClass DemoApplicationWindowClass; |
27 | |
28 | static GType demo_application_window_get_type (void); |
29 | G_DEFINE_TYPE (DemoApplicationWindow, demo_application_window, GTK_TYPE_APPLICATION_WINDOW) |
30 | |
31 | static void create_window (GApplication *app, const char *contents); |
32 | |
33 | static void |
34 | show_action_dialog (GSimpleAction *action) |
35 | { |
36 | const char *name; |
37 | GtkWidget *dialog; |
38 | |
39 | name = g_action_get_name (G_ACTION (action)); |
40 | |
41 | dialog = gtk_message_dialog_new (NULL, |
42 | flags: GTK_DIALOG_DESTROY_WITH_PARENT, |
43 | type: GTK_MESSAGE_INFO, |
44 | buttons: GTK_BUTTONS_CLOSE, |
45 | message_format: "You activated action: \"%s\"" , |
46 | name); |
47 | |
48 | g_signal_connect (dialog, "response" , |
49 | G_CALLBACK (gtk_window_destroy), NULL); |
50 | |
51 | gtk_widget_show (widget: dialog); |
52 | } |
53 | |
54 | static void |
55 | show_action_infobar (GSimpleAction *action, |
56 | GVariant *parameter, |
57 | gpointer data) |
58 | { |
59 | DemoApplicationWindow *window = data; |
60 | char *text; |
61 | const char *name; |
62 | const char *value; |
63 | |
64 | name = g_action_get_name (G_ACTION (action)); |
65 | value = g_variant_get_string (value: parameter, NULL); |
66 | |
67 | text = g_strdup_printf (format: "You activated radio action: \"%s\".\n" |
68 | "Current value: %s" , name, value); |
69 | gtk_label_set_text (GTK_LABEL (window->message), str: text); |
70 | gtk_widget_show (widget: window->infobar); |
71 | g_free (mem: text); |
72 | } |
73 | |
74 | static void |
75 | activate_action (GSimpleAction *action, |
76 | GVariant *parameter, |
77 | gpointer user_data) |
78 | { |
79 | show_action_dialog (action); |
80 | } |
81 | |
82 | static void |
83 | activate_new (GSimpleAction *action, |
84 | GVariant *parameter, |
85 | gpointer user_data) |
86 | { |
87 | GApplication *app = user_data; |
88 | |
89 | create_window (app, NULL); |
90 | } |
91 | |
92 | static void |
93 | open_response_cb (GtkNativeDialog *dialog, |
94 | int response_id, |
95 | gpointer user_data) |
96 | { |
97 | GtkFileChooserNative *native = user_data; |
98 | GApplication *app = g_object_get_data (G_OBJECT (native), key: "app" ); |
99 | GtkWidget *message_dialog; |
100 | GFile *file; |
101 | char *contents; |
102 | GError *error = NULL; |
103 | |
104 | if (response_id == GTK_RESPONSE_ACCEPT) |
105 | { |
106 | file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (native)); |
107 | |
108 | if (g_file_load_contents (file, NULL, contents: &contents, NULL, NULL, error: &error)) |
109 | { |
110 | create_window (app, contents); |
111 | g_free (mem: contents); |
112 | } |
113 | else |
114 | { |
115 | message_dialog = gtk_message_dialog_new (NULL, |
116 | flags: GTK_DIALOG_DESTROY_WITH_PARENT, |
117 | type: GTK_MESSAGE_ERROR, |
118 | buttons: GTK_BUTTONS_CLOSE, |
119 | message_format: "Error loading file: \"%s\"" , |
120 | error->message); |
121 | g_signal_connect (message_dialog, "response" , |
122 | G_CALLBACK (gtk_window_destroy), NULL); |
123 | gtk_widget_show (widget: message_dialog); |
124 | g_error_free (error); |
125 | } |
126 | } |
127 | |
128 | gtk_native_dialog_destroy (self: GTK_NATIVE_DIALOG (ptr: native)); |
129 | g_object_unref (object: native); |
130 | } |
131 | |
132 | |
133 | static void |
134 | activate_open (GSimpleAction *action, |
135 | GVariant *parameter, |
136 | gpointer user_data) |
137 | { |
138 | GApplication *app = user_data; |
139 | GtkFileChooserNative *native; |
140 | |
141 | native = gtk_file_chooser_native_new (title: "Open File" , |
142 | NULL, |
143 | action: GTK_FILE_CHOOSER_ACTION_OPEN, |
144 | accept_label: "_Open" , |
145 | cancel_label: "_Cancel" ); |
146 | |
147 | g_object_set_data_full (G_OBJECT (native), key: "app" , g_object_ref (app), destroy: g_object_unref); |
148 | g_signal_connect (native, |
149 | "response" , |
150 | G_CALLBACK (open_response_cb), |
151 | native); |
152 | |
153 | gtk_native_dialog_show (self: GTK_NATIVE_DIALOG (ptr: native)); |
154 | } |
155 | |
156 | static void |
157 | activate_toggle (GSimpleAction *action, |
158 | GVariant *parameter, |
159 | gpointer user_data) |
160 | { |
161 | GVariant *state; |
162 | |
163 | show_action_dialog (action); |
164 | |
165 | state = g_action_get_state (G_ACTION (action)); |
166 | g_action_change_state (G_ACTION (action), value: g_variant_new_boolean (value: !g_variant_get_boolean (value: state))); |
167 | g_variant_unref (value: state); |
168 | } |
169 | |
170 | static void |
171 | activate_radio (GSimpleAction *action, |
172 | GVariant *parameter, |
173 | gpointer user_data) |
174 | { |
175 | show_action_infobar (action, parameter, data: user_data); |
176 | |
177 | g_action_change_state (G_ACTION (action), value: parameter); |
178 | } |
179 | |
180 | static void |
181 | activate_about (GSimpleAction *action, |
182 | GVariant *parameter, |
183 | gpointer user_data) |
184 | { |
185 | GtkWidget *window = user_data; |
186 | |
187 | const char *authors[] = { |
188 | "Peter Mattis" , |
189 | "Spencer Kimball" , |
190 | "Josh MacDonald" , |
191 | "and many more..." , |
192 | NULL |
193 | }; |
194 | |
195 | const char *documentors[] = { |
196 | "Owen Taylor" , |
197 | "Tony Gale" , |
198 | "Matthias Clasen <mclasen@redhat.com>" , |
199 | "and many more..." , |
200 | NULL |
201 | }; |
202 | |
203 | gtk_show_about_dialog (GTK_WINDOW (window), |
204 | first_property_name: "program-name" , "GTK Code Demos" , |
205 | "version" , g_strdup_printf (format: "%s,\nRunning against GTK %d.%d.%d" , |
206 | PACKAGE_VERSION, |
207 | gtk_get_major_version (), |
208 | gtk_get_minor_version (), |
209 | gtk_get_micro_version ()), |
210 | "copyright" , "(C) 1997-2013 The GTK Team" , |
211 | "license-type" , GTK_LICENSE_LGPL_2_1, |
212 | "website" , "http://www.gtk.org" , |
213 | "comments" , "Program to demonstrate GTK functions." , |
214 | "authors" , authors, |
215 | "documenters" , documentors, |
216 | "logo-icon-name" , "org.gtk.Demo4" , |
217 | "title" , "About GTK Code Demos" , |
218 | NULL); |
219 | } |
220 | |
221 | static void |
222 | activate_quit (GSimpleAction *action, |
223 | GVariant *parameter, |
224 | gpointer user_data) |
225 | { |
226 | GtkApplication *app = user_data; |
227 | GtkWidget *win; |
228 | GList *list, *next; |
229 | |
230 | list = gtk_application_get_windows (application: app); |
231 | while (list) |
232 | { |
233 | win = list->data; |
234 | next = list->next; |
235 | |
236 | gtk_window_destroy (GTK_WINDOW (win)); |
237 | |
238 | list = next; |
239 | } |
240 | } |
241 | |
242 | static void |
243 | update_statusbar (GtkTextBuffer *buffer, |
244 | DemoApplicationWindow *window) |
245 | { |
246 | char *msg; |
247 | int row, col; |
248 | int count; |
249 | GtkTextIter iter; |
250 | |
251 | /* clear any previous message, underflow is allowed */ |
252 | gtk_statusbar_pop (GTK_STATUSBAR (window->status), context_id: 0); |
253 | |
254 | count = gtk_text_buffer_get_char_count (buffer); |
255 | |
256 | gtk_text_buffer_get_iter_at_mark (buffer, |
257 | iter: &iter, |
258 | mark: gtk_text_buffer_get_insert (buffer)); |
259 | |
260 | row = gtk_text_iter_get_line (iter: &iter); |
261 | col = gtk_text_iter_get_line_offset (iter: &iter); |
262 | |
263 | msg = g_strdup_printf (format: "Cursor at row %d column %d - %d chars in document" , |
264 | row, col, count); |
265 | |
266 | gtk_statusbar_push (GTK_STATUSBAR (window->status), context_id: 0, text: msg); |
267 | |
268 | g_free (mem: msg); |
269 | } |
270 | |
271 | static void |
272 | mark_set_callback (GtkTextBuffer *buffer, |
273 | const GtkTextIter *new_location, |
274 | GtkTextMark *mark, |
275 | DemoApplicationWindow *window) |
276 | { |
277 | update_statusbar (buffer, window); |
278 | } |
279 | |
280 | static void |
281 | change_theme_state (GSimpleAction *action, |
282 | GVariant *state, |
283 | gpointer user_data) |
284 | { |
285 | GtkSettings *settings = gtk_settings_get_default (); |
286 | |
287 | g_object_set (G_OBJECT (settings), |
288 | first_property_name: "gtk-application-prefer-dark-theme" , |
289 | g_variant_get_boolean (value: state), |
290 | NULL); |
291 | |
292 | g_simple_action_set_state (simple: action, value: state); |
293 | } |
294 | |
295 | static void |
296 | change_radio_state (GSimpleAction *action, |
297 | GVariant *state, |
298 | gpointer user_data) |
299 | { |
300 | g_simple_action_set_state (simple: action, value: state); |
301 | } |
302 | |
303 | static GActionEntry app_entries[] = { |
304 | { "new" , activate_new, NULL, NULL, NULL }, |
305 | { "open" , activate_open, NULL, NULL, NULL }, |
306 | { "save" , activate_action, NULL, NULL, NULL }, |
307 | { "save-as" , activate_action, NULL, NULL, NULL }, |
308 | { "quit" , activate_quit, NULL, NULL, NULL }, |
309 | { "dark" , activate_toggle, NULL, "false" , change_theme_state } |
310 | }; |
311 | |
312 | static GActionEntry win_entries[] = { |
313 | { "shape" , activate_radio, "s" , "'oval'" , change_radio_state }, |
314 | { "bold" , activate_toggle, NULL, "false" , NULL }, |
315 | { "about" , activate_about, NULL, NULL, NULL }, |
316 | { "file1" , activate_action, NULL, NULL, NULL }, |
317 | { "logo" , activate_action, NULL, NULL, NULL } |
318 | }; |
319 | |
320 | static void |
321 | clicked_cb (GtkWidget *widget, DemoApplicationWindow *window) |
322 | { |
323 | gtk_widget_hide (widget: window->infobar); |
324 | } |
325 | |
326 | static void |
327 | startup (GApplication *app) |
328 | { |
329 | GtkBuilder *builder; |
330 | |
331 | G_APPLICATION_CLASS (demo_application_parent_class)->startup (app); |
332 | |
333 | builder = gtk_builder_new (); |
334 | gtk_builder_add_from_resource (builder, resource_path: "/application_demo/menus.ui" , NULL); |
335 | |
336 | gtk_application_set_menubar (GTK_APPLICATION (app), |
337 | G_MENU_MODEL (gtk_builder_get_object (builder, "menubar" ))); |
338 | |
339 | g_object_unref (object: builder); |
340 | } |
341 | |
342 | static void |
343 | create_window (GApplication *app, |
344 | const char *content) |
345 | { |
346 | DemoApplicationWindow *window; |
347 | |
348 | window = (DemoApplicationWindow *)g_object_new (object_type: demo_application_window_get_type (), |
349 | first_property_name: "application" , app, |
350 | "show-menubar" , TRUE, |
351 | NULL); |
352 | if (content) |
353 | gtk_text_buffer_set_text (buffer: window->buffer, text: content, len: -1); |
354 | |
355 | gtk_window_present (GTK_WINDOW (window)); |
356 | } |
357 | |
358 | static void |
359 | activate (GApplication *app) |
360 | { |
361 | create_window (app, NULL); |
362 | } |
363 | |
364 | static void |
365 | demo_application_init (DemoApplication *app) |
366 | { |
367 | GSettings *settings; |
368 | GAction *action; |
369 | |
370 | settings = g_settings_new (schema_id: "org.gtk.Demo4" ); |
371 | |
372 | g_action_map_add_action_entries (G_ACTION_MAP (app), |
373 | entries: app_entries, G_N_ELEMENTS (app_entries), |
374 | user_data: app); |
375 | |
376 | action = g_settings_create_action (settings, key: "color" ); |
377 | |
378 | g_action_map_add_action (G_ACTION_MAP (app), action); |
379 | |
380 | g_object_unref (object: settings); |
381 | } |
382 | |
383 | static void |
384 | demo_application_class_init (DemoApplicationClass *class) |
385 | { |
386 | GApplicationClass *app_class = G_APPLICATION_CLASS (class); |
387 | |
388 | app_class->startup = startup; |
389 | app_class->activate = activate; |
390 | } |
391 | |
392 | static void |
393 | demo_application_window_store_state (DemoApplicationWindow *win) |
394 | { |
395 | GSettings *settings; |
396 | |
397 | settings = g_settings_new (schema_id: "org.gtk.Demo4" ); |
398 | g_settings_set (settings, key: "window-size" , format: "(ii)" , win->width, win->height); |
399 | g_settings_set_boolean (settings, key: "maximized" , value: win->maximized); |
400 | g_settings_set_boolean (settings, key: "fullscreen" , value: win->fullscreen); |
401 | g_object_unref (object: settings); |
402 | } |
403 | |
404 | static void |
405 | demo_application_window_load_state (DemoApplicationWindow *win) |
406 | { |
407 | GSettings *settings; |
408 | |
409 | settings = g_settings_new (schema_id: "org.gtk.Demo4" ); |
410 | g_settings_get (settings, key: "window-size" , format: "(ii)" , &win->width, &win->height); |
411 | win->maximized = g_settings_get_boolean (settings, key: "maximized" ); |
412 | win->fullscreen = g_settings_get_boolean (settings, key: "fullscreen" ); |
413 | g_object_unref (object: settings); |
414 | } |
415 | |
416 | static void |
417 | demo_application_window_init (DemoApplicationWindow *window) |
418 | { |
419 | GtkWidget *popover; |
420 | |
421 | window->width = -1; |
422 | window->height = -1; |
423 | window->maximized = FALSE; |
424 | window->fullscreen = FALSE; |
425 | |
426 | gtk_widget_init_template (GTK_WIDGET (window)); |
427 | |
428 | popover = gtk_popover_menu_new_from_model (model: window->toolmenu); |
429 | gtk_menu_button_set_popover (GTK_MENU_BUTTON (window->menubutton), popover); |
430 | |
431 | g_action_map_add_action_entries (G_ACTION_MAP (window), |
432 | entries: win_entries, G_N_ELEMENTS (win_entries), |
433 | user_data: window); |
434 | } |
435 | |
436 | static void |
437 | demo_application_window_constructed (GObject *object) |
438 | { |
439 | DemoApplicationWindow *window = (DemoApplicationWindow *)object; |
440 | |
441 | demo_application_window_load_state (win: window); |
442 | |
443 | gtk_window_set_default_size (GTK_WINDOW (window), width: window->width, height: window->height); |
444 | |
445 | if (window->maximized) |
446 | gtk_window_maximize (GTK_WINDOW (window)); |
447 | |
448 | if (window->fullscreen) |
449 | gtk_window_fullscreen (GTK_WINDOW (window)); |
450 | |
451 | G_OBJECT_CLASS (demo_application_window_parent_class)->constructed (object); |
452 | } |
453 | |
454 | static void |
455 | demo_application_window_size_allocate (GtkWidget *widget, |
456 | int width, |
457 | int height, |
458 | int baseline) |
459 | { |
460 | DemoApplicationWindow *window = (DemoApplicationWindow *)widget; |
461 | |
462 | GTK_WIDGET_CLASS (demo_application_window_parent_class)->size_allocate (widget, |
463 | width, |
464 | height, |
465 | baseline); |
466 | |
467 | if (!window->maximized && !window->fullscreen) |
468 | gtk_window_get_default_size (GTK_WINDOW (window), width: &window->width, height: &window->height); |
469 | } |
470 | |
471 | static void |
472 | surface_state_changed (GtkWidget *widget) |
473 | { |
474 | DemoApplicationWindow *window = (DemoApplicationWindow *)widget; |
475 | GdkToplevelState new_state; |
476 | |
477 | new_state = gdk_toplevel_get_state (toplevel: GDK_TOPLEVEL (ptr: gtk_native_get_surface (self: GTK_NATIVE (ptr: widget)))); |
478 | window->maximized = (new_state & GDK_TOPLEVEL_STATE_MAXIMIZED) != 0; |
479 | window->fullscreen = (new_state & GDK_TOPLEVEL_STATE_FULLSCREEN) != 0; |
480 | } |
481 | |
482 | static void |
483 | demo_application_window_realize (GtkWidget *widget) |
484 | { |
485 | GTK_WIDGET_CLASS (demo_application_window_parent_class)->realize (widget); |
486 | |
487 | g_signal_connect_swapped (gtk_native_get_surface (GTK_NATIVE (widget)), "notify::state" , |
488 | G_CALLBACK (surface_state_changed), widget); |
489 | } |
490 | |
491 | static void |
492 | demo_application_window_unrealize (GtkWidget *widget) |
493 | { |
494 | g_signal_handlers_disconnect_by_func (gtk_native_get_surface (GTK_NATIVE (widget)), |
495 | surface_state_changed, widget); |
496 | |
497 | GTK_WIDGET_CLASS (demo_application_window_parent_class)->unrealize (widget); |
498 | } |
499 | |
500 | static void |
501 | demo_application_window_dispose (GObject *object) |
502 | { |
503 | DemoApplicationWindow *window = (DemoApplicationWindow *)object; |
504 | |
505 | demo_application_window_store_state (win: window); |
506 | |
507 | G_OBJECT_CLASS (demo_application_window_parent_class)->dispose (object); |
508 | } |
509 | |
510 | static void |
511 | demo_application_window_class_init (DemoApplicationWindowClass *class) |
512 | { |
513 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
514 | GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); |
515 | |
516 | object_class->constructed = demo_application_window_constructed; |
517 | object_class->dispose = demo_application_window_dispose; |
518 | |
519 | widget_class->size_allocate = demo_application_window_size_allocate; |
520 | widget_class->realize = demo_application_window_realize; |
521 | widget_class->unrealize = demo_application_window_unrealize; |
522 | |
523 | gtk_widget_class_set_template_from_resource (widget_class, resource_name: "/application_demo/application.ui" ); |
524 | gtk_widget_class_bind_template_child (widget_class, DemoApplicationWindow, message); |
525 | gtk_widget_class_bind_template_child (widget_class, DemoApplicationWindow, infobar); |
526 | gtk_widget_class_bind_template_child (widget_class, DemoApplicationWindow, status); |
527 | gtk_widget_class_bind_template_child (widget_class, DemoApplicationWindow, buffer); |
528 | gtk_widget_class_bind_template_child (widget_class, DemoApplicationWindow, menubutton); |
529 | gtk_widget_class_bind_template_child (widget_class, DemoApplicationWindow, toolmenu); |
530 | gtk_widget_class_bind_template_callback (widget_class, clicked_cb); |
531 | gtk_widget_class_bind_template_callback (widget_class, update_statusbar); |
532 | gtk_widget_class_bind_template_callback (widget_class, mark_set_callback); |
533 | } |
534 | |
535 | int |
536 | main (int argc, char *argv[]) |
537 | { |
538 | GtkApplication *app; |
539 | |
540 | app = GTK_APPLICATION (g_object_new (demo_application_get_type (), |
541 | "application-id" , "org.gtk.Demo4.App" , |
542 | "flags" , G_APPLICATION_HANDLES_OPEN, |
543 | NULL)); |
544 | |
545 | return g_application_run (G_APPLICATION (app), argc: 0, NULL); |
546 | } |
547 | |