1 | #include <stdlib.h> |
2 | #include <gtk/gtk.h> |
3 | #include <string.h> |
4 | |
5 | |
6 | static const char *red_css = |
7 | "textview {" |
8 | " color: red;" |
9 | "}" ; |
10 | |
11 | static const char *black_css = |
12 | "textview {" |
13 | " color: black;" |
14 | "}" ; |
15 | |
16 | static void |
17 | activate_toggle (GSimpleAction *action, |
18 | GVariant *parameter, |
19 | gpointer user_data) |
20 | { |
21 | GVariant *state; |
22 | |
23 | state = g_action_get_state (G_ACTION (action)); |
24 | g_action_change_state (G_ACTION (action), value: g_variant_new_boolean (value: !g_variant_get_boolean (value: state))); |
25 | g_variant_unref (value: state); |
26 | } |
27 | |
28 | static void |
29 | change_fullscreen_state (GSimpleAction *action, |
30 | GVariant *state, |
31 | gpointer user_data) |
32 | { |
33 | if (g_variant_get_boolean (value: state)) |
34 | gtk_window_fullscreen (window: user_data); |
35 | else |
36 | gtk_window_unfullscreen (window: user_data); |
37 | |
38 | g_simple_action_set_state (simple: action, value: state); |
39 | } |
40 | |
41 | static void |
42 | window_copy (GSimpleAction *action, |
43 | GVariant *parameter, |
44 | gpointer user_data) |
45 | { |
46 | GtkWindow *window = GTK_WINDOW (user_data); |
47 | GtkTextView *text = g_object_get_data (object: (GObject*)window, key: "plugman-text" ); |
48 | |
49 | gtk_text_buffer_copy_clipboard (buffer: gtk_text_view_get_buffer (text_view: text), |
50 | clipboard: gtk_widget_get_clipboard (GTK_WIDGET (text))); |
51 | } |
52 | |
53 | static void |
54 | window_paste (GSimpleAction *action, |
55 | GVariant *parameter, |
56 | gpointer user_data) |
57 | { |
58 | GtkWindow *window = GTK_WINDOW (user_data); |
59 | GtkTextView *text = g_object_get_data (object: (GObject*)window, key: "plugman-text" ); |
60 | |
61 | gtk_text_buffer_paste_clipboard (buffer: gtk_text_view_get_buffer (text_view: text), |
62 | clipboard: gtk_widget_get_clipboard (GTK_WIDGET (text)), |
63 | NULL, |
64 | TRUE); |
65 | |
66 | } |
67 | |
68 | static GActionEntry win_entries[] = { |
69 | { "copy" , window_copy, NULL, NULL, NULL }, |
70 | { "paste" , window_paste, NULL, NULL, NULL }, |
71 | { "fullscreen" , activate_toggle, NULL, "false" , change_fullscreen_state } |
72 | }; |
73 | |
74 | static void |
75 | new_window (GApplication *app, |
76 | GFile *file) |
77 | { |
78 | GtkWidget *window, *grid, *scrolled, *view; |
79 | |
80 | window = gtk_application_window_new (GTK_APPLICATION (app)); |
81 | gtk_window_set_default_size (window: (GtkWindow*)window, width: 640, height: 480); |
82 | g_action_map_add_action_entries (G_ACTION_MAP (window), entries: win_entries, G_N_ELEMENTS (win_entries), user_data: window); |
83 | gtk_window_set_title (GTK_WINDOW (window), title: "Plugman" ); |
84 | gtk_application_window_set_show_menubar (GTK_APPLICATION_WINDOW (window), TRUE); |
85 | |
86 | grid = gtk_grid_new (); |
87 | gtk_window_set_child (GTK_WINDOW (window), child: grid); |
88 | |
89 | scrolled = gtk_scrolled_window_new (); |
90 | gtk_widget_set_hexpand (widget: scrolled, TRUE); |
91 | gtk_widget_set_vexpand (widget: scrolled, TRUE); |
92 | view = gtk_text_view_new (); |
93 | |
94 | g_object_set_data (object: (GObject*)window, key: "plugman-text" , data: view); |
95 | |
96 | gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolled), child: view); |
97 | |
98 | gtk_grid_attach (GTK_GRID (grid), child: scrolled, column: 0, row: 0, width: 1, height: 1); |
99 | |
100 | if (file != NULL) |
101 | { |
102 | char *contents; |
103 | gsize length; |
104 | |
105 | if (g_file_load_contents (file, NULL, contents: &contents, length: &length, NULL, NULL)) |
106 | { |
107 | GtkTextBuffer *buffer; |
108 | |
109 | buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); |
110 | gtk_text_buffer_set_text (buffer, text: contents, len: length); |
111 | g_free (mem: contents); |
112 | } |
113 | } |
114 | |
115 | gtk_widget_show (GTK_WIDGET (window)); |
116 | } |
117 | |
118 | static void |
119 | plug_man_activate (GApplication *application) |
120 | { |
121 | new_window (app: application, NULL); |
122 | } |
123 | |
124 | static void |
125 | plug_man_open (GApplication *application, |
126 | GFile **files, |
127 | int n_files, |
128 | const char *hint) |
129 | { |
130 | int i; |
131 | |
132 | for (i = 0; i < n_files; i++) |
133 | new_window (app: application, file: files[i]); |
134 | } |
135 | |
136 | typedef GtkApplication PlugMan; |
137 | typedef GtkApplicationClass PlugManClass; |
138 | |
139 | static GType plug_man_get_type (void); |
140 | G_DEFINE_TYPE (PlugMan, plug_man, GTK_TYPE_APPLICATION) |
141 | |
142 | static void |
143 | plug_man_finalize (GObject *object) |
144 | { |
145 | G_OBJECT_CLASS (plug_man_parent_class)->finalize (object); |
146 | } |
147 | |
148 | static void |
149 | show_about (GSimpleAction *action, |
150 | GVariant *parameter, |
151 | gpointer user_data) |
152 | { |
153 | gtk_show_about_dialog (NULL, |
154 | first_property_name: "program-name" , "Plugman" , |
155 | "title" , "About Plugman" , |
156 | "comments" , "A cheap Bloatpad clone." , |
157 | NULL); |
158 | } |
159 | |
160 | |
161 | static void |
162 | quit_app (GSimpleAction *action, |
163 | GVariant *parameter, |
164 | gpointer user_data) |
165 | { |
166 | GList *list, *next; |
167 | GtkWindow *win; |
168 | |
169 | g_print (format: "Going down...\n" ); |
170 | |
171 | list = gtk_application_get_windows (GTK_APPLICATION (g_application_get_default ())); |
172 | while (list) |
173 | { |
174 | win = list->data; |
175 | next = list->next; |
176 | |
177 | gtk_window_destroy (GTK_WINDOW (win)); |
178 | |
179 | list = next; |
180 | } |
181 | } |
182 | |
183 | static gboolean is_red_plugin_enabled; |
184 | static gboolean is_black_plugin_enabled; |
185 | |
186 | static gboolean |
187 | plugin_enabled (const char *name) |
188 | { |
189 | if (g_strcmp0 (str1: name, str2: "red" ) == 0) |
190 | return is_red_plugin_enabled; |
191 | else |
192 | return is_black_plugin_enabled; |
193 | } |
194 | |
195 | static GMenuModel * |
196 | (void) |
197 | { |
198 | return (GMenuModel*) g_object_get_data (G_OBJECT (g_application_get_default ()), key: "plugin-menu" ); |
199 | } |
200 | |
201 | static void |
202 | plugin_action (GAction *action, |
203 | GVariant *parameter, |
204 | gpointer data) |
205 | { |
206 | const char *action_name; |
207 | const char *css_to_load; |
208 | GtkCssProvider *css_provider; |
209 | |
210 | action_name = g_action_get_name (action); |
211 | if (strcmp (s1: action_name, s2: "red" ) == 0) |
212 | css_to_load = red_css; |
213 | else if (strcmp (s1: action_name, s2: "black" ) == 0) |
214 | css_to_load = black_css; |
215 | else |
216 | { |
217 | g_critical ("Unknown action name: %s" , action_name); |
218 | return; |
219 | } |
220 | |
221 | g_message ("Color: %s" , g_action_get_name (action)); |
222 | |
223 | css_provider = gtk_css_provider_new (); |
224 | gtk_css_provider_load_from_data (css_provider, data: css_to_load, length: -1); |
225 | gtk_style_context_add_provider_for_display (display: gdk_display_get_default (), |
226 | GTK_STYLE_PROVIDER (css_provider), |
227 | GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); |
228 | } |
229 | |
230 | static void |
231 | enable_plugin (const char *name) |
232 | { |
233 | GMenuModel *; |
234 | GAction *action; |
235 | |
236 | g_print (format: "Enabling '%s' plugin\n" , name); |
237 | |
238 | action = (GAction *)g_simple_action_new (name, NULL); |
239 | g_signal_connect (action, "activate" , G_CALLBACK (plugin_action), (gpointer)name); |
240 | g_action_map_add_action (G_ACTION_MAP (g_application_get_default ()), action); |
241 | g_print (format: "Actions of '%s' plugin added\n" , name); |
242 | g_object_unref (object: action); |
243 | |
244 | plugin_menu = find_plugin_menu (); |
245 | if (plugin_menu) |
246 | { |
247 | GMenu *section; |
248 | GMenuItem *item; |
249 | char *label; |
250 | char *action_name; |
251 | |
252 | section = g_menu_new (); |
253 | label = g_strdup_printf (format: "Turn text %s" , name); |
254 | action_name = g_strconcat (string1: "app." , name, NULL); |
255 | g_menu_insert (menu: section, position: 0, label, detailed_action: action_name); |
256 | g_free (mem: label); |
257 | g_free (mem: action_name); |
258 | item = g_menu_item_new_section (NULL, section: (GMenuModel*)section); |
259 | g_menu_item_set_attribute (menu_item: item, attribute: "id" , format_string: "s" , name); |
260 | g_menu_append_item (G_MENU (plugin_menu), item); |
261 | g_object_unref (object: item); |
262 | g_object_unref (object: section); |
263 | g_print (format: "Menus of '%s' plugin added\n" , name); |
264 | } |
265 | else |
266 | g_warning ("Plugin menu not found" ); |
267 | |
268 | if (g_strcmp0 (str1: name, str2: "red" ) == 0) |
269 | is_red_plugin_enabled = TRUE; |
270 | else |
271 | is_black_plugin_enabled = TRUE; |
272 | } |
273 | |
274 | static void |
275 | disable_plugin (const char *name) |
276 | { |
277 | GMenuModel *; |
278 | |
279 | g_print (format: "Disabling '%s' plugin\n" , name); |
280 | |
281 | plugin_menu = find_plugin_menu (); |
282 | if (plugin_menu) |
283 | { |
284 | int i; |
285 | |
286 | for (i = 0; i < g_menu_model_get_n_items (model: plugin_menu); i++) |
287 | { |
288 | char *id; |
289 | if (g_menu_model_get_item_attribute (model: plugin_menu, item_index: i, attribute: "id" , format_string: "s" , &id)) |
290 | { |
291 | if (g_strcmp0 (str1: id, str2: name) == 0) |
292 | { |
293 | g_menu_remove (G_MENU (plugin_menu), position: i); |
294 | g_print (format: "Menus of '%s' plugin removed\n" , name); |
295 | } |
296 | g_free (mem: id); |
297 | } |
298 | } |
299 | } |
300 | else |
301 | g_warning ("Plugin menu not found" ); |
302 | |
303 | g_action_map_remove_action (G_ACTION_MAP (g_application_get_default ()), action_name: name); |
304 | g_print (format: "Actions of '%s' plugin removed\n" , name); |
305 | |
306 | if (g_strcmp0 (str1: name, str2: "red" ) == 0) |
307 | is_red_plugin_enabled = FALSE; |
308 | else |
309 | is_black_plugin_enabled = FALSE; |
310 | } |
311 | |
312 | static void |
313 | enable_or_disable_plugin (GtkToggleButton *button, |
314 | const char *name) |
315 | { |
316 | if (plugin_enabled (name)) |
317 | disable_plugin (name); |
318 | else |
319 | enable_plugin (name); |
320 | } |
321 | |
322 | |
323 | static void |
324 | configure_plugins (GSimpleAction *action, |
325 | GVariant *parameter, |
326 | gpointer user_data) |
327 | { |
328 | GtkBuilder *builder; |
329 | GtkWidget *dialog; |
330 | GtkWidget *check; |
331 | GError *error = NULL; |
332 | |
333 | builder = gtk_builder_new (); |
334 | gtk_builder_add_from_string (builder, |
335 | buffer: "<interface>" |
336 | " <object class='GtkDialog' id='plugin-dialog'>" |
337 | " <property name='title'>Plugins</property>" |
338 | " <child internal-child='content_area'>" |
339 | " <object class='GtkBox' id='content-area'>" |
340 | " <property name='visible'>True</property>" |
341 | " <property name='orientation'>vertical</property>" |
342 | " <child>" |
343 | " <object class='GtkCheckButton' id='red-plugin'>" |
344 | " <property name='label' translatable='yes'>Red Plugin - turn your text red</property>" |
345 | " <property name='visible'>True</property>" |
346 | " </object>" |
347 | " </child>" |
348 | " <child>" |
349 | " <object class='GtkCheckButton' id='black-plugin'>" |
350 | " <property name='label' translatable='yes'>Black Plugin - turn your text black</property>" |
351 | " <property name='visible'>True</property>" |
352 | " </object>" |
353 | " </child>" |
354 | " </object>" |
355 | " </child>" |
356 | " <child internal-child='action_area'>" |
357 | " <object class='GtkBox' id='action-area'>" |
358 | " <property name='visible'>True</property>" |
359 | " <child>" |
360 | " <object class='GtkButton' id='close-button'>" |
361 | " <property name='label' translatable='yes'>Close</property>" |
362 | " <property name='visible'>True</property>" |
363 | " </object>" |
364 | " </child>" |
365 | " </object>" |
366 | " </child>" |
367 | " <action-widgets>" |
368 | " <action-widget response='-5'>close-button</action-widget>" |
369 | " </action-widgets>" |
370 | " </object>" |
371 | "</interface>" , length: -1, error: &error); |
372 | if (error) |
373 | { |
374 | g_warning ("%s" , error->message); |
375 | g_error_free (error); |
376 | goto out; |
377 | } |
378 | |
379 | dialog = (GtkWidget *)gtk_builder_get_object (builder, name: "plugin-dialog" ); |
380 | check = (GtkWidget *)gtk_builder_get_object (builder, name: "red-plugin" ); |
381 | gtk_check_button_set_active (GTK_CHECK_BUTTON (check), setting: plugin_enabled (name: "red" )); |
382 | g_signal_connect (check, "toggled" , G_CALLBACK (enable_or_disable_plugin), (char *) "red" ); |
383 | check = (GtkWidget *)gtk_builder_get_object (builder, name: "black-plugin" ); |
384 | gtk_check_button_set_active (GTK_CHECK_BUTTON (check), setting: plugin_enabled (name: "black" )); |
385 | g_signal_connect (check, "toggled" , G_CALLBACK (enable_or_disable_plugin), (char *) "black" ); |
386 | |
387 | g_signal_connect (dialog, "response" , G_CALLBACK (gtk_window_destroy), NULL); |
388 | |
389 | gtk_window_present (GTK_WINDOW (dialog)); |
390 | |
391 | out: |
392 | g_object_unref (object: builder); |
393 | } |
394 | |
395 | static GActionEntry app_entries[] = { |
396 | { "about" , show_about, NULL, NULL, NULL }, |
397 | { "quit" , quit_app, NULL, NULL, NULL }, |
398 | { "plugins" , configure_plugins, NULL, NULL, NULL }, |
399 | }; |
400 | |
401 | static void |
402 | plug_man_startup (GApplication *application) |
403 | { |
404 | GtkBuilder *builder; |
405 | |
406 | G_APPLICATION_CLASS (plug_man_parent_class) |
407 | ->startup (application); |
408 | |
409 | g_action_map_add_action_entries (G_ACTION_MAP (application), entries: app_entries, G_N_ELEMENTS (app_entries), user_data: application); |
410 | |
411 | builder = gtk_builder_new (); |
412 | gtk_builder_add_from_string (builder, |
413 | buffer: "<interface>" |
414 | " <menu id='menubar'>" |
415 | " <submenu>" |
416 | " <attribute name='label' translatable='yes'>_Plugman</attribute>" |
417 | " <section>" |
418 | " <item>" |
419 | " <attribute name='label' translatable='yes'>_About Plugman</attribute>" |
420 | " <attribute name='action'>app.about</attribute>" |
421 | " </item>" |
422 | " </section>" |
423 | " <section>" |
424 | " <item>" |
425 | " <attribute name='label' translatable='yes'>_Quit</attribute>" |
426 | " <attribute name='action'>app.quit</attribute>" |
427 | " <attribute name='accel'><Primary>q</attribute>" |
428 | " </item>" |
429 | " </section>" |
430 | " </submenu>" |
431 | " <submenu>" |
432 | " <attribute name='label' translatable='yes'>_Edit</attribute>" |
433 | " <section>" |
434 | " <item>" |
435 | " <attribute name='label' translatable='yes'>_Copy</attribute>" |
436 | " <attribute name='action'>win.copy</attribute>" |
437 | " </item>" |
438 | " <item>" |
439 | " <attribute name='label' translatable='yes'>_Paste</attribute>" |
440 | " <attribute name='action'>win.paste</attribute>" |
441 | " </item>" |
442 | " </section>" |
443 | " <item><link name='section' id='plugins'>" |
444 | " </link></item>" |
445 | " <section>" |
446 | " <item>" |
447 | " <attribute name='label' translatable='yes'>Plugins</attribute>" |
448 | " <attribute name='action'>app.plugins</attribute>" |
449 | " </item>" |
450 | " </section>" |
451 | " </submenu>" |
452 | " <submenu>" |
453 | " <attribute name='label' translatable='yes'>_View</attribute>" |
454 | " <section>" |
455 | " <item>" |
456 | " <attribute name='label' translatable='yes'>_Fullscreen</attribute>" |
457 | " <attribute name='action'>win.fullscreen</attribute>" |
458 | " </item>" |
459 | " </section>" |
460 | " </submenu>" |
461 | " </menu>" |
462 | "</interface>" , length: -1, NULL); |
463 | gtk_application_set_menubar (GTK_APPLICATION (application), G_MENU_MODEL (gtk_builder_get_object (builder, "menubar" ))); |
464 | g_object_set_data_full (G_OBJECT (application), key: "plugin-menu" , data: gtk_builder_get_object (builder, name: "plugins" ), destroy: g_object_unref); |
465 | g_object_unref (object: builder); |
466 | } |
467 | |
468 | static void |
469 | plug_man_init (PlugMan *app) |
470 | { |
471 | } |
472 | |
473 | static void |
474 | plug_man_class_init (PlugManClass *class) |
475 | { |
476 | GApplicationClass *application_class = G_APPLICATION_CLASS (class); |
477 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
478 | |
479 | application_class->startup = plug_man_startup; |
480 | application_class->activate = plug_man_activate; |
481 | application_class->open = plug_man_open; |
482 | |
483 | object_class->finalize = plug_man_finalize; |
484 | |
485 | } |
486 | |
487 | static PlugMan * |
488 | plug_man_new (void) |
489 | { |
490 | return g_object_new (object_type: plug_man_get_type (), |
491 | first_property_name: "application-id" , "org.gtk.Test.plugman" , |
492 | "flags" , G_APPLICATION_HANDLES_OPEN, |
493 | NULL); |
494 | } |
495 | |
496 | int |
497 | main (int argc, char **argv) |
498 | { |
499 | PlugMan *plug_man; |
500 | int status; |
501 | const char *accels[] = { "F11" , NULL }; |
502 | |
503 | plug_man = plug_man_new (); |
504 | gtk_application_set_accels_for_action (GTK_APPLICATION (plug_man), |
505 | detailed_action_name: "win.fullscreen" , accels); |
506 | status = g_application_run (G_APPLICATION (plug_man), argc, argv); |
507 | g_object_unref (object: plug_man); |
508 | |
509 | return status; |
510 | } |
511 | |