Compare commits
	
		
			9 Commits
		
	
	
		
			0e443c0dcd
			...
			v0.9
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						15e583beb2
	
				 | 
					
					
						|||
| 
						
						
							
						
						cdb86652b9
	
				 | 
					
					
						|||
| 
						
						
							
						
						cbdec0552d
	
				 | 
					
					
						|||
| 
						
						
							
						
						2cd100af7a
	
				 | 
					
					
						|||
| 
						
						
							
						
						44ebc3591e
	
				 | 
					
					
						|||
| 
						
						
							
						
						0691c533b4
	
				 | 
					
					
						|||
| 
						
						
							
						
						6298235e22
	
				 | 
					
					
						|||
| 
						
						
							
						
						841e2f79c0
	
				 | 
					
					
						|||
| 
						
						
							
						
						5ade0f082e
	
				 | 
					
					
						
@@ -9,7 +9,7 @@ endif ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUCC)
 | 
			
		||||
 | 
			
		||||
# Version
 | 
			
		||||
set (project_VERSION_MAJOR "0")
 | 
			
		||||
set (project_VERSION_MINOR "1")
 | 
			
		||||
set (project_VERSION_MINOR "9")
 | 
			
		||||
set (project_VERSION_PATCH "0")
 | 
			
		||||
 | 
			
		||||
set (project_VERSION "${project_VERSION_MAJOR}")
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										286
									
								
								nncmpp.c
									
									
									
									
									
								
							
							
						
						
									
										286
									
								
								nncmpp.c
									
									
									
									
									
								
							@@ -1366,7 +1366,6 @@ app_write_mpd_status (struct row_buffer *buf)
 | 
			
		||||
	bool single  = (s = str_map_find (map, "single"))  && strcmp (s, "0");
 | 
			
		||||
	bool consume = (s = str_map_find (map, "consume")) && strcmp (s, "0");
 | 
			
		||||
 | 
			
		||||
	// TODO: remove the conditionals once we make them clickable
 | 
			
		||||
	struct row_buffer right = row_buffer_make ();
 | 
			
		||||
	chtype a[2] = { APP_ATTR (NORMAL), APP_ATTR (HIGHLIGHT) };
 | 
			
		||||
	if (repeat)  row_buffer_append_args (&right,
 | 
			
		||||
@@ -1548,6 +1547,10 @@ app_goto_tab (int tab_index)
 | 
			
		||||
	XX( MPD_SEARCH,         "Global search"               ) \
 | 
			
		||||
	XX( MPD_ADD,            "Add selection to playlist"   ) \
 | 
			
		||||
	XX( MPD_REPLACE,        "Replace playlist"            ) \
 | 
			
		||||
	XX( MPD_REPEAT,         "Toggle repeat"               ) \
 | 
			
		||||
	XX( MPD_RANDOM,         "Toggle random playback"      ) \
 | 
			
		||||
	XX( MPD_SINGLE,         "Toggle single song playback" ) \
 | 
			
		||||
	XX( MPD_CONSUME,        "Toggle consume"              ) \
 | 
			
		||||
	XX( MPD_UPDATE_DB,      "Update MPD database"         ) \
 | 
			
		||||
	XX( MPD_COMMAND,        "Send raw command to MPD"     ) \
 | 
			
		||||
	\
 | 
			
		||||
@@ -1698,6 +1701,14 @@ app_on_editor_end (bool confirmed)
 | 
			
		||||
 | 
			
		||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
app_mpd_toggle (const char *name)
 | 
			
		||||
{
 | 
			
		||||
	const char *s = str_map_find (&g.playback_info, name);
 | 
			
		||||
	bool value = s && strcmp (s, "0");
 | 
			
		||||
	return MPD_SIMPLE (name, value ? "0" : "1");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
app_process_action (enum action action)
 | 
			
		||||
{
 | 
			
		||||
@@ -1705,7 +1716,6 @@ app_process_action (enum action action)
 | 
			
		||||
	struct tab *tab = g.active_tab;
 | 
			
		||||
	if (tab->on_action && tab->on_action (action))
 | 
			
		||||
	{
 | 
			
		||||
		tab->item_mark = -1;
 | 
			
		||||
		app_invalidate ();
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
@@ -1767,6 +1777,10 @@ app_process_action (enum action action)
 | 
			
		||||
	case ACTION_MPD_NEXT:               return MPD_SIMPLE ("next");
 | 
			
		||||
	case ACTION_MPD_FORWARD:            return MPD_SIMPLE ("seekcur", "+10");
 | 
			
		||||
	case ACTION_MPD_BACKWARD:           return MPD_SIMPLE ("seekcur", "-10");
 | 
			
		||||
	case ACTION_MPD_REPEAT:             return app_mpd_toggle ("repeat");
 | 
			
		||||
	case ACTION_MPD_RANDOM:             return app_mpd_toggle ("random");
 | 
			
		||||
	case ACTION_MPD_SINGLE:             return app_mpd_toggle ("single");
 | 
			
		||||
	case ACTION_MPD_CONSUME:            return app_mpd_toggle ("consume");
 | 
			
		||||
	case ACTION_MPD_UPDATE_DB:          return MPD_SIMPLE ("update");
 | 
			
		||||
 | 
			
		||||
	case ACTION_MPD_VOLUME_UP:          return app_setvol (g.volume + 10);
 | 
			
		||||
@@ -2193,52 +2207,82 @@ current_tab_on_item_draw (size_t item_index, struct row_buffer *buffer,
 | 
			
		||||
	free (s);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
current_tab_move_song (const char *id, int diff)
 | 
			
		||||
static void
 | 
			
		||||
mpd_on_move_response (const struct mpd_response *response,
 | 
			
		||||
	const struct strv *data, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	(void) data;
 | 
			
		||||
 | 
			
		||||
	*(bool *) user_data = false;
 | 
			
		||||
	if (!response->success)
 | 
			
		||||
		print_error ("%s: %s", "command failed", response->message_text);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
current_tab_move (int from, int to)
 | 
			
		||||
{
 | 
			
		||||
	compact_map_t map;
 | 
			
		||||
	const char *id;
 | 
			
		||||
	if (!(map = item_list_get (&g.playlist, from))
 | 
			
		||||
	 || !(id = compact_map_find (map, "id")))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	char *target_str = xstrdup_printf ("%d", to);
 | 
			
		||||
	mpd_client_send_command (&g.client, "moveid", id, target_str, NULL);
 | 
			
		||||
	free (target_str);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
current_tab_move_selection (int diff)
 | 
			
		||||
{
 | 
			
		||||
	static bool already_moving;
 | 
			
		||||
	if (already_moving || diff == 0)
 | 
			
		||||
		return true;
 | 
			
		||||
 | 
			
		||||
	struct mpd_client *c = &g.client;
 | 
			
		||||
	int target = g_current_tab.item_selected + diff;
 | 
			
		||||
	if (c->state != MPD_CONNECTED || target < 0)
 | 
			
		||||
	if (c->state != MPD_CONNECTED)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	char *target_str = xstrdup_printf ("%d", target);
 | 
			
		||||
	mpd_client_send_command (c, "moveid", id, target_str, NULL);
 | 
			
		||||
	free (target_str);
 | 
			
		||||
	// TODO: we should create a cancellable action waiting for the move to
 | 
			
		||||
	//   finish, so that holding Shift-arrows works as expected.
 | 
			
		||||
	mpd_client_add_task (c, mpd_on_simple_response, NULL);
 | 
			
		||||
	mpd_client_idle (c, 0);
 | 
			
		||||
	struct tab *tab = &g_current_tab;
 | 
			
		||||
	struct tab_range range = tab_selection_range (tab);
 | 
			
		||||
	if (range.from + diff < 0
 | 
			
		||||
	 || range.upto + diff >= (int) tab->item_count)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	// XXX: this behaves a bit erratically, as even if we waited for
 | 
			
		||||
	//   a confirmation from the daemon, it would precede the playlist update
 | 
			
		||||
	g_current_tab.item_selected = target;
 | 
			
		||||
	app_move_selection (0);
 | 
			
		||||
	return true;
 | 
			
		||||
	mpd_client_list_begin (c);
 | 
			
		||||
	if (diff < 0)
 | 
			
		||||
		for (int i = range.from; i <= range.upto; i++)
 | 
			
		||||
			current_tab_move (i, i + diff);
 | 
			
		||||
	else
 | 
			
		||||
		for (int i = range.upto; i >= range.from; i--)
 | 
			
		||||
			current_tab_move (i, i + diff);
 | 
			
		||||
	mpd_client_list_end (c);
 | 
			
		||||
 | 
			
		||||
	mpd_client_add_task (c, mpd_on_move_response, &already_moving);
 | 
			
		||||
	mpd_client_idle (c, 0);
 | 
			
		||||
	return already_moving = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
current_tab_on_action (enum action action)
 | 
			
		||||
{
 | 
			
		||||
	struct tab *self = g.active_tab;
 | 
			
		||||
	struct tab_range range = tab_selection_range (self);
 | 
			
		||||
	compact_map_t map = item_list_get (&g.playlist, self->item_selected);
 | 
			
		||||
 | 
			
		||||
	const char *id;
 | 
			
		||||
	if (!map || !(id = compact_map_find (map, "id")))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	struct tab *tab = &g_current_tab;
 | 
			
		||||
	compact_map_t map = item_list_get (&g.playlist, tab->item_selected);
 | 
			
		||||
	switch (action)
 | 
			
		||||
	{
 | 
			
		||||
	// TODO: make this block more than just multiselect-tolerant
 | 
			
		||||
	case ACTION_MOVE_UP:   return current_tab_move_song (id, -1);
 | 
			
		||||
	case ACTION_MOVE_DOWN: return current_tab_move_song (id,  1);
 | 
			
		||||
 | 
			
		||||
		const char *id;
 | 
			
		||||
	case ACTION_MOVE_UP:
 | 
			
		||||
		return current_tab_move_selection (-1);
 | 
			
		||||
	case ACTION_MOVE_DOWN:
 | 
			
		||||
		return current_tab_move_selection (+1);
 | 
			
		||||
	case ACTION_CHOOSE:
 | 
			
		||||
		return MPD_SIMPLE ("playid", id);
 | 
			
		||||
 | 
			
		||||
		tab->item_mark = -1;
 | 
			
		||||
		return map && (id = compact_map_find (map, "id"))
 | 
			
		||||
			&& MPD_SIMPLE ("playid", id);
 | 
			
		||||
	case ACTION_DELETE:
 | 
			
		||||
	{
 | 
			
		||||
		struct mpd_client *c = &g.client;
 | 
			
		||||
		struct tab_range range = tab_selection_range (tab);
 | 
			
		||||
		if (range.from < 0 || c->state != MPD_CONNECTED)
 | 
			
		||||
			return false;
 | 
			
		||||
 | 
			
		||||
@@ -2255,15 +2299,16 @@ current_tab_on_action (enum action action)
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
current_tab_update (void)
 | 
			
		||||
{
 | 
			
		||||
	g_current_tab.item_count = g.playlist.len;
 | 
			
		||||
	g_current_tab.item_mark =
 | 
			
		||||
		MIN ((int) g.playlist.len - 1, g_current_tab.item_mark);
 | 
			
		||||
	app_invalidate ();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -2450,6 +2495,7 @@ library_tab_change_level (const char *new_path)
 | 
			
		||||
 | 
			
		||||
	free (g_library_tab.super.header);
 | 
			
		||||
	g_library_tab.super.header = NULL;
 | 
			
		||||
	g_library_tab.super.item_mark = -1;
 | 
			
		||||
 | 
			
		||||
	if (path->len)
 | 
			
		||||
		g_library_tab.super.header = xstrdup_printf ("/%s", path->str);
 | 
			
		||||
@@ -2593,8 +2639,12 @@ static bool
 | 
			
		||||
library_tab_on_action (enum action action)
 | 
			
		||||
{
 | 
			
		||||
	struct mpd_client *c = &g.client;
 | 
			
		||||
	struct tab_range range = tab_selection_range (&g_library_tab.super);
 | 
			
		||||
	if (range.from < 0 || c->state != MPD_CONNECTED)
 | 
			
		||||
	if (c->state != MPD_CONNECTED)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	struct tab *tab = &g_library_tab.super;
 | 
			
		||||
	struct tab_range range = tab_selection_range (tab);
 | 
			
		||||
	if (range.from < 0)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	struct library_tab_item x =
 | 
			
		||||
@@ -2615,6 +2665,7 @@ library_tab_on_action (enum action action)
 | 
			
		||||
		case LIBRARY_FILE: MPD_SIMPLE ("add", x.path);  break;
 | 
			
		||||
		default:           hard_assert (!"invalid item type");
 | 
			
		||||
		}
 | 
			
		||||
		tab->item_mark = -1;
 | 
			
		||||
		return true;
 | 
			
		||||
	case ACTION_UP:
 | 
			
		||||
	{
 | 
			
		||||
@@ -2641,8 +2692,8 @@ library_tab_on_action (enum action action)
 | 
			
		||||
			free (fake_subdir);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		free (g_library_tab.super.header);
 | 
			
		||||
		g_library_tab.super.header = xstrdup_printf ("Global search");
 | 
			
		||||
		free (tab->header);
 | 
			
		||||
		tab->header = xstrdup_printf ("Global search");
 | 
			
		||||
		g_library_tab.searching = true;
 | 
			
		||||
 | 
			
		||||
		// Since we've already changed the header, empty the list,
 | 
			
		||||
@@ -2666,6 +2717,7 @@ library_tab_on_action (enum action action)
 | 
			
		||||
			if (x.type == LIBRARY_DIR || x.type == LIBRARY_FILE)
 | 
			
		||||
				MPD_SIMPLE ("add", x.path);
 | 
			
		||||
		}
 | 
			
		||||
		tab->item_mark = -1;
 | 
			
		||||
		return true;
 | 
			
		||||
	case ACTION_MPD_REPLACE:
 | 
			
		||||
		if (!library_tab_is_range_playable (range))
 | 
			
		||||
@@ -2690,6 +2742,7 @@ library_tab_on_action (enum action action)
 | 
			
		||||
		mpd_client_list_end (c);
 | 
			
		||||
		mpd_client_add_task (c, mpd_on_simple_response, NULL);
 | 
			
		||||
		mpd_client_idle (c, 0);
 | 
			
		||||
		tab->item_mark = -1;
 | 
			
		||||
		return true;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
@@ -2932,12 +2985,12 @@ error:
 | 
			
		||||
static bool
 | 
			
		||||
streams_tab_on_action (enum action action)
 | 
			
		||||
{
 | 
			
		||||
	struct tab *self = g.active_tab;
 | 
			
		||||
	if (self->item_selected < 0 || !self->item_count)
 | 
			
		||||
	struct tab *tab = g.active_tab;
 | 
			
		||||
	if (tab->item_selected < 0 || !tab->item_count)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	// For simplicity the URL is the string following the stream name
 | 
			
		||||
	const char *uri = 1 + strchr (g.streams.vector[self->item_selected], 0);
 | 
			
		||||
	const char *uri = 1 + strchr (g.streams.vector[tab->item_selected], 0);
 | 
			
		||||
 | 
			
		||||
	struct error *e = NULL;
 | 
			
		||||
	switch (action)
 | 
			
		||||
@@ -3053,7 +3106,28 @@ info_tab_init (void)
 | 
			
		||||
 | 
			
		||||
// --- Help tab ----------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
static struct strv g_help_tab_lines;
 | 
			
		||||
static struct
 | 
			
		||||
{
 | 
			
		||||
	struct tab super;                   ///< Parent class
 | 
			
		||||
	ARRAY (enum action, actions)        ///< Actions for content
 | 
			
		||||
	struct strv lines;                  ///< Visible content
 | 
			
		||||
}
 | 
			
		||||
g_help_tab;
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
help_tab_on_action (enum action action)
 | 
			
		||||
{
 | 
			
		||||
	struct tab *tab = &g_help_tab.super;
 | 
			
		||||
	if (tab->item_selected < 0
 | 
			
		||||
	 || tab->item_selected >= (int) g_help_tab.actions_len
 | 
			
		||||
	 || action != ACTION_CHOOSE)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	action = g_help_tab.actions[tab->item_selected];
 | 
			
		||||
	return action != ACTION_NONE
 | 
			
		||||
		&& action != ACTION_CHOOSE  // avoid recursion
 | 
			
		||||
		&& app_process_action (action);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
help_tab_strfkey (const termo_key_t *key, struct strv *out)
 | 
			
		||||
@@ -3071,7 +3145,20 @@ help_tab_strfkey (const termo_key_t *key, struct strv *out)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
help_tab_group (struct binding *keys, size_t len, struct strv *out)
 | 
			
		||||
help_tab_assign_action (enum action action)
 | 
			
		||||
{
 | 
			
		||||
	hard_assert (g_help_tab.lines.len > g_help_tab.actions_len);
 | 
			
		||||
 | 
			
		||||
	size_t to_push = g_help_tab.lines.len - g_help_tab.actions_len;
 | 
			
		||||
	ARRAY_RESERVE (g_help_tab.actions, to_push);
 | 
			
		||||
	for (size_t i = 1; i < to_push; i++)
 | 
			
		||||
		g_help_tab.actions[g_help_tab.actions_len++] = ACTION_NONE;
 | 
			
		||||
	g_help_tab.actions[g_help_tab.actions_len++] = action;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
help_tab_group (struct binding *keys, size_t len, struct strv *out,
 | 
			
		||||
	bool bound[ACTION_COUNT])
 | 
			
		||||
{
 | 
			
		||||
	for (enum action i = 0; i < ACTION_COUNT; i++)
 | 
			
		||||
	{
 | 
			
		||||
@@ -3085,39 +3172,71 @@ help_tab_group (struct binding *keys, size_t len, struct strv *out)
 | 
			
		||||
			strv_append_owned (out, xstrdup_printf
 | 
			
		||||
				("  %-30s %s", g_actions[i].description, joined));
 | 
			
		||||
			free (joined);
 | 
			
		||||
 | 
			
		||||
			bound[i] = true;
 | 
			
		||||
			help_tab_assign_action (i);
 | 
			
		||||
		}
 | 
			
		||||
		strv_free (&ass);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
help_tab_unbound (struct strv *out, bool bound[ACTION_COUNT])
 | 
			
		||||
{
 | 
			
		||||
	for (enum action i = 0; i < ACTION_COUNT; i++)
 | 
			
		||||
		if (!bound[i])
 | 
			
		||||
		{
 | 
			
		||||
			strv_append_owned (out,
 | 
			
		||||
				xstrdup_printf ("  %-30s", g_actions[i].description));
 | 
			
		||||
			help_tab_assign_action (i);
 | 
			
		||||
		}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
help_tab_on_item_draw (size_t item_index, struct row_buffer *buffer, int width)
 | 
			
		||||
{
 | 
			
		||||
	(void) width;
 | 
			
		||||
 | 
			
		||||
	hard_assert (item_index < g_help_tab_lines.len);
 | 
			
		||||
	const char *line = g_help_tab_lines.vector[item_index];
 | 
			
		||||
	hard_assert (item_index < g_help_tab.lines.len);
 | 
			
		||||
	const char *line = g_help_tab.lines.vector[item_index];
 | 
			
		||||
	row_buffer_append (buffer, line, *line == ' ' ? 0 : A_BOLD);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct tab *
 | 
			
		||||
help_tab_init (void)
 | 
			
		||||
{
 | 
			
		||||
	g_help_tab_lines = strv_make ();
 | 
			
		||||
	ARRAY_INIT (g_help_tab.actions);
 | 
			
		||||
	struct strv *lines = &g_help_tab.lines;
 | 
			
		||||
	*lines = strv_make ();
 | 
			
		||||
 | 
			
		||||
	strv_append (&g_help_tab_lines, "Normal mode actions");
 | 
			
		||||
	help_tab_group (g_normal_keys, g_normal_keys_len, &g_help_tab_lines);
 | 
			
		||||
	strv_append (&g_help_tab_lines, "");
 | 
			
		||||
	bool bound[ACTION_COUNT] = { [ACTION_NONE] = true };
 | 
			
		||||
 | 
			
		||||
	strv_append (&g_help_tab_lines, "Editor mode actions");
 | 
			
		||||
	help_tab_group (g_editor_keys, g_editor_keys_len, &g_help_tab_lines);
 | 
			
		||||
	strv_append (&g_help_tab_lines, "");
 | 
			
		||||
	strv_append (lines, "Normal mode actions");
 | 
			
		||||
	help_tab_group (g_normal_keys, g_normal_keys_len, lines, bound);
 | 
			
		||||
	strv_append (lines, "");
 | 
			
		||||
 | 
			
		||||
	static struct tab super;
 | 
			
		||||
	tab_init (&super, "Help");
 | 
			
		||||
	super.on_item_draw = help_tab_on_item_draw;
 | 
			
		||||
	super.item_count = g_help_tab_lines.len;
 | 
			
		||||
	return &super;
 | 
			
		||||
	strv_append (lines, "Editor mode actions");
 | 
			
		||||
	help_tab_group (g_editor_keys, g_editor_keys_len, lines, bound);
 | 
			
		||||
	strv_append (lines, "");
 | 
			
		||||
 | 
			
		||||
	bool have_unbound = false;
 | 
			
		||||
	for (enum action i = 0; i < ACTION_COUNT; i++)
 | 
			
		||||
		if (!bound[i])
 | 
			
		||||
			have_unbound = true;
 | 
			
		||||
 | 
			
		||||
	if (have_unbound)
 | 
			
		||||
	{
 | 
			
		||||
		strv_append (lines, "Unbound actions");
 | 
			
		||||
		help_tab_unbound (lines, bound);
 | 
			
		||||
		strv_append (lines, "");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	struct tab *super = &g_help_tab.super;
 | 
			
		||||
	tab_init (super, "Help");
 | 
			
		||||
	super->on_action = help_tab_on_action;
 | 
			
		||||
	super->on_item_draw = help_tab_on_item_draw;
 | 
			
		||||
	super->item_count = lines->len;
 | 
			
		||||
	return super;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// --- Debug tab ---------------------------------------------------------------
 | 
			
		||||
@@ -3282,7 +3401,7 @@ mpd_update_playback_state (void)
 | 
			
		||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
mpd_process_info (const struct strv *data)
 | 
			
		||||
mpd_process_info_data (const struct strv *data)
 | 
			
		||||
{
 | 
			
		||||
	struct str_map *map = &g.playback_info;
 | 
			
		||||
 | 
			
		||||
@@ -3316,6 +3435,57 @@ mpd_process_info (const struct strv *data)
 | 
			
		||||
	str_map_free (&item);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Find a song by its id in the current playlist.  Expensive, rarely called.
 | 
			
		||||
static ssize_t
 | 
			
		||||
mpd_find_pos_of_id (const char *desired_id)
 | 
			
		||||
{
 | 
			
		||||
	compact_map_t map;
 | 
			
		||||
	const char *id;
 | 
			
		||||
	for (size_t i = 0; i < g.playlist.len; i++)
 | 
			
		||||
	{
 | 
			
		||||
		if ((map = item_list_get (&g.playlist, i))
 | 
			
		||||
		 && (id = compact_map_find (map, "id"))
 | 
			
		||||
		 && !strcmp (id, desired_id))
 | 
			
		||||
			return i;
 | 
			
		||||
	}
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char *
 | 
			
		||||
mpd_id_of_pos (int pos)
 | 
			
		||||
{
 | 
			
		||||
	compact_map_t map = item_list_get (&g.playlist, pos);
 | 
			
		||||
	return map ? compact_map_find (map,  "id") : NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
mpd_process_info (const struct strv *data)
 | 
			
		||||
{
 | 
			
		||||
	int *selected = &g_current_tab.item_selected;
 | 
			
		||||
	int *marked   = &g_current_tab.item_mark;
 | 
			
		||||
	char *prev_sel_id  = mpd_id_of_pos (*selected);
 | 
			
		||||
	char *prev_mark_id = mpd_id_of_pos (*marked);
 | 
			
		||||
	if (prev_sel_id)   prev_sel_id  = xstrdup (prev_sel_id);
 | 
			
		||||
	if (prev_mark_id)  prev_mark_id = xstrdup (prev_mark_id);
 | 
			
		||||
 | 
			
		||||
	mpd_process_info_data (data);
 | 
			
		||||
 | 
			
		||||
	const char *sel_id  = mpd_id_of_pos (*selected);
 | 
			
		||||
	const char *mark_id = mpd_id_of_pos (*marked);
 | 
			
		||||
 | 
			
		||||
	if (prev_mark_id && (!mark_id || strcmp (prev_mark_id, mark_id)))
 | 
			
		||||
		*marked = mpd_find_pos_of_id (prev_mark_id);
 | 
			
		||||
	if (prev_sel_id  && (!sel_id  || strcmp (prev_sel_id,  sel_id)))
 | 
			
		||||
	{
 | 
			
		||||
		if ((*selected = mpd_find_pos_of_id (prev_sel_id)) < 0)
 | 
			
		||||
			*marked = -1;
 | 
			
		||||
		app_move_selection (0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	free (prev_sel_id);
 | 
			
		||||
	free (prev_mark_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
mpd_on_info_response (const struct mpd_response *response,
 | 
			
		||||
	const struct strv *data, void *user_data)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user