chiark / gitweb /
Switch to GPL v3
[disorder] / server / queue-ops.c
1 /*
2  * This file is part of DisOrder.
3  * Copyright (C) 2004-2008 Richard Kettlewell
4  *
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 3 of the License, or
8  * (at your option) any later version.
9  *
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.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 #include "disorder-server.h"
19
20 static int find_in_list(struct queue_entry *needle,
21                         int nqs, struct queue_entry **qs) {
22   int n;
23
24   for(n = 0; n < nqs; ++n)
25     if(qs[n] == needle)
26       return 1;
27   return 0;
28 }
29
30 static int id_in_use(const char *id) {
31   struct queue_entry *q;
32
33   for(q = qhead.next; q != &qhead; q = q->next)
34     if(!strcmp(id, q->id))
35       return 1;
36   return 0;
37 }
38
39 static void queue_id(struct queue_entry *q) {
40   const char *id;
41
42   id = random_id();
43   while(id_in_use(id))
44     id = random_id();
45   q->id = id;
46 }
47
48 struct queue_entry *queue_add(const char *track, const char *submitter,
49                               int where) {
50   struct queue_entry *q, *beforeme;
51
52   q = xmalloc(sizeof *q);
53   q->track = xstrdup(track);
54   q->submitter = submitter ? xstrdup(submitter) : 0;
55   q->state = playing_unplayed;
56   queue_id(q);
57   time(&q->when);
58   switch(where) {
59   case WHERE_START:
60     queue_insert_entry(&qhead, q);
61     break;
62   case WHERE_END:
63     queue_insert_entry(qhead.prev, q);
64     break;
65   case WHERE_BEFORE_RANDOM:
66     /* We want to find the point in the queue before the block of random tracks
67      * at the end. */
68     beforeme = &qhead;
69     while(beforeme->prev != &qhead
70           && beforeme->prev->state == playing_random)
71       beforeme = beforeme->prev;
72     queue_insert_entry(beforeme->prev, q);
73     break;
74   }
75   /* submitter will be a null pointer for a scratch */
76   if(submitter)
77     notify_queue(track, submitter);
78   eventlog_raw("queue", queue_marshall(q), (const char *)0);
79   return q;
80 }
81
82 int queue_move(struct queue_entry *q, int delta, const char *who) {
83   int moved = 0;
84   char buffer[20];
85
86   /* not the most efficient approach but hopefuly relatively comprehensible:
87    * the idea is that for each step we determine which nodes are affected, and
88    * fill in all the links starting at the 'prev' end and moving towards the
89    * 'next' end. */
90   
91   while(delta > 0 && q->prev != &qhead) {
92     struct queue_entry *n, *p, *pp;
93
94     n = q->next;
95     p = q->prev;
96     pp = p->prev;
97     pp->next = q;
98     q->prev = pp;
99     q->next = p;
100     p->prev = q;
101     p->next = n;
102     n->prev = p;
103     --delta;
104     ++moved;
105   }
106
107   while(delta < 0 && q->next != &qhead) {
108     struct queue_entry *n, *p, *nn;
109
110     p = q->prev;
111     n = q->next;
112     nn = n->next;
113     p->next = n;
114     n->prev = p;
115     n->next = q;
116     q->prev = n;
117     q->next = nn;
118     nn->prev = q;
119     ++delta;
120     --moved;
121   }
122
123   if(moved) {
124     info("user %s moved %s", who, q->id);
125     notify_queue_move(q->track, who);
126     sprintf(buffer, "%d", moved);
127     eventlog("moved", who, (char *)0);
128   }
129   
130   return delta;
131 }
132
133 void queue_moveafter(struct queue_entry *target,
134                      int nqs, struct queue_entry **qs,
135                      const char *who) {
136   struct queue_entry *q;
137   int n;
138
139   /* Normalize */
140   if(!target)
141     target = &qhead;
142   else
143     while(find_in_list(target, nqs, qs))
144       target = target->prev;
145   /* Do the move */
146   for(n = 0; n < nqs; ++n) {
147     q = qs[n];
148     queue_delete_entry(q);
149     queue_insert_entry(target, q);
150     target = q;
151     /* Log the individual tracks */
152     info("user %s moved %s", who, q->id);
153     notify_queue_move(q->track, who);
154   }
155   /* Report that the queue changed to the event log */
156   eventlog("moved", who, (char *)0);
157 }
158
159 void queue_remove(struct queue_entry *which, const char *who) {
160   if(who) {
161     info("user %s removed %s", who, which->id);
162     notify_queue_move(which->track, who);
163   }
164   eventlog("removed", which->id, who, (const char *)0);
165   queue_delete_entry(which);
166 }
167
168 void queue_played(struct queue_entry *q) {
169   while(pcount && pcount >= config->history) {
170     eventlog("recent_removed", phead.next->id, (char *)0);
171     queue_delete_entry(phead.next);
172     pcount--;
173   }
174   if(config->history) {
175     eventlog_raw("recent_added", queue_marshall(q), (char *)0);
176     queue_insert_entry(phead.prev, q);
177     ++pcount;
178   }
179 }
180
181 /*
182 Local Variables:
183 c-basic-offset:2
184 comment-column:40
185 fill-column:79
186 indent-tabs-mode:nil
187 End:
188 */