Commit 9767f7f9f81acfee3cd1f5a9dcade478512b1fb1
1 parent
b08a928d
Exists in
master
and in
1 other branch
Checking for duplicate accelerators.
Showing
2 changed files
with
200 additions
and
7 deletions
Show diff stats
src/dialogs/settings/accelerator.c
| ... | ... | @@ -30,6 +30,10 @@ |
| 30 | 30 | /** |
| 31 | 31 | * @brief Implements the accelerator settings widget. |
| 32 | 32 | * |
| 33 | + * References: | |
| 34 | + * | |
| 35 | + * <https://github.com/Apress/foundations-of-gtk-dev/blob/master/chapter_8/accelerators.c> | |
| 36 | + * | |
| 33 | 37 | */ |
| 34 | 38 | |
| 35 | 39 | #include <internals.h> |
| ... | ... | @@ -38,6 +42,7 @@ |
| 38 | 42 | #include <lib3270/log.h> |
| 39 | 43 | #include <v3270/actions.h> |
| 40 | 44 | #include <terminal.h> |
| 45 | + #include <gdk/gdkkeysyms-compat.h> | |
| 41 | 46 | |
| 42 | 47 | /*--[ Widget Definition ]----------------------------------------------------------------------------*/ |
| 43 | 48 | |
| ... | ... | @@ -69,6 +74,9 @@ |
| 69 | 74 | static void load(GtkWidget *w, GtkWidget *terminal); |
| 70 | 75 | static void apply(GtkWidget *w, GtkWidget *terminal); |
| 71 | 76 | |
| 77 | + static void accel_edited(GtkCellRendererAccel*, gchar*, guint, GdkModifierType, guint, V3270AcceleratorSettings*); | |
| 78 | + static void alternative_edited(GtkCellRendererAccel*, gchar*, guint, GdkModifierType, guint, V3270AcceleratorSettings*); | |
| 79 | + | |
| 72 | 80 | /*--[ Implement ]------------------------------------------------------------------------------------*/ |
| 73 | 81 | |
| 74 | 82 | static void V3270AcceleratorSettings_class_init(V3270AcceleratorSettingsClass *klass) |
| ... | ... | @@ -95,13 +103,23 @@ |
| 95 | 103 | // Create Accelerator list |
| 96 | 104 | GtkCellRenderer * text_renderer = gtk_cell_renderer_text_new(); |
| 97 | 105 | |
| 98 | - GtkCellRenderer * accel_renderer = gtk_cell_renderer_accel_new(); | |
| 106 | + GtkCellRenderer * accel_renderer[] = { gtk_cell_renderer_accel_new(), gtk_cell_renderer_accel_new() }; | |
| 107 | + | |
| 108 | + g_object_set( | |
| 109 | + accel_renderer[0], | |
| 110 | + "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER, | |
| 111 | + "editable", TRUE, | |
| 112 | + NULL); | |
| 113 | + | |
| 99 | 114 | g_object_set( |
| 100 | - accel_renderer, | |
| 115 | + accel_renderer[1], | |
| 101 | 116 | "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER, |
| 102 | 117 | "editable", TRUE, |
| 103 | 118 | NULL); |
| 104 | 119 | |
| 120 | + g_signal_connect (G_OBJECT (accel_renderer[0]), "accel_edited", G_CALLBACK (accel_edited), widget); | |
| 121 | + g_signal_connect (G_OBJECT (accel_renderer[1]), "accel_edited", G_CALLBACK (alternative_edited), widget); | |
| 122 | + | |
| 105 | 123 | widget->store = GTK_LIST_STORE(gtk_list_store_new(COLUMNS, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_INT, G_TYPE_UINT, G_TYPE_INT, G_TYPE_UINT)); |
| 106 | 124 | |
| 107 | 125 | gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(widget->store),1,GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID); |
| ... | ... | @@ -124,7 +142,7 @@ |
| 124 | 142 | GTK_TREE_VIEW(view), |
| 125 | 143 | -1, |
| 126 | 144 | _("Accelerator"), |
| 127 | - accel_renderer, | |
| 145 | + accel_renderer[0], | |
| 128 | 146 | "accel-mods", MAIN_MASK, |
| 129 | 147 | "accel-key", MAIN_VALUE, |
| 130 | 148 | NULL |
| ... | ... | @@ -134,7 +152,7 @@ |
| 134 | 152 | GTK_TREE_VIEW(view), |
| 135 | 153 | -1, |
| 136 | 154 | _("Alternative"), |
| 137 | - accel_renderer, | |
| 155 | + accel_renderer[1], | |
| 138 | 156 | "accel-mods", ALTERNATIVE_MASK, |
| 139 | 157 | "accel-key", ALTERNATIVE_VALUE, |
| 140 | 158 | NULL |
| ... | ... | @@ -155,7 +173,8 @@ |
| 155 | 173 | gtk_grid_attach(GTK_GRID(widget),box,0,0,4,4); |
| 156 | 174 | } |
| 157 | 175 | |
| 158 | - } | |
| 176 | +} | |
| 177 | + | |
| 159 | 178 | |
| 160 | 179 | LIB3270_EXPORT GtkWidget * v3270_accelerator_settings_new() |
| 161 | 180 | { |
| ... | ... | @@ -167,6 +186,163 @@ LIB3270_EXPORT GtkWidget * v3270_accelerator_settings_new() |
| 167 | 186 | return GTK_WIDGET(settings); |
| 168 | 187 | } |
| 169 | 188 | |
| 189 | +struct AccelEditInfo | |
| 190 | +{ | |
| 191 | + GtkWidget * widget; | |
| 192 | + gint response; | |
| 193 | + V3270Accelerator * origin; | |
| 194 | + V3270Accelerator * accel; | |
| 195 | + GtkTreePath *path; | |
| 196 | + guint accel_key; | |
| 197 | + GdkModifierType mask; | |
| 198 | + int id_key; | |
| 199 | + int id_mask; | |
| 200 | +}; | |
| 201 | + | |
| 202 | +static gboolean check_accel(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, struct AccelEditInfo * info) | |
| 203 | +{ | |
| 204 | + static const gint columns[] = { MAIN_MASK, MAIN_VALUE, ALTERNATIVE_MASK, ALTERNATIVE_VALUE }; | |
| 205 | + size_t ix; | |
| 206 | + GValue value; | |
| 207 | + | |
| 208 | + debug("%s",__FUNCTION__); | |
| 209 | + | |
| 210 | + for(ix = 0; ix < 2; ix++) | |
| 211 | + { | |
| 212 | + guint key; | |
| 213 | + GdkModifierType mask; | |
| 214 | + | |
| 215 | + memset(&value,0,sizeof(value)); | |
| 216 | + gtk_tree_model_get_value(model, iter, columns[(ix * 2)], &value); | |
| 217 | + mask = (GdkModifierType) g_value_get_int(&value); | |
| 218 | + g_value_unset(&value); | |
| 219 | + | |
| 220 | + memset(&value,0,sizeof(value)); | |
| 221 | + gtk_tree_model_get_value(model, iter, columns[(ix * 2)+1], &value); | |
| 222 | + key = g_value_get_uint(&value); | |
| 223 | + g_value_unset(&value); | |
| 224 | + | |
| 225 | + if(key == info->accel_key && mask == info->mask) { | |
| 226 | + | |
| 227 | + debug("************ Index %d cmp=%d",(unsigned int) ix, gtk_tree_path_compare(path, info->path)); | |
| 228 | + GtkWidget * dialog; | |
| 229 | + | |
| 230 | + if(gtk_tree_path_compare(path, info->path)) | |
| 231 | + { | |
| 232 | + // Another entry, rejects. | |
| 233 | + memset(&value,0,sizeof(value)); | |
| 234 | + gtk_tree_model_get_value(model, iter, ACTION, &value); | |
| 235 | + info->accel = (V3270Accelerator *) g_value_get_pointer(&value); | |
| 236 | + g_value_unset(&value); | |
| 237 | + | |
| 238 | + debug("Key is already set on accel %s",v3270_accelerator_get_name(info->accel)); | |
| 239 | + | |
| 240 | + // Ask for what the user wants to do. | |
| 241 | + dialog = gtk_message_dialog_new_with_markup( | |
| 242 | + GTK_WINDOW(gtk_widget_get_toplevel(info->widget)), | |
| 243 | + GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT, | |
| 244 | + GTK_MESSAGE_QUESTION, | |
| 245 | + GTK_BUTTONS_YES_NO, | |
| 246 | + _( "The selected accelerator is in use by action <b>%s</b> (<b>%s</b>)" ), | |
| 247 | + v3270_accelerator_get_description(info->accel), | |
| 248 | + v3270_accelerator_get_name(info->accel) | |
| 249 | + ); | |
| 250 | + | |
| 251 | + gtk_message_dialog_format_secondary_markup( | |
| 252 | + GTK_MESSAGE_DIALOG(dialog), | |
| 253 | + _( "Assign it to action (<b>%s</b>)?"), | |
| 254 | + v3270_accelerator_get_name(info->origin) | |
| 255 | + ); | |
| 256 | + | |
| 257 | + } | |
| 258 | + else | |
| 259 | + { | |
| 260 | + // It's on the same entry. | |
| 261 | + dialog = gtk_message_dialog_new_with_markup( | |
| 262 | + GTK_WINDOW(gtk_widget_get_toplevel(info->widget)), | |
| 263 | + GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT, | |
| 264 | + GTK_MESSAGE_QUESTION, | |
| 265 | + GTK_BUTTONS_CANCEL, | |
| 266 | + _( "The selected accelerator is in use by the same action" ) | |
| 267 | + ); | |
| 268 | + | |
| 269 | + } | |
| 270 | + | |
| 271 | + gtk_window_set_title(GTK_WINDOW(dialog),_("Accelerator is in use")); | |
| 272 | + gtk_widget_show_all(dialog); | |
| 273 | + | |
| 274 | + info->response = gtk_dialog_run(GTK_DIALOG(dialog)); | |
| 275 | + if(info->response == GTK_RESPONSE_YES) | |
| 276 | + { | |
| 277 | + debug("%s: Removing accelerator from the other action",__FUNCTION__); | |
| 278 | + | |
| 279 | + } | |
| 280 | + | |
| 281 | + gtk_widget_destroy(dialog); | |
| 282 | + | |
| 283 | + return TRUE; | |
| 284 | + | |
| 285 | + } | |
| 286 | + | |
| 287 | + } | |
| 288 | + | |
| 289 | + return FALSE; | |
| 290 | + | |
| 291 | +} | |
| 292 | + | |
| 293 | +static void change_accel(V3270AcceleratorSettings *widget, gchar *path, guint accel_key, GdkModifierType mask, int id_key, int id_mask) | |
| 294 | +{ | |
| 295 | + struct AccelEditInfo info = { | |
| 296 | + .response = GTK_RESPONSE_YES, | |
| 297 | + .widget = GTK_WIDGET(widget), | |
| 298 | + .accel = NULL, | |
| 299 | + .accel_key = accel_key, | |
| 300 | + .mask = mask, | |
| 301 | + .id_key = id_key, | |
| 302 | + .id_mask = id_mask, | |
| 303 | + .path = gtk_tree_path_new_from_string(path) | |
| 304 | + }; | |
| 305 | + | |
| 306 | + GtkTreeIter iter; | |
| 307 | + if(gtk_tree_model_get_iter(GTK_TREE_MODEL(widget->store),&iter,info.path)) | |
| 308 | + { | |
| 309 | + GValue value; | |
| 310 | + memset(&value,0,sizeof(value)); | |
| 311 | + gtk_tree_model_get_value(GTK_TREE_MODEL(widget->store), &iter, ACTION, &value); | |
| 312 | + info.origin = (V3270Accelerator *) g_value_get_pointer(&value); | |
| 313 | + g_value_unset(&value); | |
| 314 | + | |
| 315 | + if(!info.origin) | |
| 316 | + { | |
| 317 | + g_warning("Can't identify the origin accelerator, aborting action"); | |
| 318 | + return; | |
| 319 | + } | |
| 320 | + | |
| 321 | + } | |
| 322 | + | |
| 323 | + gtk_tree_model_foreach(GTK_TREE_MODEL(widget->store), (GtkTreeModelForeachFunc) check_accel, &info); | |
| 324 | + | |
| 325 | + gtk_tree_path_free(info.path); | |
| 326 | + | |
| 327 | + if(info.response == GTK_RESPONSE_YES) | |
| 328 | + { | |
| 329 | + debug("%s: Aplicar alteração",__FUNCTION__); | |
| 330 | + | |
| 331 | + } | |
| 332 | + | |
| 333 | +} | |
| 334 | + | |
| 335 | +static void accel_edited(GtkCellRendererAccel *renderer, gchar *path, guint accel_key, GdkModifierType mask, guint hardware_keycode, V3270AcceleratorSettings *widget) | |
| 336 | +{ | |
| 337 | + debug("%s(%s)",__FUNCTION__,path); | |
| 338 | + change_accel(widget, path, accel_key, mask, MAIN_MASK, MAIN_VALUE); | |
| 339 | +} | |
| 340 | + | |
| 341 | +static void alternative_edited(GtkCellRendererAccel *renderer, gchar *path, guint accel_key, GdkModifierType mask, guint hardware_keycode, V3270AcceleratorSettings *widget) | |
| 342 | +{ | |
| 343 | + debug("%s(%s)",__FUNCTION__,path); | |
| 344 | + change_accel(widget, path, accel_key, mask, ALTERNATIVE_MASK, ALTERNATIVE_VALUE); | |
| 345 | +} | |
| 170 | 346 | |
| 171 | 347 | void load(GtkWidget *widget, GtkWidget *terminal) |
| 172 | 348 | { |
| ... | ... | @@ -195,8 +371,17 @@ void load(GtkWidget *widget, GtkWidget *terminal) |
| 195 | 371 | |
| 196 | 372 | if(ix < G_N_ELEMENTS(keymaps)) |
| 197 | 373 | { |
| 198 | - keymaps[ix].key = accel->key; | |
| 199 | - keymaps[ix].mods = accel->mods; | |
| 374 | + if(accel->type == V3270_ACCELERATOR_TYPE_PFKEY) | |
| 375 | + { | |
| 376 | + keymaps[ix].key = GDK_F1 + (((V3270PFKeyAccelerator *)accel)->keycode - 1); | |
| 377 | + keymaps[ix].mods = 0; | |
| 378 | + } | |
| 379 | + else | |
| 380 | + { | |
| 381 | + keymaps[ix].key = accel->key; | |
| 382 | + keymaps[ix].mods = accel->mods; | |
| 383 | + } | |
| 384 | + | |
| 200 | 385 | ix++; |
| 201 | 386 | } |
| 202 | 387 | |
| ... | ... | @@ -206,6 +391,7 @@ void load(GtkWidget *widget, GtkWidget *terminal) |
| 206 | 391 | // Add entry |
| 207 | 392 | GtkTreeIter iter; |
| 208 | 393 | gtk_list_store_append(store, &iter); |
| 394 | + | |
| 209 | 395 | gtk_list_store_set( |
| 210 | 396 | store, |
| 211 | 397 | &iter, |
| ... | ... | @@ -218,6 +404,7 @@ void load(GtkWidget *widget, GtkWidget *terminal) |
| 218 | 404 | -1 |
| 219 | 405 | ); |
| 220 | 406 | |
| 407 | + | |
| 221 | 408 | } |
| 222 | 409 | |
| 223 | 410 | } |
| ... | ... | @@ -226,3 +413,4 @@ void apply(GtkWidget *widget, GtkWidget *terminal) |
| 226 | 413 | { |
| 227 | 414 | debug("%s::%s","V3270AcceleratorSettings",__FUNCTION__); |
| 228 | 415 | } |
| 416 | + | ... | ... |
src/terminal/keyboard/accelerator.c