GTK+ plugin: implementation of edit window. No save available yet, just look.
[lxde/lxhotkey.git] / plugins / gtk / gtk.c
CommitLineData
0cdc072f
AG
1/*
2 * Copyright (C) 2016 Andriy Grytsenko <andrej@rep.kiev.ua>
3 *
4 * This file is a part of LXHotkey project.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21#ifdef HAVE_CONFIG_H
22# include "config.h"
23#endif
24
25#include "lxhotkey.h"
7e60a2a7 26#include "edit.h"
0cdc072f
AG
27
28#include <glib/gi18n-lib.h>
29#include <gtk/gtk.h>
30
1b4c5f1a
AG
31static int inited = 0;
32
0cdc072f
AG
33static const char menu_xml[] =
34"<menubar>"
35 "<menu action='FileMenu'>"
36 "<menuitem action='Save'/>"
1b4c5f1a 37 "<menuitem action='Reload'/>"
87d89031 38 "<separator/>"
0cdc072f
AG
39 "<menuitem action='Quit'/>"
40 "</menu>"
41 "<menu action='EditMenu'>"
42 "<menuitem action='New'/>"
43 "<menuitem action='Del'/>"
44 "<menuitem action='Edit'/>"
45 "</menu>"
46 "<menu action='HelpMenu'>"
47 "<menuitem action='About'/>"
48 "</menu>"
49"</menubar>"
50"<toolbar>"
1b4c5f1a 51 "<toolitem action='Reload'/>"
0cdc072f
AG
52 "<toolitem action='Save'/>"
53 "<separator/>"
54 "<toolitem action='New'/>"
55 "<toolitem action='Del'/>"
56 "<toolitem action='Edit'/>"
57"</toolbar>";
58
60f97d42
AG
59static void set_actions_list(PluginData *data);
60static void set_apps_list(PluginData *data);
61
7e60a2a7
AG
62void _main_refresh(PluginData *data)
63{
64 set_actions_list(data);
65 set_apps_list(data);
66}
67
1b4c5f1a
AG
68static void on_reload(GtkAction *act, PluginData *data)
69{
60f97d42
AG
70 GError *error = NULL;
71
72 *data->config = data->cb->load(*data->config, &error);
73 if (error)
74 {
75 g_warning("error loading config: %s",error->message);
87d89031 76 //FIXME: show errors instead
60f97d42
AG
77 g_error_free(error);
78 }
7e60a2a7 79 _main_refresh(data);
87d89031 80 gtk_action_set_sensitive(data->save_action, FALSE);
1b4c5f1a
AG
81}
82
0cdc072f
AG
83static void on_save(GtkAction *act, PluginData *data)
84{
87d89031
AG
85 if (data->cb->save(*data->config, NULL))
86 gtk_action_set_sensitive(data->save_action, FALSE);
87 //FIXME: else show errors
0cdc072f
AG
88}
89
90static void on_quit(GtkAction *act, PluginData *data)
91{
92 gtk_main_quit();
93}
94
95static void on_new(GtkAction *act, PluginData *data)
96{
7e60a2a7
AG
97 gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(data->current_page));
98 _edit_action(data, NULL);
0cdc072f
AG
99}
100
101static void on_del(GtkAction *act, PluginData *data)
102{
103}
104
105static void on_edit(GtkAction *act, PluginData *data)
106{
7e60a2a7 107 _edit_action(data, NULL);
0cdc072f
AG
108}
109
110static void on_about(GtkAction *act, PluginData *data)
111{
87d89031
AG
112 GtkAboutDialog *about;
113 const gchar *authors[] = {
114 "Andriy Grytsenko <andrej@rep.kiev.ua>",
115 NULL
116 };
117 /* TRANSLATORS: Replace this string with your names, one name per line. */
118 gchar *translators = _("translator-credits");
119
120 about = GTK_ABOUT_DIALOG(gtk_about_dialog_new());
121 gtk_window_set_icon_name(GTK_WINDOW(about), LXHOTKEY_ICON);
122 gtk_about_dialog_set_version(about, VERSION);
123 gtk_about_dialog_set_program_name(about, "LXHotkey"); //FIXME: translated?
124 gtk_about_dialog_set_logo_icon_name(about, LXHOTKEY_ICON);
125
126 gtk_about_dialog_set_copyright(about, _("Copyright (C) 2016"));
127 gtk_about_dialog_set_comments(about, _( "Keyboard shortcuts configurator"));
128 gtk_about_dialog_set_license(about, "This program is free software; you can redistribute it and/or\n"
129 "modify it under the terms of the GNU General Public License\n"
130 "as published by the Free Software Foundation; either version 2\n"
131 "of the License, or (at your option) any later version.\n"
132 "\n"
133 "This program is distributed in the hope that it will be useful,\n"
134 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
135 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
136 "GNU General Public License for more details.\n"
137 "\n"
138 "You should have received a copy of the GNU General Public License\n"
139 "along with this program; if not, write to the Free Software Foundation,\n"
140 "Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.");
141 gtk_about_dialog_set_website(about, "http://lxde.org/");
142 gtk_about_dialog_set_authors(about, authors);
143 gtk_about_dialog_set_translator_credits(about, translators);
144 gtk_dialog_run(GTK_DIALOG(about));
145 gtk_widget_destroy(GTK_WIDGET(about));
0cdc072f
AG
146}
147
148static GtkActionEntry actions[] =
149{
150 { "FileMenu", NULL, N_("_File"), NULL, NULL, NULL },
1b4c5f1a
AG
151 { "Reload", GTK_STOCK_UNDO, N_("_Reload"), NULL,
152 N_("Drop all unsaved changes and reload all keys from Window Manager configuration"),
153 G_CALLBACK(on_reload) },
154 { "Save", GTK_STOCK_SAVE, NULL, NULL,
155 N_("Save all changes and apply them to Window Manager configuration"),
156 G_CALLBACK(on_save) },
157 { "Quit", GTK_STOCK_QUIT, NULL, NULL,
158 N_("Exit the application without saving"), G_CALLBACK(on_quit) },
0cdc072f 159 { "EditMenu", NULL, N_("_Edit"), NULL, NULL, NULL },
1b4c5f1a
AG
160 { "New", GTK_STOCK_NEW, NULL, NULL, N_("Create new action"),
161 G_CALLBACK(on_new) },
162 { "Del", GTK_STOCK_DELETE, NULL, "", N_("Remove selected action"),
163 G_CALLBACK(on_del) },
164 { "Edit", GTK_STOCK_EDIT, NULL, NULL, N_("Change selected action"),
165 G_CALLBACK(on_edit) },
0cdc072f
AG
166 { "HelpMenu", NULL, N_("_Help"), NULL, NULL, NULL },
167 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(on_about) }
168};
169
bc3c621b 170static void on_notebook_switch_page(GtkNotebook *nb, GtkTreeView *page, guint num,
0cdc072f
AG
171 PluginData *data)
172{
bc3c621b
AG
173 gboolean has_selection;
174
7e60a2a7 175 _edit_cleanup(data); /* abort edit */
bc3c621b
AG
176 data->current_page = page;
177 has_selection = gtk_tree_selection_get_selected(gtk_tree_view_get_selection(page), NULL, NULL);
178 gtk_action_set_sensitive(data->del_action, has_selection);
179 gtk_action_set_sensitive(data->edit_action, has_selection);
180}
181
182static void on_selection_changed(GtkTreeSelection *selection, PluginData *data)
183{
184 gboolean has_selection;
185
186 if (gtk_tree_view_get_selection(data->current_page) != selection)
187 return;
188
7e60a2a7 189 _edit_cleanup(data); /* abort edit */
bc3c621b
AG
190 has_selection = gtk_tree_selection_get_selected(selection, NULL, NULL);
191 gtk_action_set_sensitive(data->del_action, has_selection);
192 gtk_action_set_sensitive(data->edit_action, has_selection);
0cdc072f
AG
193}
194
1b4c5f1a
AG
195static void set_actions_list(PluginData *data)
196{
7e60a2a7
AG
197 GtkListStore *model = gtk_list_store_new(5, G_TYPE_STRING, G_TYPE_STRING,
198 G_TYPE_STRING, G_TYPE_STRING,
199 G_TYPE_POINTER);
60f97d42 200 GList *acts = data->cb->get_wm_keys(*data->config, "*", NULL);
b5447118
AG
201 GList *l;
202 LXHotkeyGlobal *act;
203 LXHotkeyAttr *attr, *opt;
204 char *val, *_val;
205 GtkTreeIter iter;
1b4c5f1a 206
60f97d42 207 for (l = acts; l; l = l->next)
b5447118
AG
208 {
209 act = l->data;
210 if (act->actions == NULL)
211 continue;
212 attr = act->actions->data;
213 _val = val = NULL;
214 opt = NULL;
215 if (attr->subopts)
216 {
217 opt = attr->subopts->data;
218 if (opt->values)
219 _val = val = g_strdup_printf("%s:%s", opt->name, (char *)opt->values->data);
220 else
221 val = opt->name;
222 }
223 gtk_list_store_insert_with_values(model, &iter, -1, 0, attr->name,
224 1, val,
225 2, act->accel1,
7e60a2a7
AG
226 3, act->accel2,
227 4, act, -1);
b5447118
AG
228 g_free(_val);
229 //FIXME: this is a stub, it should show something better than just first action
230 }
60f97d42 231 g_list_free(acts);
bc3c621b 232 gtk_tree_view_set_model(data->acts, GTK_TREE_MODEL(model));
1b4c5f1a
AG
233 g_object_unref(model);
234}
235
236static void set_apps_list(PluginData *data)
237{
7e60a2a7
AG
238 GtkListStore *model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING,
239 G_TYPE_STRING, G_TYPE_POINTER);
60f97d42 240 GList *apps = data->cb->get_app_keys(*data->config, "*", NULL);
1b4c5f1a
AG
241 GList *l;
242 LXHotkeyApp *app;
243 GtkTreeIter iter;
244
245 for (l = apps; l; l = l->next)
246 {
247 app = l->data;
248 gtk_list_store_insert_with_values(model, &iter, -1, 0, app->exec,
249 1, app->accel1,
7e60a2a7
AG
250 2, app->accel2,
251 3, app, -1);
1b4c5f1a
AG
252 }
253 g_list_free(apps);
bc3c621b 254 gtk_tree_view_set_model(data->apps, GTK_TREE_MODEL(model));
1b4c5f1a
AG
255 g_object_unref(model);
256}
257
0cdc072f 258static void module_gtk_run(const gchar *wm, const LXHotkeyPluginInit *cb,
60f97d42 259 gpointer *config, GError **error)
0cdc072f
AG
260{
261 GtkUIManager *ui;
262 GtkActionGroup *act_grp;
263 GtkAccelGroup *accel_grp;
264 GtkWidget *win, *menubar;
265 GtkToolbar *toolbar;
266 GtkBox *vbox;
0cdc072f 267 PluginData data;
0cdc072f 268
1b4c5f1a
AG
269 if (!inited)
270 gtk_init(&inited, NULL);
271 inited = 1;
0cdc072f 272
7e60a2a7
AG
273 /* force style for GtkComboBox, it's ugly when list reaches screen bottom */
274#if GTK_CHECK_VERSION(3, 0, 0)
275 GtkCssProvider *provider = gtk_css_provider_new();
276 if (gtk_css_provider_load_from_data(provider,
277 "#gtk-widget {\n"
278 "-GtkComboBox-appears-as-list : 1;\n"
279 "}\n", -1, NULL))
280 gtk_style_context_add_provider_for_screen(gdk_screen_get_default(),
281 GTK_STYLE_PROVIDER(provider),
282 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
283 g_object_unref(provider);
284#else
285 gtk_rc_parse_string("style 'default-style'\n"
286 "{\n"
287 " GtkComboBox::appears-as-list = 1\n"
288 "}\n"
289 "class 'GtkWidget' style 'default-style'");
290#endif
291
292 memset(&data, 0, sizeof(data));
0cdc072f
AG
293 data.wm = wm;
294 data.cb = cb;
295 data.config = config;
296
297 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
298 gtk_window_set_default_size(GTK_WINDOW(win), 400, 300);
87d89031 299 gtk_window_set_icon_name(GTK_WINDOW(win), LXHOTKEY_ICON);
6d55e2cc 300 g_signal_connect(win, "unmap", G_CALLBACK(gtk_main_quit), NULL);
0cdc072f
AG
301
302 vbox = (GtkBox *)gtk_vbox_new(FALSE, 0);
303
304 /* create menu bar and toolbar */
305 ui = gtk_ui_manager_new();
306 act_grp = gtk_action_group_new("Main");
307 gtk_action_group_set_translation_domain(act_grp, NULL);
308 gtk_action_group_add_actions(act_grp, actions, G_N_ELEMENTS(actions), &data);
309
310 accel_grp = gtk_ui_manager_get_accel_group(ui);
311 gtk_window_add_accel_group(GTK_WINDOW(win), accel_grp);
312
313 gtk_ui_manager_insert_action_group(ui, act_grp, 0);
314 gtk_ui_manager_add_ui_from_string(ui, menu_xml, -1, NULL);
315 g_object_unref(act_grp);
316
317 menubar = gtk_ui_manager_get_widget(ui, "/menubar");
318 toolbar = GTK_TOOLBAR(gtk_ui_manager_get_widget(ui, "/toolbar"));
319
87d89031
AG
320 data.save_action = gtk_ui_manager_get_action(ui, "/menubar/FileMenu/Save");
321 gtk_action_set_sensitive(data.save_action, FALSE);
322 data.del_action = gtk_ui_manager_get_action(ui, "/menubar/EditMenu/Del");
323 data.edit_action = gtk_ui_manager_get_action(ui, "/menubar/EditMenu/Edit");
324 gtk_action_set_sensitive(data.del_action, FALSE);
325 gtk_action_set_sensitive(data.edit_action, FALSE);
326
0cdc072f
AG
327 /* FIXME: use some style? */
328 gtk_toolbar_set_icon_size(toolbar, GTK_ICON_SIZE_SMALL_TOOLBAR);
329 gtk_toolbar_set_style(toolbar, GTK_TOOLBAR_ICONS);
330
331 gtk_box_pack_start(vbox, menubar, FALSE, TRUE, 0);
332 gtk_box_pack_start(vbox, GTK_WIDGET(toolbar), FALSE, TRUE, 0);
333
334 /* notebook - it contains two tabs: Actions and Programs */
1b4c5f1a
AG
335 data.notebook = (GtkNotebook*)gtk_notebook_new();
336 gtk_notebook_set_scrollable(data.notebook, TRUE);
337 gtk_container_set_border_width(GTK_CONTAINER(data.notebook), 0);
0cdc072f 338
1b4c5f1a 339 g_signal_connect_after(data.notebook, "switch-page",
0cdc072f
AG
340 G_CALLBACK(on_notebook_switch_page), &data);
341
1b4c5f1a 342 gtk_box_pack_start(vbox, GTK_WIDGET(data.notebook), TRUE, TRUE, 0);
0cdc072f
AG
343
344 /* setup notebook */
1b4c5f1a
AG
345 if (cb->get_wm_keys)
346 {
bc3c621b
AG
347 data.acts = GTK_TREE_VIEW(gtk_tree_view_new());
348 gtk_tree_view_insert_column_with_attributes(data.acts, 0, _("Action"),
349 gtk_cell_renderer_text_new(),
1b4c5f1a 350 "text", 0, NULL);
bc3c621b
AG
351 gtk_tree_view_insert_column_with_attributes(data.acts, 1, _("Option"),
352 gtk_cell_renderer_text_new(),
1b4c5f1a 353 "text", 1, NULL);
bc3c621b
AG
354 gtk_tree_view_insert_column_with_attributes(data.acts, 2, _("Hotkey 1"),
355 gtk_cell_renderer_text_new(),
1b4c5f1a 356 "text", 2, NULL);
bc3c621b
AG
357 gtk_tree_view_insert_column_with_attributes(data.acts, 3, _("Hotkey 2"),
358 gtk_cell_renderer_text_new(),
b5447118 359 "text", 3, NULL);
1b4c5f1a 360 set_actions_list(&data);
bc3c621b
AG
361 //FIXME: connect "row-activated" for Edit
362 g_signal_connect(gtk_tree_view_get_selection(data.acts), "changed",
363 G_CALLBACK(on_selection_changed), &data);
364 gtk_notebook_append_page(data.notebook, GTK_WIDGET(data.acts),
365 gtk_label_new(_("Actions")));
1b4c5f1a
AG
366 }
367
368 if (cb->get_app_keys)
369 {
bc3c621b
AG
370 data.apps = GTK_TREE_VIEW(gtk_tree_view_new());
371 gtk_tree_view_insert_column_with_attributes(data.apps, 0, _("Command"),
372 gtk_cell_renderer_text_new(),
1b4c5f1a 373 "text", 0, NULL);
bc3c621b
AG
374 gtk_tree_view_insert_column_with_attributes(data.apps, 1, _("Hotkey 1"),
375 gtk_cell_renderer_text_new(),
1b4c5f1a 376 "text", 1, NULL);
bc3c621b
AG
377 gtk_tree_view_insert_column_with_attributes(data.apps, 2, _("Hotkey 2"),
378 gtk_cell_renderer_text_new(),
1b4c5f1a
AG
379 "text", 2, NULL);
380 set_apps_list(&data);
bc3c621b
AG
381 //FIXME: connect "row-activated" for Edit
382 g_signal_connect(gtk_tree_view_get_selection(data.apps), "changed",
383 G_CALLBACK(on_selection_changed), &data);
384 gtk_notebook_append_page(data.notebook, GTK_WIDGET(data.apps),
385 gtk_label_new(_("Programs")));
1b4c5f1a 386 }
0cdc072f
AG
387
388 /* and finally run it all */
7e60a2a7 389 data.current_page = (GtkTreeView *)gtk_notebook_get_nth_page(data.notebook, 0);
0cdc072f
AG
390 gtk_container_add(GTK_CONTAINER(win), GTK_WIDGET(vbox));
391 gtk_widget_show_all(win);
392 gtk_main();
7e60a2a7 393 _edit_cleanup(&data);
0cdc072f
AG
394}
395
396static void module_gtk_alert(GError *error)
397{
398}
399
1b4c5f1a
AG
400static void module_gtk_init(int argc, char **argv)
401{
402 if (!inited)
403 gtk_init(&argc, &argv);
404 inited = 1;
405}
406
0cdc072f
AG
407FM_DEFINE_MODULE(lxhotkey_gui, gtk)
408
409LXHotkeyGUIPluginInit fm_module_init_lxhotkey_gui = {
410 .run = module_gtk_run,
1b4c5f1a
AG
411 .alert = module_gtk_alert,
412 .init = module_gtk_init
0cdc072f 413};