1 /* gdict-app.c - main application class
2  *
3  * This file is part of GNOME Dictionary
4  *
5  * Copyright (C) 2005 Emmanuele Bassi
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of
10  * the License, or (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 GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "config.h"
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <math.h>
27 #include <sys/stat.h>
28 
29 #include <gtk/gtk.h>
30 #include <glib.h>
31 #include <glib/gi18n.h>
32 
33 #include "gdict-common.h"
34 #include "gdict-about.h"
35 #include "gdict-pref-dialog.h"
36 #include "gdict-app.h"
37 
38 G_DEFINE_TYPE (GdictApp, gdict_app, GTK_TYPE_APPLICATION)
39 
40 static GOptionEntry gdict_app_goptions[] = {
41   {
42     "look-up", 0,
43     0,
44     G_OPTION_ARG_STRING_ARRAY, NULL,
45     N_("Words to look up"), N_("WORD")
46   },
47   {
48     "match", 0,
49     0,
50     G_OPTION_ARG_STRING_ARRAY, NULL,
51     N_("Words to match"), N_("WORD")
52   },
53   {
54     "source", 's',
55     0,
56     G_OPTION_ARG_STRING, NULL,
57     N_("Dictionary source to use"), N_("NAME")
58   },
59   {
60     "database", 'D',
61     0,
62     G_OPTION_ARG_STRING, NULL,
63     N_("Database to use"), N_("NAME")
64   },
65   {
66     "strategy", 'S',
67     0,
68     G_OPTION_ARG_STRING, NULL,
69     N_("Strategy to use"), N_("NAME")
70   },
71   {
72     G_OPTION_REMAINING, 0, 0,
73     G_OPTION_ARG_STRING_ARRAY, NULL,
74     N_("Words to look up"), N_("WORDS")
75   },
76   { NULL },
77 };
78 
79 static void
gdict_app_cmd_new(GSimpleAction * action,GVariant * parameter,gpointer user_data)80 gdict_app_cmd_new (GSimpleAction *action,
81                    GVariant      *parameter,
82                    gpointer       user_data)
83 {
84   GdictApp *app = user_data;
85   GtkWidget *window = gdict_window_new (GDICT_WINDOW_ACTION_CLEAR,
86                                         GTK_APPLICATION (app),
87                                         app->loader,
88                                         NULL, NULL, NULL,
89                                         NULL);
90 
91   gtk_widget_show (window);
92 }
93 
94 static void
gdict_app_cmd_preferences(GSimpleAction * action,GVariant * parameter,gpointer user_data)95 gdict_app_cmd_preferences (GSimpleAction *action,
96                            GVariant      *parameter,
97                            gpointer       user_data)
98 {
99   GtkApplication *app = user_data;
100   GdictWindow *window;
101 
102   g_assert (GTK_IS_APPLICATION (app));
103 
104   window = GDICT_WINDOW (gtk_application_get_windows (app)->data);
105   gdict_show_pref_dialog (GTK_WIDGET (window),
106                           _("Dictionary Preferences"),
107                           window->loader);
108 }
109 
110 static void
gdict_app_cmd_help(GSimpleAction * action,GVariant * parameter,gpointer user_data)111 gdict_app_cmd_help (GSimpleAction *action,
112                     GVariant      *parameter,
113                     gpointer       user_data)
114 {
115   GtkApplication *app = user_data;
116   GdictWindow *window;
117   GError *err = NULL;
118 
119   g_return_if_fail (GTK_IS_APPLICATION (app));
120 
121   window = GDICT_WINDOW (gtk_application_get_windows (app)->data);
122 
123 #if GTK_CHECK_VERSION (3, 22, 0)
124   gtk_show_uri_on_window (GTK_WINDOW (window),
125                           "help:gnome-dictionary",
126                           gtk_get_current_event_time (),
127                           &err);
128 #else
129   gtk_show_uri (gtk_widget_get_screen (GTK_WIDGET (window)),
130                 "help:gnome-dictionary",
131                 gtk_get_current_event_time (), &err);
132 #endif
133 
134   if (err)
135     {
136       gdict_show_gerror_dialog (GTK_WINDOW (window),
137                                 _("There was an error while displaying help"),
138                                 err);
139       g_error_free (err);
140     }
141 }
142 
143 static void
gdict_app_cmd_about(GSimpleAction * action,GVariant * parameter,gpointer user_data)144 gdict_app_cmd_about (GSimpleAction *action,
145                      GVariant      *parameter,
146                      gpointer       user_data)
147 {
148   GtkApplication *app = user_data;
149   GdictWindow *window;
150 
151   g_assert (GTK_IS_APPLICATION (app));
152 
153   window = GDICT_WINDOW (gtk_application_get_windows (app)->data);
154   gdict_show_about_dialog (GTK_WIDGET (window));
155 }
156 
157 static void
gdict_app_cmd_quit(GSimpleAction * action,GVariant * parameter,gpointer user_data)158 gdict_app_cmd_quit (GSimpleAction *action,
159                     GVariant      *parameter,
160                     gpointer       user_data)
161 {
162   GtkApplication *app = user_data;
163   GList *windows;
164 
165   g_assert (GTK_IS_APPLICATION (app));
166 
167   windows = gtk_application_get_windows (app);
168   g_list_foreach (windows, (GFunc)gtk_widget_destroy, NULL);
169 }
170 
171 static void
gdict_app_cmd_close(GSimpleAction * action,GVariant * variant,gpointer user_data)172 gdict_app_cmd_close (GSimpleAction *action,
173           GVariant *variant,
174           gpointer user_data)
175 {
176     GtkApplication *app = user_data;
177     GList *windows;
178 
179     windows = gtk_application_get_windows (app);
180 
181     if (windows != NULL)
182       gtk_window_close (windows->data);
183 }
184 
185 static const GActionEntry app_entries[] =
186 {
187   { "new", gdict_app_cmd_new, NULL, NULL, NULL },
188   { "preferences", gdict_app_cmd_preferences, NULL, NULL, NULL },
189   { "help", gdict_app_cmd_help, NULL, NULL, NULL },
190   { "about", gdict_app_cmd_about, NULL, NULL, NULL },
191   { "quit", gdict_app_cmd_quit, NULL, NULL, NULL },
192   { "close", gdict_app_cmd_close, NULL, NULL, NULL }
193 };
194 
195 static void
gdict_app_dispose(GObject * object)196 gdict_app_dispose (GObject *object)
197 {
198   GdictApp *app = GDICT_APP (object);
199 
200   g_clear_object (&app->loader);
201 
202   G_OBJECT_CLASS (gdict_app_parent_class)->dispose (object);
203 }
204 
205 static gchar **
strv_concat(gchar ** strv1,gchar ** strv2)206 strv_concat (gchar **strv1, gchar **strv2)
207 {
208   gchar **tmp;
209   guint len1, len2;
210   gint i;
211 
212   len1 = g_strv_length (strv1);
213   len2 = g_strv_length (strv2);
214   tmp = g_realloc (strv1, len1 + len2 + 1);
215   for (i = 0; i < len2; i++)
216     tmp[len1 + i] = (gchar *)strv2[i];
217   tmp[len1 + len2] = NULL;
218 
219   return tmp;
220 }
221 
222 static int
gdict_app_command_line(GApplication * application,GApplicationCommandLine * cmd_line)223 gdict_app_command_line (GApplication            *application,
224                         GApplicationCommandLine *cmd_line)
225 {
226   GdictApp *app = GDICT_APP (application);
227   gsize words_len, i;
228   GVariantDict *options;
229   gchar **gdict_lookup_words = NULL;
230   gchar **gdict_match_words = NULL;
231   gchar **remaining = NULL;
232   const gchar *gdict_source_name = NULL;
233   const gchar *gdict_database_name = NULL;
234   const gchar *gdict_strategy_name = NULL;
235 
236   options = g_application_command_line_get_options_dict (cmd_line);
237 
238   g_variant_dict_lookup (options, "look-up", "^as", &gdict_lookup_words);
239   g_variant_dict_lookup (options, "match", "^as", &gdict_match_words);
240   g_variant_dict_lookup (options, "source", "&s", &gdict_source_name);
241   g_variant_dict_lookup (options, "database", "&s", &gdict_database_name);
242   g_variant_dict_lookup (options, "strategy", "&s", &gdict_strategy_name);
243   g_variant_dict_lookup (options, G_OPTION_REMAINING, "^as", &remaining);
244 
245   if (remaining != NULL)
246     {
247       if (gdict_match_words != NULL)
248         {
249           gchar **tmp;
250           tmp = strv_concat (gdict_match_words, remaining);
251           g_strfreev (gdict_match_words);
252           g_strfreev (remaining);
253           gdict_match_words = tmp;
254         }
255       else
256         {
257           gdict_match_words = remaining;
258         }
259       remaining = NULL;
260     }
261 
262   if (gdict_lookup_words == NULL &&
263       gdict_match_words == NULL)
264     {
265       GtkWidget *window = gdict_window_new (GDICT_WINDOW_ACTION_CLEAR,
266                                             GTK_APPLICATION (application),
267                                             app->loader,
268                                             gdict_source_name,
269                                             gdict_database_name,
270                                             gdict_strategy_name,
271                                             NULL);
272       gtk_widget_show (window);
273 
274       goto out;
275     }
276 
277   if (gdict_lookup_words != NULL)
278     words_len = g_strv_length (gdict_lookup_words);
279   else
280     words_len = 0;
281 
282   for (i = 0; i < words_len; i++)
283     {
284       const gchar *word = gdict_lookup_words[i];
285       GtkWidget *window;
286 
287       window = gdict_window_new (GDICT_WINDOW_ACTION_LOOKUP,
288                                  GTK_APPLICATION (application),
289                                  app->loader,
290                                  gdict_source_name,
291                                  gdict_database_name,
292                                  gdict_strategy_name,
293                                  word);
294 
295       gtk_widget_show (window);
296     }
297 
298   if (gdict_match_words != NULL)
299     words_len = g_strv_length (gdict_match_words);
300   else
301     words_len = 0;
302 
303   for (i = 0; i < words_len; i++)
304     {
305       const gchar *word = gdict_match_words[i];
306       GtkWidget *window;
307 
308       window = gdict_window_new (GDICT_WINDOW_ACTION_MATCH,
309                                  GTK_APPLICATION (application),
310                                  app->loader,
311 				 gdict_source_name,
312                                  gdict_database_name,
313                                  gdict_strategy_name,
314 				 word);
315 
316       gtk_widget_show (window);
317     }
318 
319 out:
320   g_strfreev (gdict_lookup_words);
321   g_strfreev (gdict_match_words);
322 
323   return 0;
324 }
325 
326 static void
gdict_app_activate(GApplication * application)327 gdict_app_activate (GApplication *application)
328 {
329   GdictApp *app = GDICT_APP (application);
330   GtkWidget *window = gdict_window_new (GDICT_WINDOW_ACTION_CLEAR,
331                                         GTK_APPLICATION (application),
332                                         app->loader,
333                                         NULL, NULL, NULL,
334                                         NULL);
335 
336   gtk_widget_show (window);
337 }
338 
339 static void
gdict_app_startup(GApplication * application)340 gdict_app_startup (GApplication *application)
341 {
342   static const gchar *action_accels[] =
343   {
344     /* Win shortcuts */
345     "win.save-as",          "<Primary>s", NULL,
346     "win.preview",          "<Primary><Shift>p", NULL ,
347     "win.print",            "<Primary>p", NULL,
348     "win.find",             "<Primary>f", NULL,
349     "win.previous-def",     "Page_Up", NULL,
350     "win.next-def",         "Page_Down", NULL,
351     "win.first-def",        "Home", NULL,
352     "win.last-def",         "End", NULL,
353     "win.view-sidebar",     "F9", NULL,
354     "win.view-speller",     "<Primary>t", NULL,
355     "win.view-source",      "<Primary>d", NULL,
356     "win.view-strat",       "<Primary>r", NULL,
357     "win.lookup",           "<Primary>l", NULL,
358     "win.escape",           "Escape", NULL,
359     "win.help-overlay-ui",  "<Primary>question", NULL,
360     /* App shortcuts */
361     "app.new",              "<Primary>n", NULL,
362     "app.preferences",      "<Primary>comma", NULL,
363     "app.help",             "F1", NULL,
364     "app.close",            "<Primary>w", NULL,
365     "app.quit",             "<Primary>q", NULL,
366     NULL /* Terminating NULL */
367   };
368 
369   g_set_application_name (_("Dictionary"));
370 
371   G_APPLICATION_CLASS (gdict_app_parent_class)->startup (application);
372 
373   g_action_map_add_action_entries (G_ACTION_MAP (application), app_entries,
374                                    G_N_ELEMENTS (app_entries), application);
375 
376   GtkApplication *gtk_app = GTK_APPLICATION (application);
377   for (const char **it = action_accels;
378        it[0] != NULL;
379        it += g_strv_length ((char **) it) + 1)
380     {
381       gtk_application_set_accels_for_action (gtk_app, it[0], &it[1]);
382     }
383 }
384 
385 static void
gdict_app_class_init(GdictAppClass * klass)386 gdict_app_class_init (GdictAppClass *klass)
387 {
388   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
389   GApplicationClass *application_class = G_APPLICATION_CLASS (klass);
390 
391   gobject_class->dispose = gdict_app_dispose;
392 
393   application_class->startup = gdict_app_startup;
394   application_class->activate = gdict_app_activate;
395   application_class->command_line = gdict_app_command_line;
396 }
397 
398 static void
gdict_app_init(GdictApp * app)399 gdict_app_init (GdictApp *app)
400 {
401   char *loader_path;
402 
403   /* add user's path for fetching dictionary sources */
404   app->loader = gdict_source_loader_new ();
405   loader_path = gdict_get_config_dir ();
406   gdict_source_loader_add_search_path (app->loader, loader_path);
407   g_free (loader_path);
408 
409   /* Add the command line options */
410   g_application_add_main_option_entries (G_APPLICATION (app), gdict_app_goptions);
411 
412   /* Set main application icon */
413   gtk_window_set_default_icon_name (APPLICATION_ID);
414 }
415 
416 GApplication *
gdict_app_new(void)417 gdict_app_new (void)
418 {
419   return g_object_new (gdict_app_get_type (),
420                        "application-id", APPLICATION_ID,
421                        "resource-base-path", "/org/gnome/Dictionary",
422                        "flags", G_APPLICATION_HANDLES_COMMAND_LINE,
423                        NULL);
424 }
425