Tizen 2.1 base
[framework/uifw/ise-engine-anthy.git] / src / scim_anthy_table_editor.cpp
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  *  Copyright (C) 2004-2005 Takuro Ashie
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21   #include <config.h>
22 #endif
23
24 #include "scim_anthy_table_editor.h"
25
26 #include <string.h>
27 #include <ctype.h>
28 #include "scim_anthy_intl.h"
29
30 enum {
31     ADD_ENTRY_SIGNAL,
32     REMOVE_ENTRY_SIGNAL,
33     LAST_SIGNAL,
34 };
35
36 static void scim_anthy_table_editor_class_init   (ScimAnthyTableEditorClass *klass);
37 static void scim_anthy_table_editor_init         (ScimAnthyTableEditor      *object);
38 static void scim_anthy_table_editor_dispose      (GObject                   *object);
39
40 static void scim_anthy_table_editor_add_entry    (ScimAnthyTableEditor *editor);
41 static void scim_anthy_table_editor_remove_entry (ScimAnthyTableEditor *editor);
42
43 static gint compare_string                  (GtkTreeModel     *model,
44                                              GtkTreeIter      *a,
45                                              GtkTreeIter      *b,
46                                              gpointer          user_data);
47
48 static void on_table_view_selection_changed (GtkTreeSelection *selection,
49                                              gpointer          data);
50 static void on_add_button_clicked           (GtkButton        *button,
51                                              gpointer          data);
52 static void on_remove_button_clicked        (GtkButton        *button,
53                                              gpointer          data);
54 static void on_entry_activate               (GtkEntry         *entry,
55                                              gpointer          data);
56 static void on_entry_changed                (GtkEditable      *editable,
57                                              gpointer          data);
58 static void on_sequence_entry_insert_text   (GtkEditable      *editable,
59                                              const gchar      *text,
60                                              gint              length,
61                                              gint             *position,
62                                              gpointer          data);
63
64 static guint editor_signals[LAST_SIGNAL] = { 0 };
65 static GtkDialogClass *parent_class = NULL;
66
67 GType
68 scim_anthy_table_editor_get_type (void)
69 {
70     static GType type = 0;
71
72     if (!type) {
73         static const GTypeInfo info = {
74             sizeof (ScimAnthyTableEditorClass),
75             NULL,           /* base_init */
76             NULL,           /* base_finalize */
77             (GClassInitFunc) scim_anthy_table_editor_class_init,
78             NULL,           /* class_finalize */
79             NULL,           /* class_data */
80             sizeof (ScimAnthyTableEditor),
81             0,              /* n_preallocs */
82             (GInstanceInitFunc) scim_anthy_table_editor_init,
83         };
84
85         type = g_type_register_static (GTK_TYPE_DIALOG,
86                                        "ScimAnthyTableEditor",
87                                        &info, (GTypeFlags) 0);
88     }
89
90     return type;
91 }
92
93 static void
94 scim_anthy_table_editor_class_init (ScimAnthyTableEditorClass *klass)
95 {
96     GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
97     //GtkWidgetClass *widget_class     = GTK_WIDGET_CLASS (klass);
98
99     parent_class = (GtkDialogClass *) g_type_class_peek_parent (klass);
100
101     editor_signals[ADD_ENTRY_SIGNAL] =
102       g_signal_new ("add-entry",
103                   G_TYPE_FROM_CLASS (klass),
104                   G_SIGNAL_RUN_LAST,
105                   G_STRUCT_OFFSET (ScimAnthyTableEditorClass, add_entry),
106                   NULL, NULL,
107                   g_cclosure_marshal_VOID__VOID,
108                   G_TYPE_NONE, 0);
109     editor_signals[REMOVE_ENTRY_SIGNAL] =
110       g_signal_new ("remove-entry",
111                   G_TYPE_FROM_CLASS (klass),
112                   G_SIGNAL_RUN_LAST,
113                   G_STRUCT_OFFSET (ScimAnthyTableEditorClass, remove_entry),
114                   NULL, NULL,
115                   g_cclosure_marshal_VOID__VOID,
116                   G_TYPE_NONE, 0);
117
118     gobject_class->dispose = scim_anthy_table_editor_dispose;
119     klass->add_entry       = scim_anthy_table_editor_add_entry;
120     klass->remove_entry    = scim_anthy_table_editor_remove_entry;
121 }
122
123 static void
124 scim_anthy_table_editor_init (ScimAnthyTableEditor *editor)
125 {
126     gtk_dialog_add_buttons (GTK_DIALOG (editor),
127                             GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
128                             NULL);
129
130     gtk_window_set_default_size (GTK_WINDOW (editor), 350, 250);
131     gtk_window_set_position (GTK_WINDOW (editor),
132                              GTK_WIN_POS_CENTER_ON_PARENT);
133
134     // edit area
135     GtkWidget *hbox = gtk_hbox_new (FALSE, 0);
136     gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
137     gtk_box_pack_start (GTK_BOX (GTK_DIALOG (editor)->vbox), hbox,
138                         TRUE, TRUE, 0);
139     gtk_widget_show (hbox);
140
141     // tree view area
142     GtkWidget *scrwin = gtk_scrolled_window_new (NULL, NULL);
143     gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrwin),
144                                          GTK_SHADOW_IN);
145     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrwin),
146                                     GTK_POLICY_AUTOMATIC,
147                                     GTK_POLICY_AUTOMATIC);
148     gtk_box_pack_start (GTK_BOX (hbox), scrwin, TRUE, TRUE, 0);
149     gtk_widget_show (scrwin);
150
151     GtkWidget *treeview = gtk_tree_view_new ();
152     editor->treeview = treeview;
153     gtk_container_add (GTK_CONTAINER (scrwin), treeview);
154     gtk_widget_show (treeview);
155
156     GtkTreeSelection *selection;
157     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
158     g_signal_connect (G_OBJECT (selection), "changed",
159                       G_CALLBACK (on_table_view_selection_changed), editor);
160
161     gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (treeview), TRUE);
162     gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW (treeview), TRUE);
163
164     // button area
165     GtkWidget *vbox = gtk_vbox_new (FALSE, 0);
166     editor->button_area = vbox;
167     gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 5);
168     gtk_widget_show (vbox);
169
170     editor->entries = NULL;
171 }
172
173 static void
174 scim_anthy_table_editor_dispose (GObject *object)
175 {
176     ScimAnthyTableEditor *editor = SCIM_ANTHY_TABLE_EDITOR (object);
177
178     if (editor->entries) {
179         g_list_free (editor->entries);
180         editor->entries = NULL;
181     }
182
183         if (G_OBJECT_CLASS(parent_class)->dispose)
184                 G_OBJECT_CLASS(parent_class)->dispose(object);
185 }
186
187 GtkWidget *
188 scim_anthy_table_editor_new (void)
189 {
190     return GTK_WIDGET(g_object_new (SCIM_ANTHY_TYPE_TABLE_EDITOR,
191                                     NULL));
192 }
193
194 void
195 scim_anthy_table_editor_set_columns (ScimAnthyTableEditor *editor,
196                                      const char          **titles)
197 {
198     g_return_if_fail (SCIM_ANTHY_IS_TABLE_EDITOR (editor));
199
200     if (!titles)
201         return;
202
203     gint n_cols;
204     for (n_cols = 0; titles[n_cols]; n_cols++);
205     if (n_cols <= 0)
206         return;
207
208     GType types[n_cols];
209     for (gint i = 0; i < n_cols; i++)
210         types[i] = G_TYPE_STRING;
211
212     GtkListStore *store = gtk_list_store_newv (n_cols, types);
213     gtk_tree_view_set_model (GTK_TREE_VIEW (editor->treeview),
214                              GTK_TREE_MODEL (store));
215
216     // columns
217     for (int i = 0; i < n_cols; i++) {
218         GtkCellRenderer *cell;
219         GtkTreeViewColumn *column;
220         cell = gtk_cell_renderer_text_new ();
221         column = gtk_tree_view_column_new_with_attributes (titles[i], cell,
222                                                            "text", i,
223                                                            NULL);
224         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
225         gtk_tree_view_column_set_fixed_width (column, 80);
226         gtk_tree_view_column_set_resizable(column, TRUE);
227         gtk_tree_view_append_column(GTK_TREE_VIEW(editor->treeview), column);
228
229         gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store), i,
230                                          compare_string,
231                                          GINT_TO_POINTER (i), NULL);
232         gtk_tree_view_column_set_sort_column_id (column, i);
233     }
234
235     // entries
236     for (int i = 0; i < n_cols; i++) {
237         GtkWidget *label = gtk_label_new_with_mnemonic (titles[i]);
238         gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
239         gtk_box_pack_start (GTK_BOX (editor->button_area), label, FALSE, FALSE, 2);
240         gtk_widget_show (label);
241
242         GtkWidget *entry = gtk_entry_new ();
243         gtk_box_pack_start (GTK_BOX (editor->button_area), entry,
244                             FALSE, FALSE, 2);
245         gtk_widget_set_size_request (entry, 80, -1);
246         g_signal_connect (G_OBJECT (entry), "activate",
247                           G_CALLBACK (on_entry_activate), editor);
248         g_signal_connect (G_OBJECT (entry), "changed",
249                           G_CALLBACK (on_entry_changed), editor);
250         if (i == 0)
251             g_signal_connect (G_OBJECT (entry), "insert-text",
252                               G_CALLBACK (on_sequence_entry_insert_text),
253                               editor);
254         gtk_widget_show (entry);
255         gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
256
257         editor->entries = g_list_append (editor->entries, entry);
258     }
259
260     // buttons
261     GtkWidget *button = gtk_button_new_from_stock (GTK_STOCK_ADD);
262     editor->add_button = button;
263     gtk_box_pack_start (GTK_BOX (editor->button_area), button, FALSE, FALSE, 5);
264     g_signal_connect (G_OBJECT (button), "clicked",
265                       G_CALLBACK (on_add_button_clicked), editor);
266     gtk_widget_set_sensitive (button, FALSE);
267     gtk_widget_show (button);
268
269     button = gtk_button_new_from_stock (GTK_STOCK_REMOVE);
270     editor->remove_button = button;
271     gtk_box_pack_start (GTK_BOX (editor->button_area), button, FALSE, FALSE, 5);
272     g_signal_connect (G_OBJECT (button), "clicked",
273                       G_CALLBACK (on_remove_button_clicked), editor);
274     gtk_widget_set_sensitive (button, FALSE);
275     gtk_widget_show (button);
276
277     // clean
278     g_object_unref (store);
279 }
280
281 const char *
282 scim_anthy_table_editor_get_nth_text (ScimAnthyTableEditor *editor, guint nth)
283 {
284     g_return_val_if_fail (SCIM_ANTHY_IS_TABLE_EDITOR (editor), "");
285
286     GtkEntry *entry = GTK_ENTRY (g_list_nth_data (editor->entries, nth));
287     if (!entry)
288         return "";
289
290     return gtk_entry_get_text (entry);
291 }
292
293 static void
294 scim_anthy_table_editor_add_entry (ScimAnthyTableEditor *editor)
295 {
296     GtkTreeView *treeview = GTK_TREE_VIEW (editor->treeview);
297     GtkTreeModel *model = gtk_tree_view_get_model (treeview);
298     GtkTreeIter iter;
299  
300     gboolean go_next;
301     bool found = false;
302
303     const gchar *sequence;
304     sequence = scim_anthy_table_editor_get_nth_text (editor, 0);
305
306     if (!sequence)
307         return;
308
309     for (go_next = gtk_tree_model_get_iter_first (model, &iter);
310          go_next;
311          go_next = gtk_tree_model_iter_next (model, &iter))
312     {
313         gchar *seq = NULL;
314         gtk_tree_model_get (model, &iter,
315                             0, &seq,
316                             -1);
317         if (seq && !strcmp (sequence, seq)) {
318             found = true;
319             g_free (seq);
320             break;
321         }
322         g_free (seq);
323     }
324
325     if (!found)
326         gtk_list_store_append (GTK_LIST_STORE (model), &iter);
327
328     GList *node;
329     gint i;
330     for (i = 0, node = editor->entries;
331          node;
332          i++, node = g_list_next (node))
333     {
334         const char *text = gtk_entry_get_text (GTK_ENTRY (node->data));
335         gtk_list_store_set (GTK_LIST_STORE (model), &iter,
336                             i, text,
337                             -1);
338     }
339
340     GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
341     gtk_tree_view_set_cursor (treeview, path, NULL, FALSE);
342     gtk_tree_path_free (path);
343 }
344
345 static void
346 scim_anthy_table_editor_remove_entry (ScimAnthyTableEditor *editor)
347 {
348     GtkTreeView *treeview = GTK_TREE_VIEW (editor->treeview);
349     GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
350     GtkTreeModel *model = NULL;
351     GtkTreeIter iter, next;
352     gboolean selected, success;
353
354     selected = gtk_tree_selection_get_selected (selection, &model, &iter);
355     if (!selected)
356         return;
357
358     gchar *sequence = NULL;
359     gtk_tree_model_get (model, &iter,
360                         0, &sequence,
361                         -1);
362
363     next = iter;
364     success = gtk_tree_model_iter_next (model, &next);
365     GtkTreePath *path = NULL;
366     if (success) {
367         path = gtk_tree_model_get_path (model, &next);
368     } else {
369         path = gtk_tree_model_get_path (model, &iter);
370         if (path)
371             success = gtk_tree_path_prev (path);
372     }
373
374     if (success && path)
375         gtk_tree_view_set_cursor (GTK_TREE_VIEW (treeview), path, NULL, FALSE);
376     if (path)
377         gtk_tree_path_free (path);
378
379     gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
380
381     g_free (sequence);
382 }
383
384 static gint
385 compare_string (GtkTreeModel *model,
386                 GtkTreeIter *a,
387                 GtkTreeIter *b,
388                 gpointer user_data)
389 {
390     gint n_cols, column, cur_column = GPOINTER_TO_INT (user_data);
391     gint ret = 0;
392
393     n_cols = gtk_tree_model_get_n_columns (model);
394
395     if (cur_column < n_cols) {
396         gchar *seq1 = NULL, *seq2 = NULL;
397         gtk_tree_model_get (model, a,
398                             cur_column, &seq1,
399                             -1);
400         gtk_tree_model_get (model, b,
401                             cur_column, &seq2,
402                             -1);
403         if (!seq1 && seq2) {
404             ret = -1;
405         } else if (seq1 && !seq2) {
406             ret = 1;
407         } else if (seq1 && seq2) {
408             ret = strcmp (seq1, seq2);
409         } else {
410             ret = 0;
411         }
412         g_free (seq1);
413         g_free (seq2);
414     }
415
416     for (column = 0; ret == 0 && column < n_cols; column++) {
417         gchar *seq1 = NULL, *seq2 = NULL;
418
419         if (cur_column == column)
420             continue;
421
422         gtk_tree_model_get (model, a,
423                             column, &seq1,
424                             -1);
425         gtk_tree_model_get (model, b,
426                             column, &seq2,
427                             -1);
428         if (!seq1 && seq2) {
429             ret = -1;
430         } else if (seq1 && !seq2) {
431             ret = 1;
432         } else if (!seq1 && !seq2) {
433             ret = strcmp (seq1, seq2);
434         } else {
435             ret = 0;
436         }
437         g_free (seq1);
438         g_free (seq2);
439     }
440
441     return ret;
442 }
443
444 static void
445 on_table_view_selection_changed (GtkTreeSelection *selection, gpointer data)
446 {
447     ScimAnthyTableEditor *editor = SCIM_ANTHY_TABLE_EDITOR (data);
448     GtkTreeModel *model = NULL;
449     GtkTreeIter iter;
450
451     gboolean selected;
452
453     selected = gtk_tree_selection_get_selected (selection, &model, &iter);
454
455     if (editor->remove_button) {
456         if (selected) {
457             gtk_widget_set_sensitive (editor->remove_button, true);
458         } else {
459             gtk_widget_set_sensitive (editor->remove_button, false);
460         }
461     }
462
463     GList *node;
464
465     if (selected) {
466         gint i;
467         for (i = 0, node = editor->entries;
468              node;
469              i++, node = g_list_next (node))
470         {
471             gchar *str = NULL;
472             gtk_tree_model_get (model, &iter,
473                                 i, &str,
474                                 -1);
475             gtk_entry_set_text (GTK_ENTRY (node->data), str);
476             g_free (str);
477         }
478     } else {
479         for (node = editor->entries; node; node = g_list_next (node))
480             gtk_entry_set_text (GTK_ENTRY (node->data), "");
481     }
482 }
483
484 static void
485 on_add_button_clicked (GtkButton *button, gpointer data)
486 {
487     ScimAnthyTableEditor *editor = SCIM_ANTHY_TABLE_EDITOR (data);
488     g_signal_emit (editor, editor_signals[ADD_ENTRY_SIGNAL], 0);
489 }
490
491 static void
492 on_remove_button_clicked (GtkButton *button, gpointer data)
493 {
494     ScimAnthyTableEditor *editor = SCIM_ANTHY_TABLE_EDITOR (data);
495     g_signal_emit (editor, editor_signals[REMOVE_ENTRY_SIGNAL], 0);
496 }
497
498 static void
499 on_entry_activate (GtkEntry *entry, gpointer data)
500 {
501     ScimAnthyTableEditor *editor = SCIM_ANTHY_TABLE_EDITOR (data);
502     g_signal_emit (editor, editor_signals[ADD_ENTRY_SIGNAL], 0);
503 }
504
505 static void
506 on_entry_changed (GtkEditable *editable, gpointer data)
507 {
508     ScimAnthyTableEditor *editor = SCIM_ANTHY_TABLE_EDITOR (data);
509     const char *seq;
510
511     if (!editor->entries || !editor->entries->data)
512         return;
513
514     seq = gtk_entry_get_text (GTK_ENTRY (editor->entries->data));
515     gtk_widget_set_sensitive (editor->add_button,
516                               seq && *seq);
517 }
518
519 static void
520 on_sequence_entry_insert_text (GtkEditable *editable,
521                                const gchar *text,
522                                gint length,
523                                gint *position,
524                                gpointer data)
525 {
526     for (int i = 0; i < length; i++) {
527        if (!isascii (text[i]) || isspace (text[i])) {
528             g_signal_stop_emission_by_name (editable, "insert_text");
529             return;
530         }
531     }
532 }