7 #include <gdk/gdkkeysyms.h>
10 #define _(String) dgettext (PACKAGE, String)
12 #define CURRENT_BINDINGS \
18 (current_lang, current_name, current_command))))))
20 #define CURRENT_STATUS \
26 (current_lang, current_name, current_command))))))
28 #define CONFIG_COMMAND(plist) \
29 minput_config_command (current_lang, current_name, current_command, \
33 mtext__cat_str (MText *mt, char *str)
37 mtext_cat_char (mt, (int) *str);
42 static unsigned modifier_state = 0;
43 static MPlist *entry_keyseq;
44 static MSymbol current_lang, current_name, current_command;
60 ALT_MASK_BIT = META_MASK_BIT << 1,
61 SUPER_MASK_BIT = ALT_MASK_BIT << 1,
62 HYPER_MASK_BIT = SUPER_MASK_BIT << 1
66 keyseq_equal (MPlist *pl1, MPlist *pl2)
68 if (mplist_length (pl1) != mplist_length (pl2))
70 while (pl1 && mplist_key (pl1) == Msymbol)
72 if (mplist_value (pl1) != mplist_value (pl2))
74 pl1 = mplist_next (pl1);
75 pl2 = mplist_next (pl2);
81 update_entry (GtkEntry *entry)
83 if (mplist_key (entry_keyseq) == Mnil)
84 gtk_entry_set_text (entry, "");
90 name = msymbol_name ((MSymbol) mplist_value (entry_keyseq));
91 gtk_entry_set_text (entry, name);
92 for (p = mplist_next (entry_keyseq); mplist_key (p) != Mnil;
95 name = msymbol_name ((MSymbol) mplist_value (p));
96 gtk_entry_append_text (entry, " ");
97 gtk_entry_append_text (entry, name);
99 gtk_editable_set_position (GTK_EDITABLE (entry), -1);
104 update_binding_store (GtkWidget *view)
110 store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (view)));
111 gtk_list_store_clear (store);
113 for (pl = CURRENT_BINDINGS;
114 pl && mplist_key (pl) == Mplist;
115 pl = mplist_next (pl))
117 gtk_list_store_append (store, &iter);
118 gtk_list_store_set (store, &iter, 0, mplist_value (pl), -1);
123 key_pressed_cb (GtkEntry *entry, GdkEventKey *event, gpointer data)
130 struct BindingWidgets *bw = data;
132 c = gdk_keyval_to_unicode (event->keyval);
135 switch (event->keyval)
137 case GDK_Meta_L: case GDK_Meta_R:
138 modifier_state |= META_MASK_BIT; return TRUE;
139 case GDK_Alt_L: case GDK_Alt_R:
140 modifier_state |= ALT_MASK_BIT; return TRUE;
141 case GDK_Super_L: case GDK_Super_R:
142 modifier_state |= SUPER_MASK_BIT; return TRUE;
143 case GDK_Hyper_L: case GDK_Hyper_R:
144 modifier_state |= HYPER_MASK_BIT; return TRUE;
146 if (event->keyval >= GDK_Shift_L && event->keyval <= GDK_Shift_Lock)
149 name = gdk_keyval_name (event->keyval);
152 nbytes = strlen (name);
158 mtext_cat_char (mt, c);
159 nbytes = mconv_encode_buffer (msymbol ("utf-8"), mt,
160 (unsigned char *) name, 32);
161 m17n_object_unref (mt);
164 if (c == 0 && event->state & GDK_SHIFT_MASK)
165 buf[i++] = 'S', buf[i++] = '-';
166 if (event->state & GDK_CONTROL_MASK)
167 buf[i++] = 'C', buf[i++] = '-';
168 if (modifier_state & META_MASK_BIT)
169 buf[i++] = 'M', buf[i++] = '-';
170 if (modifier_state & ALT_MASK_BIT)
171 buf[i++] = 'A', buf[i++] = '-';
172 if (modifier_state & SUPER_MASK_BIT)
173 buf[i++] = 's', buf[i++] = '-';
174 if (modifier_state & HYPER_MASK_BIT)
175 buf[i++] = 'H', buf[i++] = '-';
176 strncpy (buf + i, name, nbytes);
178 mplist_add (entry_keyseq, Msymbol, msymbol (buf));
179 update_entry (entry);
180 gtk_widget_set_sensitive (bw->clear, TRUE);
181 gtk_widget_set_sensitive (bw->add, TRUE);
186 key_released_cb (GtkEntry *entry, GdkEventKey *event, gpointer data)
190 c = gdk_keyval_to_unicode (event->keyval);
193 switch (event->keyval)
195 case GDK_Meta_L: case GDK_Meta_R:
196 modifier_state &= ~META_MASK_BIT; break;
197 case GDK_Alt_L: case GDK_Alt_R:
198 modifier_state &= ~ALT_MASK_BIT; break;
199 case GDK_Super_L: case GDK_Super_R:
200 modifier_state &= ~SUPER_MASK_BIT; break;
201 case GDK_Hyper_L: case GDK_Hyper_R:
202 modifier_state &= ~HYPER_MASK_BIT; break;
209 update_widgets (struct BindingWidgets *bw)
211 MSymbol status = CURRENT_STATUS;
213 if (status == Mconfigured)
215 gtk_widget_set_sensitive (bw->default_, TRUE);
216 gtk_widget_set_sensitive (bw->revert, TRUE);
217 gtk_label_set_text (GTK_LABEL (bw->status), _("Status : modified"));
219 else if (status == Mcustomized)
221 gtk_widget_set_sensitive (bw->default_, TRUE);
222 gtk_widget_set_sensitive (bw->revert, FALSE);
223 gtk_label_set_text (GTK_LABEL (bw->status), _("Status : customized"));
227 gtk_widget_set_sensitive (bw->default_, FALSE);
228 gtk_widget_set_sensitive (bw->revert, FALSE);
229 gtk_label_set_text (GTK_LABEL (bw->status), _("Status : default"));
234 clear_cb (GtkButton *button, gpointer data)
236 struct BindingWidgets *bw = data;
238 mplist_set (entry_keyseq, Mnil, NULL);
239 gtk_widget_grab_focus (bw->entry);
240 update_entry (GTK_ENTRY (bw->entry));
241 gtk_widget_set_sensitive (bw->clear, FALSE);
242 gtk_widget_set_sensitive (bw->add, FALSE);
246 add_cb (GtkButton *button, gpointer data)
248 MPlist *new, *pl, *last;
251 struct BindingWidgets *bw = data;
253 if (mplist_length (entry_keyseq) == 0)
256 for (pl = CURRENT_BINDINGS;
257 pl && mplist_key (pl) == Mplist;
258 pl = mplist_next (pl))
260 if (! keyseq_equal (mplist_value (pl), entry_keyseq))
261 mplist_add (new, Mplist, mplist_value (pl));
264 /* entry_keyseq is already registered. */
265 m17n_object_unref (new);
269 mplist_add (new, Mplist, entry_keyseq);
270 CONFIG_COMMAND (new);
271 m17n_object_unref (new);
273 /* We cannot use ENTRY_KEYSEQ for gtk_list_store_set (). We must
274 use a pointer to the internally copied one. */
275 new = CURRENT_BINDINGS;
277 pl && mplist_key (pl) == Mplist;
278 last = pl, pl = mplist_next (pl));
279 store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (bw->view)));
280 gtk_list_store_append (store, &iter);
281 gtk_list_store_set (store, &iter, 0, mplist_value (last), -1);
282 update_binding_store (bw->view);
288 create_adding_section (struct BindingWidgets *bw)
290 GtkWidget *label, *hbox, *vbox;
292 label = gtk_label_new (_("New key binding:"));
294 entry_keyseq = mplist ();
295 bw->entry = gtk_entry_new ();
296 g_signal_connect (G_OBJECT (bw->entry), "key-press-event",
297 G_CALLBACK (key_pressed_cb), bw);
298 g_signal_connect (G_OBJECT (bw->entry), "key-release-event",
299 G_CALLBACK (key_released_cb), bw);
301 bw->clear = gtk_button_new_from_stock (GTK_STOCK_CLEAR);
302 gtk_widget_set_sensitive (bw->clear, FALSE);
303 g_signal_connect (G_OBJECT (bw->clear), "clicked",
304 G_CALLBACK (clear_cb), bw);
306 bw->add = gtk_button_new_from_stock (GTK_STOCK_ADD);
307 gtk_widget_set_sensitive (bw->add, FALSE);
308 g_signal_connect (G_OBJECT (bw->add), "clicked",
309 G_CALLBACK (add_cb), bw);
311 vbox = gtk_vbox_new (FALSE, 6);
312 gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
314 hbox = gtk_hbox_new (FALSE, 6);
315 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 6);
316 gtk_container_add (GTK_CONTAINER (vbox), hbox);
318 gtk_container_add (GTK_CONTAINER (vbox), bw->entry);
320 hbox = gtk_hbutton_box_new ();
321 gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_END);
322 gtk_box_set_spacing (GTK_BOX (hbox), 6);
323 gtk_container_add (GTK_CONTAINER (hbox), bw->clear);
324 gtk_container_add (GTK_CONTAINER (hbox), bw->add);
325 gtk_container_add (GTK_CONTAINER (vbox), hbox);
331 selection_cb (GtkTreeSelection *selection, gpointer data)
333 struct BindingWidgets *bw = data;
335 gtk_widget_set_sensitive
337 gtk_tree_selection_count_selected_rows (selection) ? TRUE : FALSE);
341 keyseq_render_function (GtkTreeViewColumn *column,
342 GtkCellRenderer *renderer,
349 MText *mt = mtext ();
351 gtk_tree_model_get (model, iter, 0, &keyseq, -1);
353 pl && mplist_key (pl) == Msymbol;
354 pl = mplist_next (pl))
356 mtext__cat_str (mt, msymbol_name ((MSymbol) mplist_value (pl)));
357 mtext_cat_char (mt, ' ');
359 g_object_set (renderer, "text", mtext_data (mt, NULL, NULL, NULL, NULL),
361 m17n_object_unref (mt);
365 default_cb (GtkButton *button, gpointer data)
367 MPlist *empty = mplist ();
368 struct BindingWidgets *bw = data;
370 CONFIG_COMMAND (empty);
371 m17n_object_unref (empty);
372 update_binding_store (bw->view);
377 revert_cb (GtkButton *button, gpointer data)
379 struct BindingWidgets *bw = data;
381 CONFIG_COMMAND (NULL);
382 update_binding_store (bw->view);
387 delete_cb (GtkButton *button, gpointer data)
389 GtkTreeSelection *selection;
392 MPlist *keyseq, *new, *pl;
393 struct BindingWidgets *bw = data;
395 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (bw->view));
396 if (! gtk_tree_selection_get_selected (selection, &model, &iter))
398 gtk_tree_model_get (model, &iter, 0, &keyseq, -1);
400 for (pl = CURRENT_BINDINGS;
401 pl && mplist_key (pl) == Mplist;
402 pl = mplist_next (pl))
403 if (! keyseq_equal (mplist_value (pl), keyseq))
404 mplist_add (new, Mplist, mplist_value (pl));
405 CONFIG_COMMAND (new);
406 m17n_object_unref (new);
407 gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
408 update_binding_store (bw->view);
413 create_deleting_section (struct BindingWidgets *bw)
416 GtkWidget *label, *scrolled, *hbox, *vbox;
417 GtkTreeViewColumn *column;
418 GtkCellRenderer *renderer;
420 GtkTreeSelection *selection;
423 label = gtk_label_new (_("Current key bindings:"));
425 store = gtk_list_store_new (1, G_TYPE_POINTER);
426 bw->view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
427 g_object_unref (G_OBJECT (store));
428 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (bw->view), FALSE);
429 update_binding_store (bw->view);
430 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(bw->view));
431 gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
432 g_signal_connect (G_OBJECT (selection), "changed",
433 G_CALLBACK (selection_cb), bw);
435 scrolled = gtk_scrolled_window_new (NULL, NULL);
436 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
437 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
438 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled),
441 column = gtk_tree_view_column_new ();
442 gtk_tree_view_append_column (GTK_TREE_VIEW (bw->view), column);
444 renderer = gtk_cell_renderer_text_new ();
445 gtk_tree_view_column_pack_start (column, renderer, TRUE);
446 gtk_tree_view_column_set_cell_data_func
447 (column, renderer, keyseq_render_function, NULL, NULL);
449 bw->default_ = gtk_button_new_from_stock (_("_Default"));
450 g_signal_connect (G_OBJECT (bw->default_), "clicked",
451 G_CALLBACK (default_cb), bw);
453 bw->revert = gtk_button_new_from_stock (GTK_STOCK_REVERT_TO_SAVED);
454 g_signal_connect (G_OBJECT (bw->revert), "clicked",
455 G_CALLBACK (revert_cb), bw);
457 bw->delete = gtk_button_new_from_stock (GTK_STOCK_DELETE);
458 gtk_widget_set_sensitive (bw->delete, FALSE);
459 g_signal_connect (G_OBJECT (bw->delete), "clicked",
460 G_CALLBACK (delete_cb), bw);
462 vbox = gtk_vbox_new (FALSE, 6);
463 gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
465 hbox = gtk_hbox_new (FALSE, 6);
466 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 6);
467 gtk_container_add (GTK_CONTAINER (vbox), hbox);
469 gtk_container_add (GTK_CONTAINER (vbox), scrolled);
471 hbox = gtk_hbutton_box_new ();
472 gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_END);
473 gtk_box_set_spacing (GTK_BOX (hbox), 6);
474 gtk_container_add (GTK_CONTAINER (hbox), bw->default_);
475 gtk_container_add (GTK_CONTAINER (hbox), bw->revert);
476 gtk_container_add (GTK_CONTAINER (hbox), bw->delete);
477 gtk_container_add (GTK_CONTAINER (vbox), hbox);
483 set_status (GtkListStore *store, GtkTreeIter *iter)
488 status = CURRENT_STATUS;
489 if (status == Mconfigured)
490 status_str = _("modified");
491 else if (status == Mcustomized)
492 status_str = _("customized");
494 status_str = _("default");
495 gtk_list_store_set (store, iter, 1, status_str, -1);
499 activated_cb (GtkTreeView *parent, GtkTreePath *path,
500 GtkTreeViewColumn *col, gpointer data)
504 GtkWidget *dialog, *vbox, *hbox;
505 struct BindingWidgets bw;
508 model = gtk_tree_view_get_model (parent);
509 if (! gtk_tree_model_get_iter (model, &iter, path))
511 gtk_tree_model_get (model, &iter, 0, &command, -1);
512 current_command = msymbol (command);
514 dialog = (gtk_dialog_new_with_buttons
516 GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (parent))),
517 GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
518 GTK_STOCK_OK, GTK_RESPONSE_OK,
521 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox),
522 create_deleting_section (&bw));
524 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox),
525 create_adding_section (&bw));
527 vbox = gtk_vbox_new (FALSE, 6);
528 gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
529 hbox = gtk_hbox_new (FALSE, 6);
530 bw.status = gtk_label_new (NULL);
531 gtk_box_pack_start (GTK_BOX (hbox), bw.status, FALSE, FALSE, 6);
532 gtk_container_add (GTK_CONTAINER (vbox), hbox);
533 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), vbox);
535 update_widgets (&bw);
536 gtk_widget_show_all (dialog);
537 gtk_dialog_run (GTK_DIALOG (dialog));
538 gtk_tree_model_get_iter (model, &iter, path);
539 set_status (GTK_LIST_STORE (model), &iter);
540 gtk_widget_destroy (dialog);
541 m17n_object_unref (entry_keyseq);
545 create_command_list (GtkTooltips *tip, MSymbol lang, MSymbol name)
549 GtkCellRenderer *renderer;
555 plist = minput_get_command (lang, name, Mnil);
557 * plist == ((command description status keyseq keyseq ...)
558 * (command description status keyseq keyseq ...)
562 return gtk_label_new (_("No customizable commands."));
564 store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
565 for (; plist && mplist_key (plist) == Mplist; plist = mplist_next (plist))
569 MSymbol command, status;
573 pl = mplist_value (plist);
574 /* pl == (command description status keyseq keyseq ...) */
575 current_command = command = mplist_value (pl);
577 pl = mplist_next (pl);
578 /* pl == (description status keyseq keyseq ...) */
579 if (mplist_key (pl) == Mtext)
580 /* Fixme : Assuming the return value is in UTF-8 */
581 desc = mtext_data (mplist_value (pl), NULL, NULL, NULL, NULL);
585 pl = mplist_next (pl);
586 /* pl == (status keyseq keyseq ...) */
587 status = mplist_value (pl);
588 gtk_list_store_append (store, &iter);
589 gtk_list_store_set (store, &iter,
590 0, msymbol_name (command),
592 set_status (store, &iter);
594 view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
595 g_object_unref (G_OBJECT (store));
596 renderer = gtk_cell_renderer_text_new ();
597 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
603 renderer = gtk_cell_renderer_text_new ();
604 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
610 g_signal_connect (G_OBJECT (view), "row-activated",
611 G_CALLBACK (activated_cb), NULL);