defuze.me
Client
|
00001 /************************************************************************** 00002 ** defuze.me Epitech Innovative Project 00003 ** 00004 ** Copyright 2010 00005 ** Athena Calmettes - Jocelyn De La Rosa - Francois Gaillard 00006 ** Adrien Jarthon - Alexandre Moore - Luc Peres - Arnaud Sellier 00007 ** 00008 ** All rights reserved. 00009 **************************************************************************/ 00010 00011 #include "playqueue.hpp" 00012 #include "audiotrack.hpp" 00013 #include "queuebreak.hpp" 00014 #include "queuetrack.hpp" 00015 #include "playqueuewidget.hpp" 00016 #include "jsonparser.hpp" 00017 #include "exception.hpp" 00018 #include "scheduler.hpp" 00019 #include "listsplugin.hpp" 00020 #include <QSqlQuery> 00021 #include <QSqlError> 00022 #include <QDebug> 00023 #include <QDateTime> 00024 #include <algorithm> 00025 00026 using namespace Queue; 00027 00028 PlayQueue::PlayQueue(QObject *parent) : 00029 QAbstractListModel(parent), altering(false) 00030 { 00031 widget = new PlayQueueWidget(this); 00032 connect(widget, SIGNAL(deleteElements()), SLOT(removeSelected())); 00033 connect(widget, SIGNAL(clearListFocus()), SLOT(closeControls())); 00034 uiModule = Gui::ModuleFactory::create("PlayQueue", QPoint(1, 1), widget); 00035 uiModule->setSizePolicy(QSizePolicy::MinimumExpanding); 00036 uiModule->submitForDisplay(); 00037 populateFromDB(); 00038 updateTimes(); 00039 selection = widget->selectionModel(); 00040 // connect(widget->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SLOT(updateSelection(QItemSelection,QItemSelection))); 00041 // connect(widget->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), SLOT(updateFocus(QModelIndex, QModelIndex))); 00042 QTimer *updateTimer = new QTimer(this); 00043 updateTimer->start(10000); 00044 connect(updateTimer, SIGNAL(timeout()), SLOT(updateTimes())); 00045 connect(updateTimer, SIGNAL(timeout()), widget, SLOT(update())); 00046 } 00047 00048 PlayQueue::~PlayQueue() 00049 { 00050 delete widget; 00051 while (!queue.empty()) 00052 { 00053 Queueable *elem = queue.back(); 00054 delete elem; 00055 queue.pop_back(); 00056 } 00057 } 00058 00059 void PlayQueue::aboutToQuit() 00060 { 00061 QueueableDeque::iterator it; 00062 for (it = queue.begin(); it != queue.end(); it++) 00063 { 00064 Queueable *elem = (*it); 00065 elem->saveQueueAttributes(); 00066 } 00067 } 00068 00069 void PlayQueue::init() 00070 { 00071 scheduler = plugins->cast<Scheduler::SchedulerPlugin>("scheduler"); 00072 00073 connect(scheduler, SIGNAL(newEvent(Scheduler::EventModel*)), SLOT(newEvent(Scheduler::EventModel*))); 00074 connect(scheduler, SIGNAL(updateEvent(Scheduler::EventModel*)), SLOT(updateEvent(Scheduler::EventModel*))); 00075 connect(scheduler, SIGNAL(removeEvent(Scheduler::EventModel*)), SLOT(removeEvent(Scheduler::EventModel*))); 00076 connect(scheduler, SIGNAL(loadEvent(Scheduler::EventModel*)), SLOT(newEvent(Scheduler::EventModel*))); 00077 emit initialized(); 00078 } 00079 00080 Scheduler::SchedulerPlugin* PlayQueue::getSchedulerPlugin() const 00081 { 00082 return scheduler; 00083 } 00084 00085 const QueueableDeque& PlayQueue::getQueue() const 00086 { 00087 return queue; 00088 } 00089 00090 void PlayQueue::populateFromDB() 00091 { 00092 QSqlQuery query("SELECT position, queueable_type, queueable_id, attributes FROM queue ORDER BY position ASC"); 00093 if (!query.exec()) 00094 throw_exception(0x01, tr("Can't load play queue: %1").arg(query.lastError().text())); 00095 int position = 0; 00096 while (query.next()) { 00097 Queueable *elem = NULL; 00098 int DBposition = query.value(0).toInt(); 00099 int id = query.value(2).toInt(); 00100 QString type = query.value(1).toString(); 00101 00102 // TODO: Validate position errors 00103 if (type == "QueueTrack") 00104 { 00105 Library::AudioTrack *track = Library::AudioTrack::getTrack(id); 00106 if (track) 00107 { 00108 elem = new QueueTrack(*track); 00109 connect(track, SIGNAL(removed(Library::AudioTrack*)), SLOT(removeAll(Library::AudioTrack*))); 00110 } 00111 } 00112 else if (type == "QueueBreak") 00113 elem = new QueueBreak(); 00114 else 00115 qDebug() << "Unknow queue elem type: " << type; 00116 00117 if (elem) 00118 { 00119 elem->attributes = Network::JsonParser().parse(query.value(3).toByteArray()).toMap(); 00120 elem->position = position; 00121 if (DBposition != position) 00122 elem->correctPosition(DBposition); 00123 connect(elem, SIGNAL(remove(Queueable*)), SLOT(removeOne(Queueable*))); 00124 beginInsertRows(QModelIndex(), queue.size(), queue.size()); 00125 queue.push_back(elem); 00126 endInsertRows(); 00127 position++; 00128 } 00129 else 00130 { 00131 Queueable::remove(DBposition); 00132 } 00133 } 00134 // QueueBreak *b = new QueueBreak; 00135 // b->setEnd(QDateTime::currentDateTime().addSecs(6600)); 00136 // add(b, 5); 00137 // for(int i = 6; i < 11; i++) 00138 // queue[i]->setEvent(4); 00139 } 00140 00141 Queueable* PlayQueue::head() const 00142 { 00143 if (queue.size() > 0) 00144 return queue[0]; 00145 else 00146 return NULL; 00147 } 00148 00149 Queueable* PlayQueue::next() const 00150 { 00151 if (queue.size() > 1) 00152 return queue[1]; 00153 else 00154 return NULL; 00155 } 00156 00157 Queueable* PlayQueue::nextNext() const 00158 { 00159 if (queue.size() > 2) 00160 return queue[2]; 00161 else 00162 return NULL; 00163 } 00164 00165 void PlayQueue::pop() 00166 { 00167 QSqlQuery query; 00168 query.exec("BEGIN"); 00169 remove(0); 00170 query.exec("END"); 00171 emit popQueue(); 00172 emitAltered(); 00173 } 00174 00175 void PlayQueue::shiftDBPositions(int pos, int shift) 00176 { 00177 QSqlQuery query; 00178 bool failed = false; 00179 //query.exec("BEGIN"); 00180 //QString sql = ""; 00181 query.prepare("UPDATE queue SET position = position + :shift WHERE position = :position"); 00182 if (shift > 0) 00183 { 00184 for (int p = queue.size() - 1; p >= pos; p--) 00185 // sql += QString("UPDATE queue SET position = position + %1 WHERE position = %2; ").arg(shift).arg(p); 00186 { 00187 query.bindValue(":position", p); 00188 query.bindValue(":shift", shift); 00189 failed |= !query.exec(); 00190 } 00191 } 00192 else if (shift < 0) 00193 { 00194 for (unsigned p = pos; p < queue.size(); p++) 00195 // sql += QString("UPDATE queue SET position = position + %1 WHERE position = %2; ").arg(shift).arg(p); 00196 { 00197 query.bindValue(":position", p); 00198 query.bindValue(":shift", shift); 00199 failed |= !query.exec(); 00200 } 00201 } 00202 //sql += ""; 00203 //query.exec("COMMIT"); 00204 if (failed) 00205 throw_exception(0x02, tr("Can't shift queue position: %1").arg(query.lastError().databaseText())); 00206 } 00207 00208 int PlayQueue::rowCount(const QModelIndex &) const 00209 { 00210 return queue.size(); 00211 } 00212 00213 void PlayQueue::updateFocus(const QModelIndex ¤t, const QModelIndex &previous) 00214 { 00215 Q_UNUSED(current); 00216 Q_UNUSED(previous); 00217 /* 00218 if (current.isValid()) 00219 queue[current.row()]->showControls(); 00220 if (previous.isValid()) 00221 queue[previous.row()]->hideControls();*/ 00222 } 00223 00224 void PlayQueue::closeControls() 00225 { 00226 /* 00227 if (widget->selectionModel()->currentIndex().isValid()) 00228 queue[widget->selectionModel()->currentIndex().row()]->hideControls();*/ 00229 } 00230 00231 QVariant PlayQueue::data(const QModelIndex &index, int role) const 00232 { 00233 if (role == Qt::DisplayRole) 00234 return qVariantFromValue((void*)queue[index.row()]); 00235 else 00236 return QVariant(); 00237 } 00238 00239 Qt::ItemFlags PlayQueue::flags(const QModelIndex &) const 00240 { 00241 return (Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled); 00242 } 00243 00244 void PlayQueue::updateTimes(int shift) 00245 { 00246 QDateTime t = QDateTime::currentDateTime(); 00247 QueueableDeque::iterator it; 00248 bool finite = true; 00249 00250 t = t.addSecs(-shift); 00251 for (it = queue.begin(); it != queue.end(); it++) 00252 { 00253 Queueable *elem = (*it); 00254 if (finite) 00255 { 00256 elem->setPlayTime(t); 00257 if (elem->queueIsFinite()) 00258 { 00259 t = t.addSecs(elem->queueDuration(t)); 00260 } 00261 else 00262 finite = false; 00263 } 00264 else 00265 elem->setPlayTime(QDateTime()); 00266 } 00267 } 00268 00269 void PlayQueue::updatePositions() 00270 { 00271 for (unsigned pos = 0; pos < queue.size(); pos++) 00272 queue[pos]->position = pos; 00273 } 00274 00275 QWidget* PlayQueue::getWidget() 00276 { 00277 return widget; 00278 } 00279 00280 void PlayQueue::add(Queueable *elem, int position) 00281 { 00282 if (position == -1) 00283 { 00284 beginInsertRows(QModelIndex(), queue.size(), queue.size()); 00285 elem->queueAt(queue.size()); 00286 queue.push_back(elem); 00287 } 00288 else 00289 { 00290 beginInsertRows(QModelIndex(), position, position); 00291 shiftDBPositions(position, +1); 00292 elem->queueAt(position); 00293 queue.insert(queue.begin() + position, elem); 00294 updatePositions(); 00295 } 00296 if (elem->isTrack()) 00297 { 00298 QueueTrack *track = static_cast<QueueTrack*>(elem); 00299 connect(track->getTrack(), SIGNAL(removed(Library::AudioTrack*)), SLOT(removeAll(Library::AudioTrack*))); 00300 } 00301 connect(elem, SIGNAL(remove(Queueable*)), SLOT(removeOne(Queueable*))); 00302 endInsertRows(); 00303 updateTimes(); 00304 emit addQueueElem(elem); 00305 } 00306 00307 void PlayQueue::add(Container *elem, int position) 00308 { 00309 throw_exception(0x03, tr("PlayQueue::add(Container *elem, int position) is not implemented yet")); 00310 if (position == -1) 00311 queue.insert(queue.end(), elem->getChildren().begin(), elem->getChildren().end()); 00312 else 00313 queue.insert(queue.begin() + position, elem->getChildren().begin(), elem->getChildren().end()); 00314 } 00315 00316 void PlayQueue::remove(int position) 00317 { 00318 Queueable *elem; 00319 QSqlQuery query; 00320 00321 if (position >= (int)queue.size() || position < 0) 00322 return; 00323 query.prepare("DELETE FROM queue WHERE position = :position"); 00324 query.bindValue(":position", position); 00325 if (!query.exec()) 00326 throw_exception(0x04, tr("Can't remove queueable: %1").arg(query.lastError().text())); 00327 beginRemoveRows(QModelIndex(), position, position); 00328 shiftDBPositions(position, -1); 00329 elem = queue.at(position); 00330 queue.erase(queue.begin() + position); 00331 updatePositions(); 00332 emit removeQueueElem(elem); 00333 delete elem; 00334 updateTimes(); 00335 endRemoveRows(); 00336 } 00337 00338 void PlayQueue::remove(QList<int> positions) 00339 { 00340 QSqlQuery query; 00341 query.exec("BEGIN"); 00342 qSort(positions); 00343 for (int i = positions.size() - 1; i >= 0; --i) 00344 remove(positions[i]); 00345 query.exec("COMMIT"); 00346 } 00347 00348 void PlayQueue::removeAll(Library::AudioTrack* track) 00349 { 00350 QList<int> positions; 00351 QueueableDeque::iterator it; 00352 for (it = queue.begin(); it != queue.end(); it++) 00353 { 00354 Queueable *elem = *it; 00355 if (elem->isTrack() && elem->toQueueTrack()->getTrack() == track) 00356 positions << elem->toQueueTrack()->getPosition(); 00357 } 00358 remove(positions); 00359 } 00360 00361 void PlayQueue::removeSelected() 00362 { 00363 QList<int> positions; 00364 foreach(const QModelIndex &index, selection->selectedIndexes()) 00365 positions << index.row(); 00366 startAlteringALot(); 00367 remove(positions); 00368 finishAlteringALot(); 00369 } 00370 00371 void PlayQueue::removeOne(Queueable *elem) 00372 { 00373 if (queue[elem->getPosition()] == elem) 00374 { 00375 remove(elem->getPosition()); 00376 emitAltered(); 00377 } 00378 else 00379 qDebug() << "bad remove position"; 00380 } 00381 00382 void PlayQueue::bulkMove(QList<int> positions, int destination) 00383 { 00384 qSort(positions); 00385 QSqlQuery query; 00386 00387 startAlteringALot(); 00388 query.exec("BEGIN"); 00389 // Get element's references 00390 QList<Queueable*> queueables; 00391 foreach(int pos, positions) 00392 if (queue[pos]) 00393 { 00394 qDebug() << "move " << queue[pos]; 00395 queueables << queue[pos]; 00396 } 00397 00398 // Clone and insert 00399 int shift = 0; 00400 foreach(Queueable* queueable, queueables) 00401 { 00402 add(queueable->clone(), destination + shift); 00403 if (destination >= 0) 00404 shift++; 00405 } 00406 00407 // Remove 00408 foreach(Queueable* queueable, queueables) 00409 removeOne(queueable); 00410 query.exec("COMMIT"); 00411 finishAlteringALot(); 00412 } 00413 00414 void PlayQueue::emitAltered() 00415 { 00416 emit altered(); 00417 } 00418 00419 QStringList PlayQueue::mimeTypes() const 00420 { 00421 QStringList types; 00422 types << "application/x-defuzeme-audiotrack"; 00423 return types; 00424 } 00425 00426 QMimeData *PlayQueue::mimeData(const QModelIndexList &indexes) const 00427 { 00428 QMimeData *mimeData = new QMimeData(); 00429 QByteArray queueables, audioTracks; 00430 00431 // Generate list of queueable positions (for local moves) 00432 QDataStream sQueueables(&queueables, QIODevice::WriteOnly); 00433 // Generate list of audiotrack id (for other widgets like playlist) 00434 QDataStream sAudioTracks(&audioTracks, QIODevice::WriteOnly); 00435 foreach (const QModelIndex &index, indexes) 00436 { 00437 if (index.isValid() && queue[index.row()]) 00438 { 00439 sQueueables << index.row(); 00440 if (queue[index.row()]->isTrack()) 00441 sAudioTracks << queue[index.row()]->toQueueTrack()->getTrack()->getUid(); 00442 } 00443 } 00444 mimeData->setData("application/x-defuzeme-queueable", queueables); 00445 mimeData->setData("application/x-defuzeme-audiotrack", audioTracks); 00446 return mimeData; 00447 } 00448 00449 00450 bool PlayQueue::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) 00451 { 00452 Q_UNUSED(action); 00453 Q_UNUSED(column); 00454 Q_UNUSED(parent); 00455 00456 QByteArray encodedData; 00457 bool isQueueable = data->hasFormat("application/x-defuzeme-queueable"); 00458 00459 if (isQueueable) 00460 encodedData = data->data("application/x-defuzeme-queueable"); 00461 else if (data->hasFormat("application/x-defuzeme-audiotrack")) 00462 encodedData = data->data("application/x-defuzeme-audiotrack"); 00463 else 00464 return false; 00465 00466 QDataStream stream(&encodedData, QIODevice::ReadOnly); 00467 QList<int> ids; 00468 00469 while (!stream.atEnd()) { 00470 int id; 00471 stream >> id; 00472 ids << id; 00473 } 00474 00475 if (isQueueable) 00476 bulkMove(ids, row); 00477 else 00478 { 00479 // When multiple tracks are dropped, increment position in order not to inverse them 00480 QSqlQuery query; 00481 query.exec("BEGIN"); 00482 int shift = 0; 00483 foreach(int id, ids) 00484 { 00485 qDebug() << "drop multiple tracks: " << id; 00486 Library::AudioTrack *track = Library::AudioTrack::getTrack(id); 00487 if (track) 00488 { 00489 add(new Queue::QueueTrack(*track), row + shift); 00490 shift++; 00491 } 00492 } 00493 query.exec("COMMIT"); 00494 emitAltered(); 00495 } 00496 00497 return true; 00498 } 00499 00500 bool PlayQueue::isBeingAlteredALot() const 00501 { 00502 return altering; 00503 } 00504 00505 void PlayQueue::startAlteringALot() 00506 { 00507 if (isBeingAlteredALot()) 00508 qDebug() << "Warning: recursive bulk altering on queue list!"; 00509 else 00510 altering = true; 00511 } 00512 00513 void PlayQueue::finishAlteringALot() 00514 { 00515 if (isBeingAlteredALot()) 00516 { 00517 altering = false; 00518 emitAltered(); 00519 } 00520 else 00521 qDebug() << "Warning: finishing bulk altering not started on queue list!"; 00522 } 00523 00524 QList<int> PlayQueue::findByEvent(Scheduler::EventModel *event) 00525 { 00526 QList<int> elems; 00527 for(unsigned i = 0; i < queue.size(); i++) 00528 if (queue[i]->getEvent() == event->getId()) 00529 elems << i; 00530 return elems; 00531 } 00532 00533 void PlayQueue::newEvent(Scheduler::EventModel* event) 00534 { 00535 QSqlQuery query; 00536 qDebug() << "newEvent" << event->getTitle(); 00537 00538 // Check existing items 00539 if (!findByEvent(event).empty()) 00540 return; 00541 00542 // Find starting position 00543 QDateTime start = event->nextInstance(); 00544 unsigned position = 0; 00545 while (position < queue.size() && queue[position]->getPlayTime() < start) 00546 position++; 00547 if (position > 0 && queue[position - 1]->isBreak()) 00548 position--; 00549 // position is now just after event start 00550 // optionnally test if it's better to take the previous 00551 00552 // Fetch tracks from playlists 00553 QList<int> playlists = event->getPlaylists(); 00554 QList<int> tracks; 00555 foreach (int playlist, playlists) 00556 tracks.append(Lists::ListsPlugin::getTracksForNormalPlaylist(playlist)); 00557 00558 if (tracks.empty()) 00559 return; 00560 00561 startAlteringALot(); 00562 query.exec("BEGIN"); 00563 // Insert break if needed 00564 if (position >= queue.size() || queue[position]->getPlayTime() < start) 00565 { 00566 // best position is end of queue, insert break to fill 00567 add(new QueueBreak(start), position); 00568 position++; 00569 } 00570 00571 // Random order 00572 std::random_shuffle(tracks.begin(), tracks.end()); 00573 00574 // Insert into queue 00575 int duration = 0; 00576 int t = 0; 00577 while (duration < event->getDuration() * 60) 00578 { 00579 Queueable *track = new QueueTrack(*Library::AudioTrack::getTrack(tracks[t])); 00580 duration += track->queueDuration(); 00581 track->setEvent(event->getId()); 00582 add(track, position); 00583 position++; 00584 // Cycle through tracks 00585 t++; 00586 if (t >= tracks.size()) 00587 t = 0; 00588 } 00589 query.exec("COMMIT"); 00590 finishAlteringALot(); 00591 } 00592 00593 void PlayQueue::updateEvent(Scheduler::EventModel* event) 00594 { 00595 qDebug() << "updateEvent" << event->getTitle(); 00596 00597 removeEvent(event); 00598 newEvent(event); 00599 } 00600 00601 void PlayQueue::removeEvent(Scheduler::EventModel* event) 00602 { 00603 qDebug() << "removeEvent" << event->getTitle(); 00604 00605 // Find event elements 00606 QList<int> elems = findByEvent(event); 00607 if (elems.empty()) 00608 return; 00609 00610 // Add optional break 00611 if (elems.first() > 0 && queue[elems.first() - 1]->isBreak()) 00612 elems << elems.first() - 1; 00613 00614 startAlteringALot(); 00615 remove(elems); 00616 finishAlteringALot(); 00617 }