Index: interface.c
===================================================================
--- interface.c	(revision 2161)
+++ interface.c	(working copy)
@@ -60,10 +60,13 @@
 
 #define INTERFACE_LOG	"mocp_client_log"
 
+#define QUEUE_CLEAR_THRESH 128
+
 /* Socket of the server connection. */
 static int srv_sock = -1;
 
 static struct plist *playlist = NULL; /* our playlist */
+static struct plist *queue = NULL; /* our queue */
 static struct plist *dir_plist = NULL; /* content of the current directory */
 
 /* Queue for events comming from the server. */
@@ -242,13 +245,16 @@
 {
 	switch (type) {
 		case EV_PLIST_ADD:
+		case EV_QUEUE_ADD:
 			return recv_item_from_srv ();
 		case EV_PLIST_DEL:
+		case EV_QUEUE_DEL:
 		case EV_STATUS_MSG:
 			return get_str_from_srv ();
 		case EV_FILE_TAGS:
 			return recv_tags_data_from_srv ();
 		case EV_PLIST_MOVE:
+		case EV_QUEUE_MOVE:
 			return recv_move_ev_data_from_srv ();
 	}
 
@@ -325,6 +331,8 @@
 	plist_init (dir_plist);
 	playlist = (struct plist *)xmalloc (sizeof(struct plist));
 	plist_init (playlist);
+	queue = (struct plist *)xmalloc (sizeof(struct plist));
+	plist_init (queue);
 
 	/* set serial numbers for the playlist */
 	send_int_to_srv (CMD_GET_SERIAL);
@@ -849,6 +857,20 @@
 	}
 }
 
+/* Handle EV_QUEUE_ADD. */
+static void event_queue_add (const struct plist_item *item)
+{
+	if (plist_find_fname(queue, item->file) == -1) {
+		int added; /* TODO: remove/tags/interface? */
+
+		added = plist_add_from_item (queue, item);
+		iface_set_files_in_queue (plist_count(queue));
+		logit ("Adding %s to queue", item->file);
+	}
+	else
+		logit ("Adding file already present in queue");
+}
+
 /* Get error message from the server and show it. */
 static void update_error ()
 {
@@ -912,6 +934,27 @@
 	return 1;
 }
 
+static void recv_server_queue (struct plist *queue)
+{
+	int end_of_list = 0;
+	struct plist_item *item;
+
+	logit ("Asking server for the queue.");
+	send_int_to_srv (CMD_GET_QUEUE);
+	logit ("Waiting for response");
+	wait_for_data (); /* There must always be (possibly empty) queue. */
+
+	do {
+		item = recv_item_from_srv ();
+		if (item->file[0])
+			plist_add_from_item (queue, item);
+		else
+			end_of_list = 1;
+		plist_free_item_fields (item);
+		free (item);
+	} while (!end_of_list);
+}
+
 /* Clear the playlist locally */
 static void clear_playlist ()
 {
@@ -925,6 +968,14 @@
 	iface_set_status ("");
 }
 
+static void clear_queue ()
+{
+	plist_clear (queue);
+	iface_set_files_in_queue (0);
+
+	interface_message ("The queue was cleared.");
+}
+
 /* Handle EV_PLIST_DEL. */
 static void event_plist_del (char *file)
 {
@@ -953,6 +1004,29 @@
 				" playlist.");
 }
 
+/* Handle EV_QUEUE_DEL. */
+static void event_queue_del (char *file)
+{
+	int item = plist_find_fname (queue, file);
+
+	if (item != -1) {
+		plist_delete (queue, item);
+
+		/* Free the deleted items occasionally.
+		 * QUEUE_CLEAR_THRESH is chosen to be two times
+		 * the initial size of the playlist */
+		if (plist_count(queue) == 0
+				&& queue->num >= QUEUE_CLEAR_THRESH)
+			plist_clear (queue);
+
+		iface_set_files_in_queue (plist_count(queue));
+		logit ("Deleting %s from queue", file);
+	}
+	else
+		logit ("Deleting an item not present in the queue");
+
+}
+
 /* Swap 2 file on the playlist. */
 static void swap_playlist_items (const char *file1, const char *file2)
 {
@@ -973,6 +1047,18 @@
 	swap_playlist_items (d->from, d->to);
 }
 
+/* Handle EV_QUEUE_MOVE. */
+/* Unused for now, might be useful later */
+static void event_queue_move (const struct move_ev_data *d)
+{
+	assert (d != NULL);
+	assert (d->from != NULL);
+	assert (d->to != NULL);
+
+	plist_swap_files (queue, d->from, d->to);
+	/* TODO interface update */
+}
+
 /* Handle server event. */
 static void server_event (const int event, void *data)
 {
@@ -1013,7 +1099,6 @@
 		case EV_PLIST_ADD:
 			if (options_get_int("SyncPlaylist"))
 				event_plist_add ((struct plist_item *)data);
-			plist_free_item_fields (data);
 			break;
 		case EV_PLIST_CLEAR:
 			if (options_get_int("SyncPlaylist"))
@@ -1042,6 +1127,18 @@
 		case EV_AVG_BITRATE:
 			curr_file.avg_bitrate = get_avg_bitrate ();
 			break;
+		case EV_QUEUE_ADD:
+			event_queue_add ((struct plist_item *)data);
+			break;
+		case EV_QUEUE_DEL:
+			event_queue_del ((char *)data);
+			break;
+		case EV_QUEUE_CLEAR:
+			clear_queue ();
+			break;
+		case EV_QUEUE_MOVE:
+			event_queue_move ((struct move_ev_data *)data);
+			break;
 		default:
 			interface_fatal ("Unknown event: 0x%02x", event);
 	}
@@ -1313,6 +1410,17 @@
 	return 0;
 }
 
+static void use_server_queue ()
+{
+	iface_set_status ("Getting the queue...");
+	debug ("Getting the queue...");
+
+	recv_server_queue(queue);
+	/* TODO: tags? */
+	iface_set_files_in_queue (plist_count(queue));
+	iface_set_status ("");
+}
+
 /* Process file names passwd as arguments. */
 static void process_args (char **args, const int num)
 {
@@ -1482,7 +1590,10 @@
 			load_playlist ();
 		enter_first_dir ();
 	}
-	
+
+	/* Ask the server for queue. */
+	use_server_queue ();
+
 	if (options_get_int("SyncPlaylist"))
 		send_int_to_srv (CMD_CAN_SEND_PLIST);
 
@@ -1861,6 +1972,59 @@
 	free (file);
 }
 
+static void queue_toggle_file ()
+{
+	char *file;
+
+	file = iface_get_curr_file ();
+
+	if (!file)
+		return;
+
+	if (iface_curritem_get_type() != F_SOUND) {
+		/* TODO: Why not url? */
+		error ("You can only add a file using this command.");
+		free (file);
+		return;
+	}
+
+	/* check if the file is already in the queue; if it isn't, add it,
+	 * othrewise, remove it */
+
+	send_int_to_srv (CMD_LOCK); /* TODO does this lock make sense? */
+
+	if (plist_find_fname(queue, file) == -1) {
+
+		struct plist_item *item;
+		struct plist *iface_plist;
+
+		assert (iface_in_plist_menu() || iface_in_dir_menu());
+		iface_plist = (iface_in_plist_menu() ? playlist : dir_plist);
+
+		int i = plist_find_fname (iface_plist, file);
+		assert (i != -1);
+
+		item = &iface_plist->items[i];
+
+		/* Add item to the server's queue */
+		send_int_to_srv (CMD_QUEUE_ADD);
+		send_str_to_srv (file);
+
+		logit ("Added to queue: %s", file);
+	}
+	else {
+		/* Delete this item from the server's playlist */
+		send_int_to_srv (CMD_QUEUE_DEL);
+		send_str_to_srv (file);
+
+		logit ("Removed from queue: %s", file);
+	}
+
+	send_int_to_srv (CMD_UNLOCK);
+
+	free (file);
+}
+
 static void toggle_option (const char *name)
 {
 	send_int_to_srv (CMD_SET_OPTION);
@@ -1922,6 +2086,11 @@
 		clear_playlist ();
 }
 
+static void cmd_clear_queue ()
+{
+	send_int_to_srv (CMD_QUEUE_CLEAR);
+}
+
 static void go_to_music_dir ()
 {
 	if (options_get_str("MusicDir")) {
@@ -3301,6 +3470,12 @@
 			case KEY_CMD_EXEC10:
 				exec_custom_command ("ExecCommand10");
 				break;
+			case KEY_CMD_QUEUE_TOGGLE_FILE:
+				queue_toggle_file ();
+				break;
+			case KEY_CMD_QUEUE_CLEAR:
+				cmd_clear_queue ();
+				break;
 			default:
 				abort ();
 		}
@@ -3436,8 +3611,10 @@
 
 	plist_free (dir_plist);
 	plist_free (playlist);
+	plist_free (queue);
 	free (dir_plist);
 	free (playlist);
+	free (queue);
 
 	event_queue_free (&events);
 	
@@ -3786,6 +3963,7 @@
 
 	plist_free (dir_plist);
 	plist_free (playlist);
+	plist_free (queue);
 }
 
 void interface_cmdline_playit (int server_sock, char **args, const int arg_num)
@@ -4100,6 +4278,7 @@
 
 	plist_free (dir_plist);
 	plist_free (playlist);
+	plist_free (queue);
 
 	printf("%s\n", str);
 	free(str);
Index: keys.c
===================================================================
--- keys.c	(revision 2161)
+++ keys.c	(working copy)
@@ -807,7 +807,23 @@
  		CON_MENU,
  		{ 'P', -1 },
  		1
-  	}
+  	},
+	{
+		KEY_CMD_QUEUE_TOGGLE_FILE,
+		"enqueue_file",
+		"Add (or remove) a file to (from) queue",
+		CON_MENU,
+		{ 'z', -1 },
+		1
+	},
+	{
+		KEY_CMD_QUEUE_CLEAR,
+		"clear_queue",
+		"Clear the queue",
+		CON_MENU,
+		{ 'Z', -1 },
+		1
+	}
 };
 
 static struct special_keys
Index: keys.h
===================================================================
--- keys.h	(revision 2161)
+++ keys.h	(working copy)
@@ -97,6 +97,8 @@
         KEY_CMD_EQUALIZER_NEXT,
         KEY_CMD_TOGGLE_MAKE_MONO,
 	KEY_CMD_LYRICS,
+	KEY_CMD_QUEUE_TOGGLE_FILE,
+	KEY_CMD_QUEUE_CLEAR,
 	KEY_CMD_WRONG
 };
 
Index: keymap.example
===================================================================
--- keymap.example	(revision 2161)
+++ keymap.example	(working copy)
@@ -119,6 +119,16 @@
 plist_move_down =	j
 theme_menu =		T
 
+toggle_equalizer =	E
+equalizer_refresh =	e
+equalizer_prev =	K
+equalizer_next =	k
+toggle_make_mono =	J
+show_lyrics =		L
+playlist_full_paths =	P
+enqueue_file =		z
+clear_queue =		Z
+
 exec_command1 = 	F1
 exec_command2 = 	F2
 exec_command3 = 	F3
Index: config.example
===================================================================
--- config.example	(revision 2161)
+++ config.example	(working copy)
@@ -417,3 +417,11 @@
 # to stopped (when user stopped playing or changes a song).
 #
 #OnStop = "/home/jack/.moc/myscript_on_stop"
+
+# This options determines what song to play after finishing all the songs in
+# queue. Setting this to 1 causes MOC to play the song which follows the song
+# played before playing the queue. If set to 0, MOC will play the song after the
+# last song in the queue if it was in the playlist. If it wasn't, MOC will
+# choose the song as if this option was set to 1. Other players usually behave
+# like this, so it's the default.
+#QueueNextSongReturn = 0
Index: interface_elements.c
===================================================================
--- interface_elements.c	(revision 2161)
+++ interface_elements.c	(working copy)
@@ -206,6 +206,8 @@
 	int bitrate;		/* in kbps */
 	int rate;		/* in kHz */
 
+	int files_in_queue;
+
 	/* time in seconds */
 	int curr_time;
 	int total_time;
@@ -2514,6 +2516,8 @@
 	w->bitrate = -1;
 	w->rate = -1;
 
+	w->files_in_queue = 0;
+
 	w->curr_time = -1;
 	w->total_time = -1;
 
@@ -2593,6 +2597,37 @@
 	info_win_draw_status (w);
 }
 
+static void info_win_draw_files_in_queue (const struct info_win *w)
+{
+	assert (w != NULL);
+
+	const int hstart = 5 + sizeof(w->status_msg) + 2;
+
+	if(!w->in_entry && !w->too_small) {
+		if (w->files_in_queue) {
+			wattrset (w->win, get_color(CLR_STATUS));
+			mvwaddch (w->win, 0, hstart, lines.rtee);
+			xwprintw (w->win, "Q:%3d", w->files_in_queue);
+			waddch (w->win, lines.ltee);
+		}
+		else {
+			wattrset (w->win, get_color(CLR_FRAME));
+			mvwhline (w->win, 0, hstart, lines.horiz, 9);
+		}
+	}
+
+	info_win_update_curs (w);
+}
+
+static void info_win_set_files_in_queue (struct info_win *w, const int num)
+{
+	assert (w != NULL);
+	assert (num >= 0);
+
+	w->files_in_queue = num;
+	info_win_draw_files_in_queue (w);
+}
+
 static void info_win_draw_state (const struct info_win *w)
 {
 	const char *state_symbol;
@@ -3017,6 +3052,7 @@
 		info_win_draw_title (w);
 		info_win_draw_options_state (w);
 		info_win_draw_status (w);
+		info_win_draw_files_in_queue (w);
 		info_win_draw_files_time (w);
 		info_win_draw_bitrate (w);
 		info_win_draw_rate (w);
@@ -3293,6 +3329,17 @@
 	}
 }
 
+/* Set the number of files in song queue in the info window */
+void iface_set_files_in_queue (const int num)
+{
+	assert (num >= 0);
+
+	if (iface_initialized) {
+		info_win_set_files_in_queue (&info_win, num);
+		iface_refresh_screen ();
+	}
+}
+
 static void iface_show_num_files (const int num)
 {
 	char str[20];
Index: interface_elements.h
===================================================================
--- interface_elements.h	(revision 2161)
+++ interface_elements.h	(working copy)
@@ -71,6 +71,7 @@
 void iface_set_played_file (const char *file);
 void iface_set_played_file_title (const char *title);
 void iface_set_mixer_value (const int value);
+void iface_set_files_in_queue (const int num);
 void iface_tick ();
 void iface_switch_to_plist ();
 void iface_switch_to_dir ();
Index: protocol.c
===================================================================
--- protocol.c	(revision 2161)
+++ protocol.c	(working copy)
@@ -585,15 +585,16 @@
 /* Free data associated with the event if any. */
 void free_event_data (const int type, void *data)
 {
-	if (type == EV_PLIST_ADD) {
+	if (type == EV_PLIST_ADD || type == EV_QUEUE_ADD) {
 		plist_free_item_fields ((struct plist_item *)data);
 		free (data);
 	}
 	else if (type == EV_FILE_TAGS)
 		free_tag_ev_data ((struct tag_ev_response *)data);
-	else if (type == EV_PLIST_DEL || type == EV_STATUS_MSG)
+	else if (type == EV_PLIST_DEL || type == EV_STATUS_MSG
+			|| type == EV_QUEUE_DEL)
 		free (data);
-	else if (type == EV_PLIST_MOVE)
+	else if (type == EV_PLIST_MOVE || type == EV_QUEUE_MOVE)
 		free_move_ev_data ((struct move_ev_data *)data);
 	else if (data)
 		abort (); /* BUG */
@@ -658,11 +659,13 @@
 
 	packet_buf_add_int (b, e->type);
 
-	if (e->type == EV_PLIST_DEL || e->type == EV_STATUS_MSG) {
+	if (e->type == EV_PLIST_DEL
+			|| e->type == EV_QUEUE_DEL
+			|| e->type == EV_STATUS_MSG) {
 		assert (e->data != NULL);
 		packet_buf_add_str (b, e->data);
 	}
-	else if (e->type == EV_PLIST_ADD) {
+	else if (e->type == EV_PLIST_ADD || e->type == EV_QUEUE_ADD) {
 		assert (e->data != NULL);
 		packet_buf_add_item (b, e->data);
 	}
@@ -675,7 +678,7 @@
 		packet_buf_add_str (b, r->file);
 		packet_buf_add_tags (b, r->tags);
 	}
-	else if (e->type == EV_PLIST_MOVE) {
+	else if (e->type == EV_PLIST_MOVE || e->type == EV_QUEUE_MOVE) {
 		struct move_ev_data *m;
 		
 		assert (e->data != NULL);
Index: audio.c
===================================================================
--- audio.c	(revision 2161)
+++ audio.c	(working copy)
@@ -62,7 +62,13 @@
 
 /* currentlu played file */
 static int curr_playing = -1;
+/* file we played before playing songs from queue */
+static char *before_queue_fname = NULL;
 static char *curr_playing_fname = NULL;
+/* This flag is set 1 if audio_play() was called with nonempty queue,
+ * so we know that when the queue is empty, we should play the regular
+ * playlist from the beginning */
+static int started_playing_in_queue = 0;
 static pthread_mutex_t curr_playing_mut = PTHREAD_MUTEX_INITIALIZER;
 
 static struct out_buf out_buf;
@@ -82,6 +88,7 @@
 /* Playlists. */
 static struct plist playlist;
 static struct plist shuffled_plist;
+static struct plist queue;
 static struct plist *curr_plist; /* currently used playlist */
 static pthread_mutex_t plist_mut = PTHREAD_MUTEX_INITIALIZER;
 
@@ -278,108 +285,128 @@
 	return Bps;
 }
 
-/* Move to the next file depending on set options and the user request. */
+/* Move to the next file depending on set options, the user request and
+ * whether there are files in queue or not. */
 static void go_to_another_file ()
 {
-	int shuffle = options_get_int("Shuffle");
-	
+	int shuffle = options_get_int ("Shuffle");
+	int go_next = (play_next || options_get_int("AutoNext"));
+	int curr_playing_curr_pos;
+	/* shouldn't play_next be protected by mutex? */
+
 	LOCK (curr_playing_mut);
 	LOCK (plist_mut);
-	
-	if (shuffle && plist_count(&playlist)
-			&& !plist_count(&shuffled_plist)) {
-		plist_cat (&shuffled_plist, &playlist);
-		plist_shuffle (&shuffled_plist);
-		if (curr_playing != -1)
-			plist_swap_first_fname (&shuffled_plist,
-					plist_get_file(curr_plist,
-						curr_playing));
-	}
 
-	/* If Shuffle was switched while playing, we must correct curr_playing
-	 * by searching for the current item on the list where we are
-	 * switching. */
-	if (shuffle && curr_plist != &shuffled_plist) {
-		char *file = plist_get_file (&playlist, curr_playing);
+	/* If we move forward in the playlist and there are some songs in
+	 * the queue, play them */
+	if (plist_count(&queue) && go_next) {
+		logit ("Playing file from queue");
 
-		if (file) {
-			curr_playing = plist_find_del_fname (&shuffled_plist,
-					file);
-			free (file);
+		if (!before_queue_fname && curr_playing_fname) {
+			before_queue_fname = xstrdup (curr_playing_fname);
 		}
+
+		curr_plist = &queue;
+		curr_playing = plist_next (&queue, -1);
+
+		server_queue_pop (queue.items[curr_playing].file);
+		plist_delete (&queue, curr_playing);
 	}
-	else if (!shuffle && curr_plist != &playlist) {
-		char *file = plist_get_file (&shuffled_plist, curr_playing);
-
-		if (file) {
-			curr_playing = plist_find_del_fname (&playlist,
-					file);
-			free (file);
+	else {
+		/* If we just finished playing files from the queue and the
+		 * appropriate option is set, continue with the file played
+		 * before playing queue.
+		 * */
+		if (before_queue_fname && options_get_int("QueueNextSongReturn")) {
+			free (curr_playing_fname);
+			curr_playing_fname = before_queue_fname;
+			before_queue_fname = NULL;
 		}
-	}
-	
-	if (shuffle)
-		curr_plist = &shuffled_plist;
-	else
-		curr_plist = &playlist;
 
-	/* If curr_playing == -1 now, the playlist is empty.
-	 * 
-	 * Order of files on the playlist could have been changed, so we can't
-	 * just use plist_prev()/plist_next(), we must first find the current
-	 * position of the last played file. */
-	
-	if (play_prev == 1 && curr_playing != -1) { 
-		int curr_playing_current_pos;
+		if (shuffle) {
+			curr_plist = &shuffled_plist;
 
-		logit ("Playing previous...");
-		
-		curr_playing_current_pos = plist_find_fname (curr_plist,
-				curr_playing_fname);
+			if (plist_count(&playlist)
+					&& !plist_count(&shuffled_plist)) {
+				plist_cat (&shuffled_plist, &playlist);
+				plist_shuffle (&shuffled_plist);
 
-		if (curr_playing_current_pos != -1)
-			curr_playing = plist_prev (curr_plist,
-					curr_playing_current_pos);
+				if (curr_playing_fname)
+					plist_swap_first_fname (&shuffled_plist,
+							curr_playing_fname);
+			}
+		}
 		else
-			curr_playing = plist_prev (curr_plist, curr_playing);
-		if (curr_playing == -1) {
-			if (options_get_int("Repeat"))
-				curr_playing = plist_last (curr_plist);
-			logit ("Beginning of the list.");
-		}
-		else 
-			logit ("Previous item.");
-	}
-	else if (curr_playing != -1
-			&& (options_get_int("AutoNext") || play_next)) {
-		int curr_playing_current_pos;
+			curr_plist = &playlist;
 
-		curr_playing_current_pos = plist_find_fname (curr_plist,
+		curr_playing_curr_pos = plist_find_fname (curr_plist,
 				curr_playing_fname);
 
-		if (curr_playing_current_pos != -1)
-			curr_playing = plist_next (curr_plist,
-					curr_playing_current_pos);
-		else
-			curr_playing = plist_next (curr_plist, curr_playing);
-		if (curr_playing == -1 && options_get_int("Repeat")) {
-			if (shuffle) {
-				plist_clear (&shuffled_plist);
-				plist_cat (&shuffled_plist, &playlist);
-				plist_shuffle (&shuffled_plist);
+		/* If we came from the queue and the last file in
+		 * queue wasn't in the playlist, we try to revert to
+		 * the QueueNextSongReturn = 1 behaviour. */
+		if (curr_playing_curr_pos == -1 && before_queue_fname) {
+			curr_playing_curr_pos = plist_find_fname (curr_plist,
+					before_queue_fname);
+		}
+
+		if (play_prev && plist_count(curr_plist)) {
+			logit ("Playing previous...");
+
+			if (curr_playing_curr_pos == -1
+					|| started_playing_in_queue) {
+				curr_playing = plist_prev (curr_plist, -1);
+				started_playing_in_queue = 0;
 			}
-			curr_playing = plist_next (curr_plist, -1);
-			logit ("Going back to the first item.");
+			else
+				curr_playing = plist_prev (curr_plist,
+						curr_playing_curr_pos);
+
+			if (curr_playing == -1) {
+				if (options_get_int("Repeat"))
+					curr_playing = plist_last (curr_plist);
+				logit ("Beginning of the list.");
+			}
+			else
+				logit ("Previous item.");
 		}
-		else if (curr_playing == -1)
-			logit ("End of the list.");
+		else if (go_next && plist_count(curr_plist)) {
+			logit ("Playing next...");
+
+			if (curr_playing_curr_pos == -1
+					|| started_playing_in_queue) {
+				curr_playing = plist_next (curr_plist, -1);
+				started_playing_in_queue = 0;
+			}
+			else
+				curr_playing = plist_next (curr_plist,
+						curr_playing_curr_pos);
+
+			if (curr_playing == -1 && options_get_int("Repeat")) {
+				if (shuffle) {
+					plist_clear (&shuffled_plist);
+					plist_cat (&shuffled_plist, &playlist);
+					plist_shuffle (&shuffled_plist);
+				}
+				curr_playing = plist_next (curr_plist, -1);
+				logit ("Going back to the first item.");
+			}
+			else if (curr_playing == -1)
+				logit ("End of the list");
+			else
+				logit ("Next item");
+
+		}
+		else if (!options_get_int("Repeat")) {
+			curr_playing = -1;
+		}
 		else
-			logit ("Next item.");
+			debug ("Repeating file");
+
+		if (before_queue_fname)
+			free (before_queue_fname);
+		before_queue_fname = NULL;
 	}
-	else if (!options_get_int("Repeat"))
-		curr_playing = -1;
-	else
-		debug ("Repeating file");
 
 	UNLOCK (plist_mut);
 	UNLOCK (curr_playing_mut);
@@ -507,7 +534,21 @@
 	
 	LOCK (curr_playing_mut);
 	LOCK (plist_mut);
-	if (options_get_int("Shuffle")) {
+
+	/* If we have songs in the queue and fname is empty string, start
+	 * playing file from the queue */
+	if (plist_count(&queue) && !(*fname)) {
+		curr_plist = &queue;
+		curr_playing = plist_next (&queue, -1);
+
+		/* remove the file from queue */
+		server_queue_pop (queue.items[curr_playing].file);
+		plist_delete (curr_plist, curr_playing);
+
+		started_playing_in_queue = 1;
+
+	}
+	else if (options_get_int("Shuffle")) {
 		plist_clear (&shuffled_plist);
 		plist_cat (&shuffled_plist, &playlist);
 		plist_shuffle (&shuffled_plist);
@@ -918,6 +959,7 @@
 
         plist_init (&playlist);
 	plist_init (&shuffled_plist);
+	plist_init (&queue);
 	player_init ();
 }
 
@@ -929,6 +971,7 @@
 	out_buf_destroy (&out_buf);
 	plist_free (&playlist);
 	plist_free (&shuffled_plist);
+	plist_free (&queue);
 	player_cleanup ();
 	if (pthread_mutex_destroy(&curr_playing_mut))
 		logit ("Can't destroy curr_playing_mut: %s", strerror(errno));
@@ -988,6 +1031,17 @@
 	UNLOCK (plist_mut);
 }
 
+void audio_queue_add (const char *file)
+{
+	LOCK (plist_mut);
+	if (plist_find_fname(&queue, file) == -1)
+		plist_add (&queue, file);
+	else
+		logit ("Wanted to add a file that is already present in the "
+				"queue: %s", file);
+	UNLOCK (plist_mut);
+}
+
 void audio_plist_clear ()
 {
 	LOCK (plist_mut);
@@ -996,6 +1050,13 @@
 	UNLOCK (plist_mut);
 }
 
+void audio_queue_clear ()
+{
+	LOCK (plist_mut);
+	plist_clear (&queue);
+	UNLOCK (plist_mut);
+}
+
 /* Returned memory is malloc()ed. */
 char *audio_get_sname ()
 {
@@ -1044,6 +1105,17 @@
 	UNLOCK (plist_mut);
 }
 
+void audio_queue_delete (const char *file)
+{
+	int num;
+
+	LOCK (plist_mut);
+	num = plist_find_fname (&queue, file);
+	if (num != -1)
+		plist_delete (&queue, num);
+	UNLOCK (plist_mut);
+}
+
 /* Get the time of a file if it is on the playlist and the time is avilable. */
 int audio_get_ftime (const char *file)
 {
@@ -1117,6 +1189,28 @@
 	UNLOCK (plist_mut);
 }
 
+void audio_queue_move (const char *file1, const char *file2)
+{
+	LOCK (plist_mut);
+	plist_swap_files (&queue, file1, file2);
+	UNLOCK (plist_mut);
+}
+
+/* Return copy of the song queue. We cannot just return constant
+ * pointer, because it will be used in different thread.
+ * It obviously needs to be freed after use. */
+struct plist* audio_queue_get_contents ()
+{
+	struct plist *ret = (struct plist *)xmalloc (sizeof(struct plist));
+	plist_init (ret);
+
+	LOCK (plist_mut);
+	plist_cat (ret, &queue);
+	UNLOCK (plist_mut);
+
+	return ret;
+}
+
 struct file_tags *audio_get_curr_tags ()
 {
 	return player_get_curr_tags ();
Index: protocol.h
===================================================================
--- protocol.h	(revision 2161)
+++ protocol.h	(working copy)
@@ -65,6 +65,13 @@
 #define EV_PLIST_MOVE	0x52 /* move an item, followed by 2 file names */
 #define EV_PLIST_CLEAR	0x53 /* clear the playlist */
 
+/* These events, though similar to the four previous are caused by server
+ * which takes care of clients' queue synchronization. */
+#define EV_QUEUE_ADD	0x54
+#define EV_QUEUE_DEL	0x55
+#define EV_QUEUE_MOVE	0x56
+#define EV_QUEUE_CLEAR	0x57
+
 /* State of the server. */
 #define STATE_PLAY	0x01
 #define STATE_STOP	0x02
@@ -129,6 +136,11 @@
 
 #define CMD_TOGGLE_MAKE_MONO    0x39    /* toggle mono mixing */
 #define CMD_JUMP_TO	0x3a /* jumps to a some position in the current stream */
+#define CMD_QUEUE_ADD	0x3b /* add an item to the queue */
+#define CMD_QUEUE_DEL	0x3c /* delete an item from the queue */
+#define CMD_QUEUE_MOVE	0x3d /* move an item in the queue */
+#define CMD_QUEUE_CLEAR	0x3e /* clear the queue */
+#define CMD_GET_QUEUE	0x3f /* request the queue from the server */
 
 char *socket_name ();
 int get_int (int sock, int *i);
Index: audio.h
===================================================================
--- audio.h	(revision 2161)
+++ audio.h	(working copy)
@@ -251,5 +251,10 @@
 char *audio_get_mixer_channel_name ();
 void audio_toggle_mixer_channel ();
 void audio_plist_move (const char *file1, const char *file2);
+void audio_queue_add (const char *file);
+void audio_queue_delete (const char *file);
+void audio_queue_clear ();
+void audio_queue_move (const char *file1, const char *file2);
+struct plist* audio_queue_get_contents ();
 
 #endif
Index: server.c
===================================================================
--- server.c	(revision 2161)
+++ server.c	(working copy)
@@ -534,15 +534,18 @@
 			void *data_copy = NULL;
 
 			if (data) {
-				if (event == EV_PLIST_ADD) {
+				if (event == EV_PLIST_ADD
+						|| event == EV_QUEUE_ADD) {
 					data_copy = plist_new_item ();
 					plist_item_copy (data_copy, data);
 				}
 				else if (event == EV_PLIST_DEL
+						|| event == EV_QUEUE_DEL
 						|| event == EV_STATUS_MSG) {
 					data_copy = xstrdup (data);
 				}
-				else if (event == EV_PLIST_MOVE)
+				else if (event == EV_PLIST_MOVE
+						|| event == EV_QUEUE_MOVE)
 					data_copy = move_ev_data_dup (
 							(struct move_ev_data *)
 							data);
@@ -633,6 +636,40 @@
 	return 1;
 }
 
+/* Handle CMD_QUEUE_ADD, return 1 if ok or 0 on error. */
+static int req_queue_add (const struct client *cli)
+{
+	char *file;
+	struct plist_item *item;
+
+	file = get_str (cli->socket);
+	if (!file)
+		return 0;
+
+	logit ("Adding '%s' to the queue", file);
+
+	audio_queue_add (file);
+
+	/* We'll wrap the filename in struct plist_item.
+	 * TODO: fill in the tags (if possible synchronously)
+	 * TODO: reuse code from plist_add?
+	 * it's ultra ugly, but hopefuly temporary ...
+	 */
+
+	item = plist_new_item ();
+
+	item->file = xstrdup (file);
+	item->type = F_SOUND; /* XXX streams? */
+
+	add_event_all (EV_QUEUE_ADD, item);
+
+	plist_free_item_fields (item);
+	free (item);
+	free (file);
+
+	return 1;
+}
+
 /* Handle CMD_PLAY, return 1 if ok or 0 on error. */
 static int req_play (struct client *cli)
 {
@@ -783,6 +820,23 @@
 	return 1;
 }
 
+static int req_queue_del (const struct client *cli)
+{
+	char *file;
+
+	if (!(file = get_str(cli->socket)))
+		return 0;
+
+	debug ("Deleting '%s' from queue", file);
+
+	audio_queue_delete (file);
+	/* XXX similar remarks as in req_queue_add apply */
+	add_event_all (EV_QUEUE_DEL, file);
+	free (file);
+
+	return 1;
+}
+
 /* Return the index of the first client able to send the playlist or -1 if
  * there isn't any. */
 static int find_sending_plist ()
@@ -911,6 +965,50 @@
 	return item ? 1 : 0;
 }
 
+/* Client requested to send queue so we get it from audio.c and
+ * send it to the client */
+static int req_send_queue (struct client *cli)
+{
+	int i;
+	struct plist *queue;
+
+	logit ("Client with fd %d wants queue ... sending it", cli->socket);
+
+	if (!send_int(cli->socket, EV_DATA)) {
+		logit ("Error while sending response, disconnecting"
+				" the client.");
+		close (cli->socket);
+		del_client (cli);
+		return 0;
+	}
+
+	queue = audio_queue_get_contents ();
+
+	for (i = 0; i < queue->num; i++)
+		if (!plist_deleted(queue, i)) {
+			if(!send_item(cli->socket, &queue->items[i])){
+				logit ("Error sending queue, disconnecting"
+						" the client.");
+				close (cli->socket);
+				del_client (cli);
+				return 0;
+			}
+		}
+
+	plist_free (queue);
+
+	if (!send_item (cli->socket, NULL)) {
+		logit ("Error while sending end of playlist mark, disconnecting"
+				" the client.");
+		close (cli->socket);
+		del_client (cli);
+		return 0;
+	}
+
+	logit ("Queue sent");
+	return 1;
+}
+
 /* Handle command that synchinize playlists between interfaces (except
  * forwarding the whole list). Return 0 on error. */
 static int plist_sync_cmd (struct client *cli, const int cmd)
@@ -1200,6 +1298,34 @@
 	return 1;
 }
 
+/* Handle CMD_QUEUE_MOVE. Return 0 on error. */
+static int req_queue_move (const struct client *cli)
+{
+	/*char *from;
+	char *to;
+	*/
+	struct move_ev_data m;
+
+	if (!(m.from = get_str(cli->socket)))
+		return 0;
+	if (!(m.to = get_str(cli->socket))) {
+		free (m.from);
+		return 0;
+	}
+
+	audio_queue_move (m.from, m.to);
+
+	logit ("Swapping %s with %s in the queue", m.from, m.to);
+
+	/* Broadcast the event to clients */
+	add_event_all (EV_QUEUE_MOVE, &m);
+
+	free (m.from);
+	free (m.to);
+
+	return 1;
+}
+
 /* Reveive a command from the client and execute it. */
 static void handle_command (const int client_id)
 {
@@ -1398,6 +1524,27 @@
                 case CMD_TOGGLE_MAKE_MONO:
                         req_toggle_make_mono();
                         break;
+		case CMD_QUEUE_ADD:
+			if (!req_queue_add(cli))
+				err = 1;
+			break;
+		case CMD_QUEUE_DEL:
+			if (!req_queue_del(cli))
+				err = 1;
+			break;
+		case CMD_QUEUE_CLEAR:
+			logit ("Clearing the queue");
+			audio_queue_clear ();
+			add_event_all (EV_QUEUE_CLEAR, NULL);
+			break;
+		case CMD_QUEUE_MOVE:
+			if (!req_queue_move(cli))
+				err = 1;
+			break;
+		case CMD_GET_QUEUE:
+			if (!req_send_queue(cli))
+				err = 1;
+			break;
 		default:
 			logit ("Bad command (0x%x) from the client.", cmd);
 			err = 1;
@@ -1607,3 +1754,14 @@
 		wake_up_server ();
 	}
 }
+
+/* Announce to clients that first file from the queue is being played
+ * and therefore needs to be removed from it */
+/* XXX: this function is called from player thread and add_event_all
+ *      imho doesn't properly lock all shared variables -- possible
+ *      race condition??? */
+void server_queue_pop (const char *filename)
+{
+	debug ("Queue pop -- broadcasting EV_QUEUE_DEL");
+	add_event_all (EV_QUEUE_DEL, filename);
+}
Index: server.h
===================================================================
--- server.h	(revision 2161)
+++ server.h	(working copy)
@@ -18,5 +18,6 @@
 void status_msg (const char *msg);
 void tags_response (const int client_id, const char *file,
 		const struct file_tags *tags);
+void server_queue_pop (const char *filename);
 
 #endif
Index: options.c
===================================================================
--- options.c	(revision 2161)
+++ options.c	(working copy)
@@ -313,6 +313,8 @@
         
         option_add_int ("Equalizer_SaveState", 1);
 
+	option_add_int ("QueueNextSongReturn", 0);
+
 }
 
 /* Return 1 if a parameter to an integer option is valid. */

