9035e8dab811d2716af8406b17469c38ced47739
[dana/obconf.git] / src / main.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    main.c for ObConf, the configuration tool for Openbox
4    Copyright (c) 2003-2008   Dana Jansens
5    Copyright (c) 2003        Tim Riley
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "main.h"
21 #include "archive.h"
22 #include "theme.h"
23 #include "appearance.h"
24 #include "windows.h"
25 #include "margins.h"
26 #include "mouse.h"
27 #include "desktops.h"
28 #include "dock.h"
29 #include "preview_update.h"
30 #include "gettext.h"
31
32 #include <gdk/gdkx.h>
33 #define SN_API_NOT_YET_FROZEN
34 #include <libsn/sn.h>
35 #undef SN_API_NOT_YET_FROZEN
36 #include <stdlib.h>
37
38 GtkWidget *mainwin = NULL;
39
40 GladeXML *glade;
41 xmlDocPtr doc;
42 xmlNodePtr root;
43 RrInstance *rrinst;
44 gchar *obc_config_file = NULL;
45 ObtPaths *paths;
46 ObtXmlInst *parse_i;
47
48 static gchar *obc_theme_install = NULL;
49 static gchar *obc_theme_archive = NULL;
50
51 void obconf_error(gchar *msg, gboolean modal)
52 {
53     GtkWidget *d;
54
55     d = gtk_message_dialog_new(mainwin ? GTK_WINDOW(mainwin) : NULL,
56                                GTK_DIALOG_DESTROY_WITH_PARENT,
57                                GTK_MESSAGE_ERROR,
58                                GTK_BUTTONS_CLOSE,
59                                "%s", msg);
60     gtk_window_set_title(GTK_WINDOW(d), "ObConf Error");
61     if (modal)
62         gtk_dialog_run(GTK_DIALOG(d));
63     else {
64         g_signal_connect_swapped(GTK_OBJECT(d), "response",
65                                  G_CALLBACK(gtk_widget_destroy),
66                                  GTK_OBJECT(d));
67         gtk_widget_show(d);
68     }
69 }
70
71 static void print_version()
72 {
73     g_print("ObConf %s\n", PACKAGE_VERSION);
74     g_print(_("Copyright (c)"));
75     g_print(" 2003-2008   Dana Jansens\n");
76     g_print(_("Copyright (c)"));
77     g_print(" 2003        Tim Riley\n");
78     g_print(_("Copyright (c)"));
79     g_print(" 2007        Javeed Shaikh\n\n");
80     g_print("This program comes with ABSOLUTELY NO WARRANTY.\n");
81     g_print("This is free software, and you are welcome to redistribute it\n");
82     g_print("under certain conditions. See the file COPYING for details.\n\n");
83
84     exit(EXIT_SUCCESS);
85 }
86
87 static void print_help()
88 {
89     g_print(_("Syntax: obconf [options] [ARCHIVE.obt]\n"));
90     g_print(_("\nOptions:\n"));
91     g_print(_("  --help                Display this help and exit\n"));
92     g_print(_("  --version             Display the version and exit\n"));
93     g_print(_("  --install ARCHIVE.obt Install the given theme archive and select it\n"));
94     g_print(_("  --archive THEME       Create a theme archive from the given theme directory\n"));
95     g_print(_("  --config-file FILE    Specify the path to the config file to use\n"));
96     g_print(_("\nPlease report bugs at %s\n\n"), PACKAGE_BUGREPORT);
97     
98     exit(EXIT_SUCCESS);
99 }
100
101 static void parse_args(int argc, char **argv)
102 {
103     int i;
104
105     for (i = 1; i < argc; ++i) {
106         if (!strcmp(argv[i], "--help"))
107             print_help();
108         if (!strcmp(argv[i], "--version"))
109             print_version();
110         else if (!strcmp(argv[i], "--install")) {
111             if (i == argc - 1) /* no args left */
112                 g_printerr(_("--install requires an argument\n"));
113             else
114                 obc_theme_install = argv[++i];
115         }
116         else if (!strcmp(argv[i], "--archive")) {
117             if (i == argc - 1) /* no args left */
118                 g_printerr(_("--archive requires an argument\n"));
119             else
120                 obc_theme_archive = argv[++i];
121         }
122         else if (!strcmp(argv[i], "--config-file")) {
123             if (i == argc - 1) /* no args left */
124                 g_printerr(_("--config-file requires an argument\n"));
125             else
126                 obc_config_file = argv[++i];
127         } else
128             obc_theme_install = argv[i];
129     }
130 }
131
132 static gboolean get_all(Window win, Atom prop, Atom type, gint size,
133                         guchar **data, guint *num)
134 {
135     gboolean ret = FALSE;
136     gint res;
137     guchar *xdata = NULL;
138     Atom ret_type;
139     gint ret_size;
140     gulong ret_items, bytes_left;
141
142     res = XGetWindowProperty(GDK_DISPLAY(), win, prop, 0l, G_MAXLONG,
143                              FALSE, type, &ret_type, &ret_size,
144                              &ret_items, &bytes_left, &xdata);
145     if (res == Success) {
146         if (ret_size == size && ret_items > 0) {
147             guint i;
148
149             *data = g_malloc(ret_items * (size / 8));
150             for (i = 0; i < ret_items; ++i)
151                 switch (size) {
152                 case 8:
153                     (*data)[i] = xdata[i];
154                     break;
155                 case 16:
156                     ((guint16*)*data)[i] = ((gushort*)xdata)[i];
157                     break;
158                 case 32:
159                     ((guint32*)*data)[i] = ((gulong*)xdata)[i];
160                     break;
161                 default:
162                     g_assert_not_reached(); /* unhandled size */
163                 }
164             *num = ret_items;
165             ret = TRUE;
166         }
167         XFree(xdata);
168     }
169     return ret;
170 }
171
172 static gboolean prop_get_string_utf8(Window win, Atom prop, gchar **ret)
173 {
174     gchar *raw;
175     gchar *str;
176     guint num;
177
178     if (get_all(win, prop,
179                 gdk_x11_get_xatom_by_name("UTF8_STRING"),
180                 8,(guchar**)&raw, &num))
181     {
182         str = g_strndup(raw, num); /* grab the first string from the list */
183         g_free(raw);
184         if (g_utf8_validate(str, -1, NULL)) {
185             *ret = str;
186             return TRUE;
187         }
188         g_free(str);
189     }
190     return FALSE;
191 }
192
193 int main(int argc, char **argv)
194 {
195     gchar *p;
196     gboolean exit_with_error = FALSE;
197
198     bindtextdomain(PACKAGE_NAME, LOCALEDIR);
199     bind_textdomain_codeset(PACKAGE_NAME, "UTF-8");
200     textdomain(PACKAGE_NAME);
201
202     gtk_init(&argc, &argv);
203     parse_args(argc, argv);
204
205     if (obc_theme_archive) {
206         archive_create(obc_theme_archive);
207         return;
208     }
209
210     p = g_build_filename(GLADEDIR, "obconf.glade", NULL);
211     glade = glade_xml_new(p, NULL, NULL);
212     g_free(p);
213
214     if (!glade) {
215         obconf_error(_("Failed to load the obconf.glade interface file. You have probably failed to install ObConf properly."), TRUE);
216         exit_with_error = TRUE;
217     }
218
219     paths = obt_paths_new();
220     parse_i = obt_xml_instance_new();
221     rrinst = RrInstanceNew(GDK_DISPLAY(), gdk_x11_get_default_screen());
222
223     if (!obc_config_file) {
224         gchar *p;
225
226         if (prop_get_string_utf8(GDK_ROOT_WINDOW(),
227                                  gdk_x11_get_xatom_by_name("_OB_CONFIG_FILE"),
228                                  &p))
229         {
230             obc_config_file = g_filename_from_utf8(p, -1, NULL, NULL, NULL);
231             g_free(p);
232         }
233     }
234
235     xmlIndentTreeOutput = 1;
236     if (!obt_xml_load_config_file(parse_i,
237                                   "openbox",
238                                   (obc_config_file ?
239                                    obc_config_file : "rc.xml"),
240                                   "openbox_config"))
241     {
242         obconf_error(_("Failed to load an rc.xml. You have probably failed to install Openbox properly."), TRUE);
243         exit_with_error = TRUE;
244     }
245     else {
246         doc = obt_xml_doc(parse_i);
247         root = obt_xml_root(parse_i);
248     }
249
250     /* look for parsing errors */
251     {
252         xmlErrorPtr e = xmlGetLastError();
253         if (e) {
254             char *a = g_strdup_printf
255                 (_("Error while parsing the Openbox configuration file.  Your configuration file is not valid XML.\n\nMessage: %s"),
256                  e->message);
257             obconf_error(a, TRUE);
258             g_free(a);
259             exit_with_error = TRUE;
260         }
261     }
262
263     if (!exit_with_error) {
264         glade_xml_signal_autoconnect(glade);
265
266         {
267             gchar *s = g_strdup_printf
268                 ("<span weight=\"bold\" size=\"xx-large\">ObConf %s</span>",
269                  PACKAGE_VERSION);
270             gtk_label_set_markup(GTK_LABEL
271                                  (glade_xml_get_widget(glade, "title_label")),
272                                  s);
273             g_free(s);
274         }
275
276         theme_setup_tab();
277         appearance_setup_tab();
278         windows_setup_tab();
279         moveresize_setup_tab();
280         mouse_setup_tab();
281         desktops_setup_tab();
282         margins_setup_tab();
283         dock_setup_tab();
284
285         mainwin = get_widget("main_window");
286
287         if (obc_theme_install)
288             theme_install(obc_theme_install);
289         else
290             theme_load_all();
291
292         /* the main window is not shown here ! it is shown when the theme
293            previews are completed */
294         gtk_main();
295
296         preview_update_set_active_font(NULL);
297         preview_update_set_inactive_font(NULL);
298         preview_update_set_menu_header_font(NULL);
299         preview_update_set_menu_item_font(NULL);
300         preview_update_set_osd_active_font(NULL);
301         preview_update_set_osd_inactive_font(NULL);
302         preview_update_set_title_layout(NULL);
303     }
304
305     RrInstanceFree(rrinst);
306     obt_xml_instance_unref(parse_i);
307     obt_paths_unref(paths);
308
309     xmlFreeDoc(doc);
310     return 0;
311 }
312
313 gboolean on_main_window_delete_event(GtkWidget *w, GdkEvent *e, gpointer d)
314 {
315     gtk_main_quit();
316     return FALSE;
317 }
318
319 void on_close_clicked()
320 {
321     gtk_main_quit();
322 }
323
324 void obconf_show_main()
325 {
326     SnDisplay *sn_d;
327     SnLauncheeContext *sn_cx;
328
329     if (GTK_WIDGET_VISIBLE(mainwin)) return;
330
331     gtk_widget_show_all(mainwin);
332
333     sn_d = sn_display_new(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
334                           NULL, NULL);
335
336     sn_cx = sn_launchee_context_new_from_environment
337         (sn_d, gdk_screen_get_number(gdk_display_get_default_screen
338                                      (gdk_display_get_default())));
339
340     if (sn_cx)
341         sn_launchee_context_setup_window
342             (sn_cx, GDK_WINDOW_XWINDOW(GDK_WINDOW(mainwin->window)));
343
344     if (sn_cx)
345         sn_launchee_context_complete(sn_cx);
346
347     if (sn_cx)
348         sn_launchee_context_unref(sn_cx);
349     sn_display_unref(sn_d);
350 }