1b9e0a6e2642f5edfb532f812e02131b547d2b32
[mstardict] / src / mstardict.cpp
1 /*
2  *  MStarDict - International dictionary for Maemo.
3  *  Copyright (C) 2010 Roman Moravcik
4  *
5  *  base on code of stardict:
6  *  Copyright (C) 2003-2007 Hu Zheng <huzheng_001@163.com>
7  *
8  *  based on code of sdcv:
9  *  Copyright (C) 2005-2006 Evgeniy <dushistov@mail.ru>
10  *
11  *  This program is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation; either version 2 of the License, or
14  *  (at your option) any later version.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program; if not, write to the Free Software
23  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #  include "config.h"
28 #endif
29
30 #include <cerrno>
31 #include <cstring>
32 #include <cstdlib>
33 #include <cstdio>
34 #include <clocale>
35
36 #include <glib.h>
37 #include <glib/gi18n.h>
38 #include <glib/gstdio.h>
39
40 #include <gtk/gtk.h>
41 #include <hildon/hildon.h>
42
43 #include <getopt.h>
44 #include <string>
45 #include <vector>
46 #include <memory>
47 #include <list>
48
49 #include "conf.hpp"
50 #include "dictmngr.hpp"
51 #include "libwrapper.hpp"
52 #include "transwin.hpp"
53 #include "mstardict.hpp"
54
55 MStarDict *pMStarDict;
56
57 enum {
58     DEF_COLUMN,
59     N_COLUMNS
60 };
61
62 MStarDict::MStarDict()
63 {
64     window = NULL;
65     label_widget = NULL;
66     results_widget = NULL;
67     results_view = NULL;
68     results_view_scroll = NULL;
69
70     /* create list of ressults */
71     results_list = gtk_list_store_new(N_COLUMNS,
72                                       G_TYPE_STRING);   /* DEF_COLUMN */
73
74     /* initialize configuration */
75     oConf = new Conf();
76
77     /* initialize stardict plugins */
78     std::list < std::string > plugin_order_list;
79     std::list < std::string > plugin_disable_list;
80     oStarDictPlugins = new StarDictPlugins("/usr/lib/mstardict/plugins",
81                                            plugin_order_list,
82                                            plugin_disable_list);
83
84     /* initialize dict manager */
85     oDict = new DictMngr(this);
86
87     /* initialize translation window */
88     oTransWin = new TransWin(this);
89
90     /* initialize stardict library */
91     oLibs = new Library(this);
92 }
93
94 MStarDict::~MStarDict()
95 {
96     /* destroy list of results */
97     g_object_unref(results_list);
98
99     /* deinitialize stardict library */
100     delete oLibs;
101
102     /* deinitialize translation window */
103     delete oTransWin;
104
105     /* deinitialize dict manager */
106     delete oDict;
107
108     /* deinitialize stardict plugins */
109     delete oStarDictPlugins;
110
111     /* deinitialize configuration */
112     delete oConf;
113 }
114
115 gboolean
116 MStarDict::onResultsViewSelectionChanged(GtkTreeSelection *selection,
117                                          MStarDict *mStarDict)
118 {
119     GtkTreeModel *model;
120     GtkTreeIter iter;
121     const gchar *sWord;
122     bool bFound = false;
123
124     if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
125         GList *results = NULL;
126
127         /* unselect selected rows */
128         gtk_tree_selection_unselect_all(selection);
129
130         gtk_tree_model_get(model, &iter, DEF_COLUMN, &sWord, -1);
131
132         for (size_t iLib = 0; iLib < mStarDict->oLibs->query_dictmask.size(); iLib++) {
133             bFound =
134                 mStarDict->oLibs->BuildResultData(mStarDict->oLibs->query_dictmask, sWord,
135                                                   mStarDict->oLibs->iCurrentIndex, iLib,
136                                                   &results);
137         }
138
139         /* create translation window */
140         mStarDict->oTransWin->CreateTransWindow(results);
141
142         /* free result data */
143         mStarDict->oLibs->FreeResultData(results);
144     }
145
146     /* grab focus to search entry */
147     mStarDict->GrabFocus();
148
149     return true;
150 }
151
152 gboolean
153 MStarDict::onSearchEntryChanged(GtkEditable* editable,
154                                 MStarDict* mStarDict)
155 {
156     const gchar *sWord;
157     bool bFound = false;
158     std::string query;
159
160     if (mStarDict->oLibs->query_dictmask.empty())
161         return true;
162
163     sWord = gtk_entry_get_text(GTK_ENTRY(editable));
164     if (strcmp(sWord, "") == 0) {
165         mStarDict->ShowNoResults(true);
166     } else {
167         mStarDict->ShowProgressIndicator(true);
168         mStarDict->ResultsUnselectAll(GTK_SELECTION_NONE);
169
170         switch (analyse_query(sWord, query)) {
171         case qtFUZZY:
172             bFound = mStarDict->oLibs->LookupWithFuzzy(query.c_str());
173             break;
174
175         case qtPATTERN:
176             bFound = mStarDict->oLibs->LookupWithRule(query.c_str());
177             break;
178
179         case qtREGEX:
180             bFound = mStarDict->oLibs->LookupWithRegex(query.c_str());
181             break;
182
183         case qtSIMPLE:
184             bFound = mStarDict->oLibs->SimpleLookup(query.c_str(), mStarDict->oLibs->iCurrentIndex);
185             if (!bFound) {
186                 const gchar *sugWord = mStarDict->oLibs->GetSuggestWord(query.c_str(),
187                                                                         mStarDict->
188                                                                         oLibs->iCurrentIndex,
189                                                                         mStarDict->
190                                                                         oLibs->query_dictmask, 0);
191                 if (sugWord) {
192                     gchar *sSugWord = g_strdup(sugWord);
193                     bFound =
194                         mStarDict->oLibs->SimpleLookup(sSugWord, mStarDict->oLibs->iCurrentIndex);
195                     g_free(sSugWord);
196                 }
197             }
198             mStarDict->oLibs->ListWords(mStarDict->oLibs->iCurrentIndex);
199             break;
200
201         default:
202             break;
203         }
204
205         if (bFound)
206             mStarDict->ShowNoResults(false);
207         else
208             mStarDict->ShowNoResults(true);
209
210         mStarDict->ResultsUnselectAll(GTK_SELECTION_SINGLE);
211         mStarDict->ShowProgressIndicator(false);
212     }
213
214     return true;
215 }
216
217 gboolean
218 MStarDict::onSearchClearClicked(GtkButton* button,
219                                 MStarDict* mStarDict)
220 {
221     gtk_entry_set_text(GTK_ENTRY(mStarDict->search_entry), "");
222     mStarDict->GrabFocus();
223     return true;
224 }
225
226 gboolean
227 MStarDict::onDictionariesMenuItemClicked(GtkButton *button,
228                                          MStarDict *mStarDict)
229 {
230     mStarDict->oDict->CreateDictMngrDialog();
231
232     /* trigger re-search */
233     mStarDict->onSearchEntryChanged(GTK_EDITABLE(mStarDict->search_entry), mStarDict);
234     mStarDict->GrabFocus();
235     return true;
236 }
237
238 gboolean
239 MStarDict::onQuitMenuItemClicked(GtkButton *button,
240                                  MStarDict *mStarDict)
241 {
242     gtk_main_quit();
243     return true;
244 }
245
246 gboolean
247 MStarDict::onLookupProgressDialogResponse(GtkDialog *dialog,
248                                           gint response_id,
249                                           bool *cancel)
250 {
251     *cancel = true;
252     return true;
253 }
254
255 gboolean
256 MStarDict::onMainWindowKeyPressEvent(GtkWidget *window,
257                                      GdkEventKey *event,
258                                      MStarDict *mStarDict)
259 {
260     if (event->type == GDK_KEY_PRESS && event->keyval == GDK_KP_Enter) {
261         mStarDict->SearchWord();
262     } else if (event->type == GDK_KEY_PRESS && event->keyval >= 0x21 && event->keyval <= 0x7E) {
263         mStarDict->GrabFocus();
264     }
265     return false;
266 }
267
268 GtkWidget *
269 MStarDict::CreateLookupProgressDialog(bool *cancel)
270 {
271     GtkWidget *dialog, *progress;
272
273     /* create dialog */
274     dialog = gtk_dialog_new();
275     gtk_window_set_title(GTK_WINDOW(dialog), _("Searching"));
276     gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(window));
277     gtk_dialog_add_button(GTK_DIALOG(dialog), _("Cancel"), GTK_RESPONSE_OK);
278
279     g_signal_connect(dialog, "response", G_CALLBACK(onLookupProgressDialogResponse), cancel);
280
281     /* add progress bar */
282     progress = gtk_progress_bar_new();
283     gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), progress);
284     g_object_set_data(G_OBJECT(dialog), "progress_bar", progress);
285
286     /* show dialog */
287     gtk_widget_show_all(dialog);
288
289     while (gtk_events_pending())
290         gtk_main_iteration();
291
292     return dialog;
293 }
294
295 void
296 MStarDict::DestroyLookupProgressDialog(GtkWidget *dialog)
297 {
298     gtk_widget_destroy(GTK_WIDGET(dialog));
299 }
300
301 void
302 MStarDict::CreateMainWindow()
303 {
304     HildonProgram *program = NULL;
305     GtkWidget *alignment, *main_vbox, *search;
306     GtkCellRenderer *renderer;
307     GtkTreeSelection *selection;
308
309     /* hildon program */
310     program = hildon_program_get_instance();
311     g_set_application_name(_("MStardict"));
312
313     /* main window */
314     window = hildon_stackable_window_new();
315     hildon_program_add_window(program, HILDON_WINDOW(window));
316
317     /* aligment */
318     alignment = gtk_alignment_new(0.5, 0.5, 1.0, 1.0);
319     gtk_alignment_set_padding(GTK_ALIGNMENT(alignment),
320                               HILDON_MARGIN_HALF, 0, HILDON_MARGIN_DEFAULT, HILDON_MARGIN_DEFAULT);
321     gtk_container_add(GTK_CONTAINER(window), alignment);
322
323     /* main vbox */
324     main_vbox = gtk_vbox_new(FALSE, 0);
325     gtk_container_add(GTK_CONTAINER(alignment), main_vbox);
326
327     /* no_search_result label */
328     label_widget = gtk_label_new(_("No search result"));
329     hildon_helper_set_logical_color(label_widget, GTK_RC_FG,
330                                     GTK_STATE_NORMAL, "SecondaryTextColor");
331     hildon_helper_set_logical_font(label_widget, "LargeSystemFont");
332     gtk_box_pack_start(GTK_BOX(main_vbox), label_widget, TRUE, TRUE, 0);
333
334     /* alignment for pannable area */
335     results_widget = gtk_alignment_new(0.5, 0.5, 1.0, 1.0);
336     gtk_alignment_set_padding(GTK_ALIGNMENT(results_widget),
337                               0, 0, HILDON_MARGIN_DEFAULT, HILDON_MARGIN_DEFAULT);
338     gtk_box_pack_start(GTK_BOX(main_vbox), results_widget, TRUE, TRUE, 0);
339
340     /* pannable for tree view */
341     results_view_scroll = hildon_pannable_area_new();
342     gtk_container_add(GTK_CONTAINER(results_widget), results_view_scroll);
343
344     /* result tree view */
345     results_view = hildon_gtk_tree_view_new(HILDON_UI_MODE_EDIT);
346     gtk_tree_view_set_model(GTK_TREE_VIEW(results_view), GTK_TREE_MODEL(results_list));
347     gtk_container_add(GTK_CONTAINER(results_view_scroll), results_view);
348
349     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(results_view));
350     g_signal_connect(selection, "changed", G_CALLBACK(onResultsViewSelectionChanged), this);
351
352     /* def column */
353     renderer = gtk_cell_renderer_text_new();
354     gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW
355                                                 (results_view), -1, "Def",
356                                                 renderer, "text", DEF_COLUMN, NULL);
357     g_object_set(G_OBJECT(renderer),
358                  "xpad", 10,
359                  "ellipsize", PANGO_ELLIPSIZE_END,
360                  "ellipsize-set", TRUE,
361                  NULL);
362
363     /* create search bar */
364     search = CreateSearchBar();
365     gtk_box_pack_end(GTK_BOX(main_vbox), search, FALSE, TRUE, 0);
366
367     /* window signals */
368     g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
369     g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK(onMainWindowKeyPressEvent), this);
370
371     /* show all widget instead of alignment */
372     gtk_widget_show_all(GTK_WIDGET(window));
373
374     /* grab focus to search entry */
375     GrabFocus();
376 }
377
378 GtkWidget *
379 MStarDict::CreateSearchBar()
380 {
381     GtkWidget *hbox, *entry, *button;
382     GtkEntryCompletion *completion;
383
384     /* search hbox */
385     hbox = gtk_hbox_new(FALSE, HILDON_MARGIN_DEFAULT);
386
387     /* search entry */
388     entry = hildon_entry_new(HILDON_SIZE_FINGER_HEIGHT);
389     hildon_gtk_entry_set_input_mode(GTK_ENTRY(entry), HILDON_GTK_INPUT_MODE_FULL);
390     gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
391
392     completion = gtk_entry_completion_new();
393     gtk_entry_completion_set_inline_completion(completion, TRUE);
394     gtk_entry_completion_set_popup_completion(completion, FALSE);
395     gtk_entry_set_completion(GTK_ENTRY(entry), completion);
396
397     /* clear button */
398     button = GTK_WIDGET(gtk_tool_button_new(gtk_image_new_from_icon_name("general_delete",
399                                                                          (GtkIconSize)HILDON_ICON_PIXEL_SIZE_FINGER),
400                                             "Clear"));
401     gtk_box_pack_end(GTK_BOX (hbox), button, FALSE, TRUE, 0);
402
403     /* search signals */
404     g_signal_connect(entry, "changed", G_CALLBACK(onSearchEntryChanged), this);
405     g_signal_connect(button, "clicked", G_CALLBACK(onSearchClearClicked), this);
406
407     search_entry = entry;
408     return hbox;
409 }
410
411
412 void
413 MStarDict::CreateMainMenu()
414 {
415     HildonAppMenu *menu;
416     GtkWidget *item;
417
418     menu = HILDON_APP_MENU(hildon_app_menu_new());
419     hildon_window_set_app_menu(HILDON_WINDOW(window), menu);
420
421     /* dictionaries menu item */
422     item = hildon_gtk_button_new(HILDON_SIZE_AUTO);
423     gtk_button_set_label(GTK_BUTTON(item), _("Dictionaries"));
424     hildon_app_menu_append(menu, GTK_BUTTON(item));
425     g_signal_connect(item, "clicked", G_CALLBACK(onDictionariesMenuItemClicked), this);
426
427     /* quit menu item */
428     item = hildon_gtk_button_new(HILDON_SIZE_AUTO);
429     gtk_button_set_label(GTK_BUTTON(item), _("Quit"));
430     hildon_app_menu_append(menu, GTK_BUTTON(item));
431     g_signal_connect(item, "clicked", G_CALLBACK(onQuitMenuItemClicked), this);
432
433     /* show main menu */
434     gtk_widget_show_all(GTK_WIDGET(menu));
435 }
436
437 void
438 MStarDict::SearchWord()
439 {
440     const gchar *sWord;
441     bool bFound = false;
442     std::string query;
443
444     if (oLibs->query_dictmask.empty())
445         return;
446
447     sWord = gtk_entry_get_text(GTK_ENTRY(search_entry));
448     if (strcmp(sWord, "") == 0) {
449         ShowNoResults(true);
450     } else {
451         /* unselect rows */
452         ResultsUnselectAll(GTK_SELECTION_NONE);
453
454         switch (analyse_query(sWord, query)) {
455         case qtDATA:
456             bFound = oLibs->LookupData(query.c_str());
457             break;
458         default:
459             /* nothing */ ;
460         }
461
462         /* unselect selected rows */
463         ResultsUnselectAll(GTK_SELECTION_SINGLE);
464
465         if (bFound)
466             ShowNoResults(false);
467         else
468             ShowNoResults(true);
469     }
470 }
471
472 void
473 MStarDict::ResultsListClear()
474 {
475     gtk_list_store_clear(results_list);
476 }
477
478 void
479 MStarDict::ResultsListInsertLast(const gchar *word)
480 {
481     GtkTreeIter iter;
482     gtk_list_store_append(results_list, &iter);
483     gtk_list_store_set(results_list, &iter, DEF_COLUMN, word, -1);
484 }
485
486 void
487 MStarDict::ResultsReScroll()
488 {
489     hildon_pannable_area_scroll_to(HILDON_PANNABLE_AREA(results_view_scroll), -1, 0);
490 }
491
492 void
493 MStarDict::ResultsUnselectAll(GtkSelectionMode mode)
494 {
495     GtkTreeSelection *selection;
496
497     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(results_view));
498     gtk_tree_selection_set_mode(selection, mode);
499     gtk_tree_selection_unselect_all(selection);
500 }
501
502 void
503 MStarDict::ShowNoResults(bool bNoResults)
504 {
505     if (bNoResults) {
506         gtk_label_set_text(GTK_LABEL(label_widget), _("No search result"));
507         gtk_widget_show(label_widget);
508         gtk_widget_hide(results_widget);
509     } else {
510         gtk_widget_hide(label_widget);
511         gtk_widget_show(results_widget);
512     }
513 }
514
515 void
516 MStarDict::ShowNoDictionary(bool bNoDictionary)
517 {
518     if (bNoDictionary) {
519         gtk_label_set_text(GTK_LABEL(label_widget), _("No loaded dictionary"));
520         gtk_widget_show(label_widget);
521         gtk_widget_hide(results_widget);
522     } else {
523         gtk_widget_hide(label_widget);
524         gtk_widget_show(results_widget);
525     }
526 }
527
528 void
529 MStarDict::ShowProgressIndicator(bool bShow)
530 {
531     if (bShow)
532         hildon_gtk_window_set_progress_indicator(GTK_WINDOW(window), 1);
533     else
534         hildon_gtk_window_set_progress_indicator(GTK_WINDOW(window), 0);
535 }
536
537 void
538 MStarDict::GrabFocus()
539 {
540     gtk_widget_grab_focus(GTK_WIDGET(search_entry));
541 }
542
543 int
544 main(int argc,
545      char **argv)
546 {
547     /* initialize hildon */
548     hildon_gtk_init(&argc, &argv);
549
550     /* initialize localization */
551     setlocale(LC_ALL, "");
552     bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
553     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
554     textdomain(GETTEXT_PACKAGE);
555
556     /* create main window */
557     MStarDict mStarDict;
558     pMStarDict = &mStarDict;
559     mStarDict.CreateMainWindow();
560     mStarDict.CreateMainMenu();
561     mStarDict.ShowNoResults(true);
562
563     /* load dictionaries */
564     mStarDict.oDict->LoadDictionaries();
565
566     gtk_main();
567     return 0;
568 }