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, \
32 static unsigned modifier_state = 0;
33 static MPlist *entry_keyseq;
34 static MSymbol current_lang, current_name, current_command;
50 ALT_MASK_BIT = META_MASK_BIT << 1,
51 SUPER_MASK_BIT = ALT_MASK_BIT << 1,
52 HYPER_MASK_BIT = SUPER_MASK_BIT << 1
56 keyseq_equal (MPlist *pl1, MPlist *pl2)
58 if (mplist_length (pl1) != mplist_length (pl2))
60 while (pl1 && mplist_key (pl1) == Msymbol)
62 if (mplist_value (pl1) != mplist_value (pl2))
64 pl1 = mplist_next (pl1);
65 pl2 = mplist_next (pl2);
71 update_entry (GtkEntry *entry)
73 if (mplist_key (entry_keyseq) == Mnil)
74 gtk_entry_set_text (entry, "");
80 name = msymbol_name ((MSymbol) mplist_value (entry_keyseq));
81 gtk_entry_set_text (entry, name);
82 for (p = mplist_next (entry_keyseq); mplist_key (p) != Mnil;
85 name = msymbol_name ((MSymbol) mplist_value (p));
86 gtk_entry_append_text (entry, " ");
87 gtk_entry_append_text (entry, name);
89 gtk_editable_set_position (GTK_EDITABLE (entry), -1);
94 update_binding_store (GtkWidget *view)
100 store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (view)));
101 gtk_list_store_clear (store);
103 for (pl = CURRENT_BINDINGS;
104 pl && mplist_key (pl) == Mplist;
105 pl = mplist_next (pl))
107 gtk_list_store_append (store, &iter);
108 gtk_list_store_set (store, &iter, 0, mplist_value (pl), -1);
113 key_pressed_cb (GtkEntry *entry, GdkEventKey *event, gpointer data)
120 struct BindingWidgets *bw = data;
122 c = gdk_keyval_to_unicode (event->keyval);
125 switch (event->keyval)
127 case GDK_Meta_L: case GDK_Meta_R:
128 modifier_state |= META_MASK_BIT; return TRUE;
129 case GDK_Alt_L: case GDK_Alt_R:
130 modifier_state |= ALT_MASK_BIT; return TRUE;
131 case GDK_Super_L: case GDK_Super_R:
132 modifier_state |= SUPER_MASK_BIT; return TRUE;
133 case GDK_Hyper_L: case GDK_Hyper_R:
134 modifier_state |= HYPER_MASK_BIT; return TRUE;
136 if (event->keyval >= GDK_Shift_L && event->keyval <= GDK_Shift_Lock)
139 name = gdk_keyval_name (event->keyval);
142 nbytes = strlen (name);
148 mtext_cat_char (mt, c);
149 nbytes = mconv_encode_buffer (msymbol ("utf-8"), mt,
150 (unsigned char *) name, 32);
151 m17n_object_unref (mt);
154 if (c == 0 && event->state & GDK_SHIFT_MASK)
155 buf[i++] = 'S', buf[i++] = '-';
156 if (event->state & GDK_CONTROL_MASK)
157 buf[i++] = 'C', buf[i++] = '-';
158 if (modifier_state & META_MASK_BIT)
159 buf[i++] = 'M', buf[i++] = '-';
160 if (modifier_state & ALT_MASK_BIT)
161 buf[i++] = 'A', buf[i++] = '-';
162 if (modifier_state & SUPER_MASK_BIT)
163 buf[i++] = 's', buf[i++] = '-';
164 if (modifier_state & HYPER_MASK_BIT)
165 buf[i++] = 'H', buf[i++] = '-';
166 strncpy (buf + i, name, nbytes);
168 mplist_add (entry_keyseq, Msymbol, msymbol (buf));
169 update_entry (entry);
170 gtk_widget_set_sensitive (bw->clear, TRUE);
171 gtk_widget_set_sensitive (bw->add, TRUE);
176 key_released_cb (GtkEntry *entry, GdkEventKey *event, gpointer data)
180 c = gdk_keyval_to_unicode (event->keyval);
183 switch (event->keyval)
185 case GDK_Meta_L: case GDK_Meta_R:
186 modifier_state &= ~META_MASK_BIT; break;
187 case GDK_Alt_L: case GDK_Alt_R:
188 modifier_state &= ~ALT_MASK_BIT; break;
189 case GDK_Super_L: case GDK_Super_R:
190 modifier_state &= ~SUPER_MASK_BIT; break;
191 case GDK_Hyper_L: case GDK_Hyper_R:
192 modifier_state &= ~HYPER_MASK_BIT; break;
199 update_widgets (struct BindingWidgets *bw)
201 MSymbol status = CURRENT_STATUS;
203 if (status == Mconfigured)
205 gtk_widget_set_sensitive (bw->default_, TRUE);
206 gtk_widget_set_sensitive (bw->revert, TRUE);
207 gtk_label_set_text (GTK_LABEL (bw->status), _("Status : modified"));
209 else if (status == Mcustomized)
211 gtk_widget_set_sensitive (bw->default_, TRUE);
212 gtk_widget_set_sensitive (bw->revert, FALSE);
213 gtk_label_set_text (GTK_LABEL (bw->status), _("Status : customized"));
217 gtk_widget_set_sensitive (bw->default_, FALSE);
218 gtk_widget_set_sensitive (bw->revert, FALSE);
219 gtk_label_set_text (GTK_LABEL (bw->status), _("Status : default"));
224 clear_cb (GtkButton *button, gpointer data)
226 struct BindingWidgets *bw = data;
228 mplist_set (entry_keyseq, Mnil, NULL);
229 gtk_widget_grab_focus (bw->entry);
230 update_entry (GTK_ENTRY (bw->entry));
231 gtk_widget_set_sensitive (bw->clear, FALSE);
232 gtk_widget_set_sensitive (bw->add, FALSE);
236 add_cb (GtkButton *button, gpointer data)
238 MPlist *new, *pl, *last;
241 struct BindingWidgets *bw = data;
243 if (mplist_length (entry_keyseq) == 0)
246 for (pl = CURRENT_BINDINGS;
247 pl && mplist_key (pl) == Mplist;
248 pl = mplist_next (pl))
250 if (! keyseq_equal (mplist_value (pl), entry_keyseq))
251 mplist_add (new, Mplist, mplist_value (pl));
254 /* entry_keyseq is already registered. */
255 m17n_object_unref (new);
259 mplist_add (new, Mplist, entry_keyseq);
260 CONFIG_COMMAND (new);
261 m17n_object_unref (new);
263 /* We cannot use ENTRY_KEYSEQ for gtk_list_store_set (). We must
264 use a pointer to the internally copied one. */
265 new = CURRENT_BINDINGS;
267 pl && mplist_key (pl) == Mplist;
268 last = pl, pl = mplist_next (pl));
269 store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (bw->view)));
270 gtk_list_store_append (store, &iter);
271 gtk_list_store_set (store, &iter, 0, mplist_value (last), -1);
272 update_binding_store (bw->view);
278 create_adding_section (struct BindingWidgets *bw)
280 GtkWidget *label, *hbox, *vbox;
282 label = gtk_label_new (_("New key binding:"));
284 entry_keyseq = mplist ();
285 bw->entry = gtk_entry_new ();
286 g_signal_connect (G_OBJECT (bw->entry), "key-press-event",
287 G_CALLBACK (key_pressed_cb), bw);
288 g_signal_connect (G_OBJECT (bw->entry), "key-release-event",
289 G_CALLBACK (key_released_cb), bw);
291 bw->clear = gtk_button_new_from_stock (GTK_STOCK_CLEAR);
292 gtk_widget_set_sensitive (bw->clear, FALSE);
293 g_signal_connect (G_OBJECT (bw->clear), "clicked",
294 G_CALLBACK (clear_cb), bw);
296 bw->add = gtk_button_new_from_stock (GTK_STOCK_ADD);
297 gtk_widget_set_sensitive (bw->add, FALSE);
298 g_signal_connect (G_OBJECT (bw->add), "clicked",
299 G_CALLBACK (add_cb), bw);
301 vbox = gtk_vbox_new (FALSE, 6);
302 gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
304 hbox = gtk_hbox_new (FALSE, 6);
305 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 6);
306 gtk_container_add (GTK_CONTAINER (vbox), hbox);
308 gtk_container_add (GTK_CONTAINER (vbox), bw->entry);
310 hbox = gtk_hbutton_box_new ();
311 gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_END);
312 gtk_box_set_spacing (GTK_BOX (hbox), 6);
313 gtk_container_add (GTK_CONTAINER (hbox), bw->clear);
314 gtk_container_add (GTK_CONTAINER (hbox), bw->add);
315 gtk_container_add (GTK_CONTAINER (vbox), hbox);
321 selection_cb (GtkTreeSelection *selection, gpointer data)
323 struct BindingWidgets *bw = data;
325 gtk_widget_set_sensitive
327 gtk_tree_selection_count_selected_rows (selection) ? TRUE : FALSE);
331 keyseq_render_function (GtkTreeViewColumn *column,
332 GtkCellRenderer *renderer,
341 gtk_tree_model_get (model, iter, 0, &keyseq, -1);
342 for (pl = keyseq, n = 0;
343 pl && mplist_key (pl) == Msymbol;
344 pl = mplist_next (pl))
345 n += strlen (msymbol_name ((MSymbol) mplist_value (pl))) + 1;
346 if (n < sizeof (buf))
350 pl && mplist_key (pl) == Msymbol;
351 pl = mplist_next (pl))
353 strcat (buf, msymbol_name ((MSymbol) mplist_value (pl)));
356 g_object_set (renderer, "foreground-set", FALSE, NULL);
360 g_snprintf (buf, sizeof (buf), _("Too long to display"));
361 g_object_set (renderer, "foreground", "Red", "foreground-set", TRUE,
364 g_object_set (renderer, "text", buf, NULL);
368 default_cb (GtkButton *button, gpointer data)
370 MPlist *empty = mplist ();
371 struct BindingWidgets *bw = data;
373 CONFIG_COMMAND (empty);
374 m17n_object_unref (empty);
375 update_binding_store (bw->view);
380 revert_cb (GtkButton *button, gpointer data)
382 struct BindingWidgets *bw = data;
384 CONFIG_COMMAND (NULL);
385 update_binding_store (bw->view);
390 delete_cb (GtkButton *button, gpointer data)
392 GtkTreeSelection *selection;
395 MPlist *keyseq, *new, *pl;
396 struct BindingWidgets *bw = data;
398 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (bw->view));
399 if (! gtk_tree_selection_get_selected (selection, &model, &iter))
401 gtk_tree_model_get (model, &iter, 0, &keyseq, -1);
403 for (pl = CURRENT_BINDINGS;
404 pl && mplist_key (pl) == Mplist;
405 pl = mplist_next (pl))
406 if (! keyseq_equal (mplist_value (pl), keyseq))
407 mplist_add (new, Mplist, mplist_value (pl));
408 CONFIG_COMMAND (new);
409 m17n_object_unref (new);
410 gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
411 update_binding_store (bw->view);
416 create_deleting_section (struct BindingWidgets *bw)
419 GtkWidget *scrolled, *hbox, *vbox;
420 GtkTreeViewColumn *column;
421 GtkCellRenderer *renderer;
423 GtkTreeSelection *selection;
426 store = gtk_list_store_new (1, G_TYPE_POINTER);
427 bw->view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
428 g_object_unref (G_OBJECT (store));
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_column_set_title (column, _("Current Key Bindings"));
443 gtk_tree_view_append_column (GTK_TREE_VIEW (bw->view), column);
445 renderer = gtk_cell_renderer_text_new ();
446 gtk_tree_view_column_pack_start (column, renderer, TRUE);
447 gtk_tree_view_column_set_cell_data_func
448 (column, renderer, keyseq_render_function, NULL, NULL);
450 bw->default_ = gtk_button_new_from_stock (_("_Default"));
451 g_signal_connect (G_OBJECT (bw->default_), "clicked",
452 G_CALLBACK (default_cb), bw);
454 bw->revert = gtk_button_new_from_stock (GTK_STOCK_REVERT_TO_SAVED);
455 g_signal_connect (G_OBJECT (bw->revert), "clicked",
456 G_CALLBACK (revert_cb), bw);
458 bw->delete = gtk_button_new_from_stock (GTK_STOCK_DELETE);
459 gtk_widget_set_sensitive (bw->delete, FALSE);
460 g_signal_connect (G_OBJECT (bw->delete), "clicked",
461 G_CALLBACK (delete_cb), bw);
463 vbox = gtk_vbox_new (FALSE, 6);
464 gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
466 gtk_container_add (GTK_CONTAINER (vbox), scrolled);
468 hbox = gtk_hbutton_box_new ();
469 gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_END);
470 gtk_box_set_spacing (GTK_BOX (hbox), 6);
471 gtk_container_add (GTK_CONTAINER (hbox), bw->default_);
472 gtk_container_add (GTK_CONTAINER (hbox), bw->revert);
473 gtk_container_add (GTK_CONTAINER (hbox), bw->delete);
474 gtk_container_add (GTK_CONTAINER (vbox), hbox);
480 set_status (GtkListStore *store, GtkTreeIter *iter)
485 status = CURRENT_STATUS;
486 if (status == Mconfigured)
487 status_str = _("modified");
488 else if (status == Mcustomized)
489 status_str = _("customized");
491 status_str = _("default");
492 gtk_list_store_set (store, iter, 1, status_str, -1);
496 activated_cb (GtkTreeView *parent, GtkTreePath *path,
497 GtkTreeViewColumn *col, gpointer data)
501 GtkWidget *dialog, *vbox;
502 struct BindingWidgets bw;
505 model = gtk_tree_view_get_model (parent);
506 if (! gtk_tree_model_get_iter (model, &iter, path))
508 gtk_tree_model_get (model, &iter, 0, &command, -1);
509 current_command = msymbol (command);
511 dialog = (gtk_dialog_new_with_buttons
513 GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (parent))),
514 GTK_DIALOG_DESTROY_WITH_PARENT,
515 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
518 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox),
519 create_adding_section (&bw));
521 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox),
522 create_deleting_section (&bw));
524 bw.status = gtk_label_new (NULL);
525 vbox = gtk_vbox_new (FALSE, 12);
526 gtk_container_add (GTK_CONTAINER (vbox), bw.status);
527 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), vbox);
529 update_widgets (&bw);
530 gtk_widget_show_all (dialog);
531 gtk_dialog_run (GTK_DIALOG (dialog));
532 gtk_tree_model_get_iter (model, &iter, path);
533 set_status (GTK_LIST_STORE (model), &iter);
534 gtk_widget_destroy (dialog);
535 m17n_object_unref (entry_keyseq);
539 create_command_list (GtkTooltips *tip, MSymbol lang, MSymbol name)
543 GtkCellRenderer *renderer;
549 plist = minput_get_command (lang, name, Mnil);
551 * plist == ((command description status keyseq keyseq ...)
552 * (command description status keyseq keyseq ...)
556 return gtk_label_new (_("No customizable commands."));
558 store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
559 for (; plist && mplist_key (plist) == Mplist; plist = mplist_next (plist))
563 MSymbol command, status;
567 pl = mplist_value (plist);
568 /* pl == (command description status keyseq keyseq ...) */
569 current_command = command = mplist_value (pl);
571 pl = mplist_next (pl);
572 /* pl == (description status keyseq keyseq ...) */
573 if (mplist_key (pl) == Mtext)
574 /* Fixme : Assuming the return value is in UTF-8 */
575 desc = mtext_data (mplist_value (pl), NULL, NULL, NULL, NULL);
579 pl = mplist_next (pl);
580 /* pl == (status keyseq keyseq ...) */
581 status = mplist_value (pl);
582 gtk_list_store_append (store, &iter);
583 gtk_list_store_set (store, &iter,
584 0, msymbol_name (command),
586 set_status (store, &iter);
588 view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
589 g_object_unref (G_OBJECT (store));
590 renderer = gtk_cell_renderer_text_new ();
591 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
597 renderer = gtk_cell_renderer_text_new ();
598 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
604 g_signal_connect (G_OBJECT (view), "row-activated",
605 G_CALLBACK (activated_cb), NULL);