1 /* ncmpc (Ncurses MPD Client)
2 * (c) 2004-2010 The Music Player Daemon Project
3 * Project homepage: http://musicpd.org
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 of the License, or
8 * (at your option) any later version.
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.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "screen_keydef.h"
21 #include "screen_interface.h"
22 #include "screen_status.h"
23 #include "screen_find.h"
27 #include "screen_utils.h"
34 static struct list_window
*lw
;
36 static command_definition_t
*cmds
= NULL
;
38 /** the number of commands */
39 static unsigned command_n_commands
= 0;
42 * the position of the "apply" item. It's the same as command_n_commands,
43 * because array subscripts start at 0, while numbers of items start at 1.
45 static G_GNUC_PURE
inline unsigned
46 command_item_apply(void)
48 return command_n_commands
;
51 /** the position of the "apply and save" item */
52 static G_GNUC_PURE
inline unsigned
53 command_item_save(void)
55 return command_item_apply() + 1;
58 /** the number of items in the "command" view */
59 static G_GNUC_PURE
inline unsigned
62 return command_item_save() + 1;
67 * The command being edited, represented by a array subscript to @cmds, or -1,
68 * if no command is being edited
70 static int subcmd
= -1;
72 /** The number of keys assigned to the current command */
73 static unsigned subcmd_n_keys
= 0;
75 /** The position of the up ("[..]") item */
76 static G_GNUC_CONST
inline unsigned
82 /** The position of the "add a key" item */
83 static G_GNUC_PURE
inline unsigned
86 return subcmd_n_keys
+ 1;
89 /** The number of items in the list_window, if there's a command being edited */
90 static G_GNUC_PURE
inline unsigned
93 return subcmd_item_add() + 1;
96 /** Check whether a given item is a key */
97 static G_GNUC_PURE
inline bool
98 subcmd_item_is_key(unsigned i
)
100 return (i
> subcmd_item_up() && i
< subcmd_item_add());
104 * Convert an item id (as in lw->selected) into a "key id", which is an array
105 * subscript to cmds[subcmd].keys.
107 static G_GNUC_CONST
inline unsigned
108 subcmd_item_to_key_id(unsigned i
)
115 keybindings_changed(void)
117 command_definition_t
*orginal_cmds
= get_command_definitions();
118 size_t size
= command_n_commands
* sizeof(command_definition_t
);
120 return memcmp(orginal_cmds
, cmds
, size
);
126 if (keybindings_changed()) {
127 command_definition_t
*orginal_cmds
= get_command_definitions();
128 size_t size
= command_n_commands
* sizeof(command_definition_t
);
130 memcpy(orginal_cmds
, cmds
, size
);
131 screen_status_printf(_("You have new key bindings"));
133 screen_status_printf(_("Keybindings unchanged."));
142 if (check_user_conf_dir()) {
143 screen_status_printf(_("Error: Unable to create directory ~/.ncmpc - %s"),
149 filename
= build_user_key_binding_filename();
151 if ((f
= fopen(filename
,"w")) == NULL
) {
152 screen_status_printf(_("Error: %s - %s"), filename
, strerror(errno
));
158 if (write_key_bindings(f
, KEYDEF_WRITE_HEADER
))
159 screen_status_printf(_("Error: %s - %s"), filename
, strerror(errno
));
161 screen_status_printf(_("Wrote %s"), filename
);
167 /* TODO: rename to check_n_keys / subcmd_count_keys? */
169 check_subcmd_length(void)
173 /* this loops counts the continous valid keys at the start of the the keys
174 array, so make sure you don't have gaps */
175 for (i
= 0; i
< MAX_COMMAND_KEYS
; i
++)
176 if (cmds
[subcmd
].keys
[i
] == 0)
180 list_window_set_length(lw
, subcmd_length());
193 /** lw->start the last time switch_to_subcmd_mode() was called */
194 static unsigned saved_start
= 0;
197 switch_to_subcmd_mode(int cmd
)
199 assert(subcmd
== -1);
201 saved_start
= lw
->start
;
204 list_window_reset(lw
);
205 check_subcmd_length();
211 switch_to_command_mode(void)
213 assert(subcmd
!= -1);
215 list_window_set_length(lw
, command_length());
216 list_window_set_cursor(lw
, subcmd
);
219 lw
->start
= saved_start
;
225 * Delete a key from a given command's definition
226 * @param cmd_index the command
227 * @param key_index the key (see below)
230 delete_key(int cmd_index
, int key_index
)
232 /* shift the keys to close the gap that appeared */
234 while (i
< MAX_COMMAND_KEYS
&& cmds
[cmd_index
].keys
[i
])
235 cmds
[cmd_index
].keys
[key_index
++] = cmds
[cmd_index
].keys
[i
++];
237 /* As key_index now holds the index of the last key slot that contained
238 a key, we use it to empty this slot, because this key has been copied
239 to the previous slot in the loop above */
240 cmds
[cmd_index
].keys
[key_index
] = 0;
242 cmds
[cmd_index
].flags
|= COMMAND_KEY_MODIFIED
;
243 check_subcmd_length();
245 screen_status_printf(_("Deleted"));
250 /* update key conflict flags */
251 check_key_bindings(cmds
, NULL
, 0);
254 /* assigns a new key to a key slot */
256 overwrite_key(int cmd_index
, int key_index
)
262 assert(key_index
< MAX_COMMAND_KEYS
);
264 buf
= g_strdup_printf(_("Enter new key for %s: "), cmds
[cmd_index
].name
);
265 key
= screen_getch(buf
);
269 screen_status_printf(_("Aborted"));
274 screen_status_printf(_("Ctrl-Space can't be used"));
278 cmd
= find_key_command(key
, cmds
);
279 if (cmd
!= CMD_NONE
) {
280 screen_status_printf(_("Error: key %s is already used for %s"),
281 key2str(key
), get_key_command_name(cmd
));
286 cmds
[cmd_index
].keys
[key_index
] = key
;
287 cmds
[cmd_index
].flags
|= COMMAND_KEY_MODIFIED
;
289 screen_status_printf(_("Assigned %s to %s"),
290 key2str(key
),cmds
[cmd_index
].name
);
291 check_subcmd_length();
296 /* update key conflict flags */
297 check_key_bindings(cmds
, NULL
, 0);
300 /* assign a new key to a new slot */
302 add_key(int cmd_index
)
304 if (subcmd_n_keys
< MAX_COMMAND_KEYS
)
305 overwrite_key(cmd_index
, subcmd_n_keys
);
309 list_callback(unsigned idx
, G_GNUC_UNUSED
void *data
)
311 static char buf
[256];
314 if (idx
== command_item_apply())
315 return _("===> Apply key bindings ");
316 if (idx
== command_item_save())
317 return _("===> Apply & Save key bindings ");
319 assert(idx
< (unsigned) command_n_commands
);
322 * Format the lines in two aligned columnes for the key name and
323 * the description, like this:
325 * this-command - do this
328 size_t len
= strlen(cmds
[idx
].name
);
329 strncpy(buf
, cmds
[idx
].name
, sizeof(buf
));
331 if (len
< get_cmds_max_name_width(cmds
))
332 memset(buf
+ len
, ' ', get_cmds_max_name_width(cmds
) - len
);
334 g_snprintf(buf
+ get_cmds_max_name_width(cmds
),
335 sizeof(buf
) - get_cmds_max_name_width(cmds
),
336 " - %s", _(cmds
[idx
].description
));
340 if (idx
== subcmd_item_up())
343 if (idx
== subcmd_item_add()) {
344 g_snprintf(buf
, sizeof(buf
), "%d. %s",
345 idx
, _("Add new key"));
349 assert(subcmd_item_is_key(idx
));
351 g_snprintf(buf
, sizeof(buf
),
352 "%d. %-20s (%d) ", idx
,
353 key2str(cmds
[subcmd
].keys
[subcmd_item_to_key_id(idx
)]),
354 cmds
[subcmd
].keys
[subcmd_item_to_key_id(idx
)]);
360 keydef_init(WINDOW
*w
, int cols
, int rows
)
362 lw
= list_window_init(w
, cols
, rows
);
366 keydef_resize(int cols
, int rows
)
368 list_window_resize(lw
, cols
, rows
);
374 list_window_free(lw
);
382 keydef_open(G_GNUC_UNUSED
struct mpdclient
*c
)
385 command_definition_t
*current_cmds
= get_command_definitions();
388 command_n_commands
= 0;
389 while (current_cmds
[command_n_commands
].name
)
390 command_n_commands
++;
392 /* +1 for the terminator element */
393 cmds_size
= (command_n_commands
+ 1) * sizeof(command_definition_t
);
394 cmds
= g_malloc0(cmds_size
);
395 memcpy(cmds
, current_cmds
, cmds_size
);
399 list_window_set_length(lw
, command_length());
405 if (cmds
&& !keybindings_changed()) {
409 screen_status_printf(_("Note: Did you forget to \'Apply\' your changes?"));
413 keydef_title(char *str
, size_t size
)
416 return _("Edit key bindings");
418 g_snprintf(str
, size
, _("Edit keys for %s"), cmds
[subcmd
].name
);
425 list_window_paint(lw
, list_callback
, NULL
);
429 keydef_cmd(G_GNUC_UNUSED
struct mpdclient
*c
, command_t cmd
)
431 if (cmd
== CMD_LIST_RANGE_SELECT
)
434 if (list_window_cmd(lw
, cmd
)) {
442 if (lw
->selected
== command_item_apply()) {
444 } else if (lw
->selected
== command_item_save()) {
448 switch_to_subcmd_mode(lw
->selected
);
451 if (lw
->selected
== subcmd_item_up()) {
452 switch_to_command_mode();
453 } else if (lw
->selected
== subcmd_item_add()) {
456 /* just to be sure ;-) */
457 assert(subcmd_item_is_key(lw
->selected
));
458 overwrite_key(subcmd
, subcmd_item_to_key_id(lw
->selected
));
462 case CMD_GO_PARENT_DIRECTORY
:
463 case CMD_GO_ROOT_DIRECTORY
:
465 switch_to_command_mode();
468 if (subcmd
!= -1 && subcmd_item_is_key(lw
->selected
))
469 delete_key(subcmd
, subcmd_item_to_key_id(lw
->selected
));
476 case CMD_SAVE_PLAYLIST
:
482 case CMD_LIST_FIND_NEXT
:
483 case CMD_LIST_RFIND_NEXT
:
484 screen_find(lw
, cmd
, list_callback
, NULL
);
497 const struct screen_functions screen_keydef
= {
501 .close
= keydef_close
,
502 .resize
= keydef_resize
,
503 .paint
= keydef_paint
,
505 .get_title
= keydef_title
,