1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qdeclarativeplaylist_p.h" |
41 | |
42 | QT_BEGIN_NAMESPACE |
43 | |
44 | /*! |
45 | \qmltype PlaylistItem |
46 | \instantiates QDeclarativePlaylistItem |
47 | \since 5.6 |
48 | |
49 | \inqmlmodule QtMultimedia |
50 | \ingroup multimedia_qml |
51 | \ingroup multimedia_audio_qml |
52 | \ingroup multimedia_video_qml |
53 | \brief Defines an item in a Playlist. |
54 | |
55 | \sa Playlist |
56 | */ |
57 | |
58 | /*! |
59 | \qmlproperty url QtMultimedia::PlaylistItem::source |
60 | |
61 | This property holds the source URL of the item. |
62 | |
63 | \sa Playlist |
64 | */ |
65 | QDeclarativePlaylistItem::QDeclarativePlaylistItem(QObject *parent) |
66 | : QObject(parent) |
67 | { |
68 | } |
69 | |
70 | QUrl QDeclarativePlaylistItem::source() const |
71 | { |
72 | return m_source; |
73 | } |
74 | |
75 | void QDeclarativePlaylistItem::setSource(const QUrl &source) |
76 | { |
77 | m_source = source; |
78 | } |
79 | |
80 | /*! |
81 | \qmltype Playlist |
82 | \instantiates QDeclarativePlaylist |
83 | \since 5.6 |
84 | \brief For specifying a list of media to be played. |
85 | |
86 | \inqmlmodule QtMultimedia |
87 | \ingroup multimedia_qml |
88 | \ingroup multimedia_audio_qml |
89 | \ingroup multimedia_video_qml |
90 | |
91 | The Playlist type provides a way to play a list of media with the MediaPlayer, Audio and Video |
92 | types. It can be used as a data source for view elements (such as ListView) and other elements |
93 | that interact with model data (such as Repeater). When used as a data model, each playlist |
94 | item's source URL can be accessed using the \c source role. |
95 | |
96 | \qml |
97 | Item { |
98 | width: 400; |
99 | height: 300; |
100 | |
101 | Audio { |
102 | id: player; |
103 | playlist: Playlist { |
104 | id: playlist |
105 | PlaylistItem { source: "song1.ogg"; } |
106 | PlaylistItem { source: "song2.ogg"; } |
107 | PlaylistItem { source: "song3.ogg"; } |
108 | } |
109 | } |
110 | |
111 | ListView { |
112 | model: playlist; |
113 | delegate: Text { |
114 | font.pixelSize: 16; |
115 | text: source; |
116 | } |
117 | } |
118 | |
119 | MouseArea { |
120 | anchors.fill: parent; |
121 | onPressed: { |
122 | if (player.playbackState != Audio.PlayingState) { |
123 | player.play(); |
124 | } else { |
125 | player.pause(); |
126 | } |
127 | } |
128 | } |
129 | } |
130 | \endqml |
131 | |
132 | \sa MediaPlayer, Audio, Video |
133 | */ |
134 | |
135 | void QDeclarativePlaylist::_q_mediaAboutToBeInserted(int start, int end) |
136 | { |
137 | emit itemAboutToBeInserted(start, end); |
138 | |
139 | beginInsertRows(parent: QModelIndex(), first: start, last: end); |
140 | } |
141 | |
142 | void QDeclarativePlaylist::_q_mediaInserted(int start, int end) |
143 | { |
144 | endInsertRows(); |
145 | |
146 | emit itemCountChanged(); |
147 | emit itemInserted(start, end); |
148 | } |
149 | |
150 | void QDeclarativePlaylist::_q_mediaAboutToBeRemoved(int start, int end) |
151 | { |
152 | emit itemAboutToBeRemoved(start, end); |
153 | |
154 | beginRemoveRows(parent: QModelIndex(), first: start, last: end); |
155 | } |
156 | |
157 | void QDeclarativePlaylist::_q_mediaRemoved(int start, int end) |
158 | { |
159 | endRemoveRows(); |
160 | |
161 | emit itemCountChanged(); |
162 | emit itemRemoved(start, end); |
163 | } |
164 | |
165 | void QDeclarativePlaylist::_q_mediaChanged(int start, int end) |
166 | { |
167 | emit dataChanged(topLeft: createIndex(arow: start, acolumn: 0), bottomRight: createIndex(arow: end, acolumn: 0)); |
168 | emit itemChanged(start, end); |
169 | } |
170 | |
171 | void QDeclarativePlaylist::_q_loadFailed() |
172 | { |
173 | m_error = m_playlist->error(); |
174 | m_errorString = m_playlist->errorString(); |
175 | |
176 | emit error(error: Error(m_error), errorString: m_errorString); |
177 | emit errorChanged(); |
178 | emit loadFailed(); |
179 | } |
180 | |
181 | QDeclarativePlaylist::QDeclarativePlaylist(QObject *parent) |
182 | : QAbstractListModel(parent) |
183 | , m_playlist(0) |
184 | , m_error(QMediaPlaylist::NoError) |
185 | , m_readOnly(false) |
186 | { |
187 | } |
188 | |
189 | QDeclarativePlaylist::~QDeclarativePlaylist() |
190 | { |
191 | delete m_playlist; |
192 | } |
193 | |
194 | /*! |
195 | \qmlproperty enumeration QtMultimedia::Playlist::playbackMode |
196 | |
197 | This property holds the order in which items in the playlist are played. |
198 | |
199 | \table |
200 | \header \li Value \li Description |
201 | \row \li CurrentItemOnce |
202 | \li The current item is played only once. |
203 | \row \li CurrentItemInLoop |
204 | \li The current item is played repeatedly in a loop. |
205 | \row \li Sequential |
206 | \li Playback starts from the current and moves through each successive item until the last |
207 | is reached and then stops. The next item is a null item when the last one is currently |
208 | playing. |
209 | \row \li Loop |
210 | \li Playback restarts at the first item after the last has finished playing. |
211 | \row \li Random |
212 | \li Play items in random order. |
213 | \endtable |
214 | */ |
215 | QDeclarativePlaylist::PlaybackMode QDeclarativePlaylist::playbackMode() const |
216 | { |
217 | return PlaybackMode(m_playlist->playbackMode()); |
218 | } |
219 | |
220 | void QDeclarativePlaylist::setPlaybackMode(PlaybackMode mode) |
221 | { |
222 | if (playbackMode() == mode) |
223 | return; |
224 | |
225 | m_playlist->setPlaybackMode(QMediaPlaylist::PlaybackMode(mode)); |
226 | } |
227 | |
228 | /*! |
229 | \qmlproperty url QtMultimedia::Playlist::currentItemsource |
230 | |
231 | This property holds the source URL of the current item in the playlist. |
232 | */ |
233 | QUrl QDeclarativePlaylist::currentItemSource() const |
234 | { |
235 | return m_playlist->currentMedia().request().url(); |
236 | } |
237 | |
238 | /*! |
239 | \qmlproperty int QtMultimedia::Playlist::currentIndex |
240 | |
241 | This property holds the position of the current item in the playlist. |
242 | */ |
243 | int QDeclarativePlaylist::currentIndex() const |
244 | { |
245 | return m_playlist->currentIndex(); |
246 | } |
247 | |
248 | void QDeclarativePlaylist::setCurrentIndex(int index) |
249 | { |
250 | if (currentIndex() == index) |
251 | return; |
252 | |
253 | m_playlist->setCurrentIndex(index); |
254 | } |
255 | |
256 | /*! |
257 | \qmlproperty int QtMultimedia::Playlist::itemCount |
258 | |
259 | This property holds the number of items in the playlist. |
260 | */ |
261 | int QDeclarativePlaylist::itemCount() const |
262 | { |
263 | return m_playlist->mediaCount(); |
264 | } |
265 | |
266 | /*! |
267 | \qmlproperty bool QtMultimedia::Playlist::readOnly |
268 | |
269 | This property indicates if the playlist can be modified. |
270 | */ |
271 | bool QDeclarativePlaylist::readOnly() const |
272 | { |
273 | // There's no signal to tell whether or not the read only state changed, so we consider it fixed |
274 | // after its initial retrieval in componentComplete(). |
275 | return m_readOnly; |
276 | } |
277 | |
278 | /*! |
279 | \qmlproperty enumeration QtMultimedia::Playlist::error |
280 | |
281 | This property holds the error condition of the playlist. |
282 | |
283 | \table |
284 | \header \li Value \li Description |
285 | \row \li NoError |
286 | \li No errors |
287 | \row \li FormatError |
288 | \li Format error. |
289 | \row \li FormatNotSupportedError |
290 | \li Format not supported. |
291 | \row \li NetworkError |
292 | \li Network error. |
293 | \row \li AccessDeniedError |
294 | \li Access denied error. |
295 | \endtable |
296 | */ |
297 | QDeclarativePlaylist::Error QDeclarativePlaylist::error() const |
298 | { |
299 | return Error(m_error); |
300 | } |
301 | |
302 | /*! |
303 | \qmlproperty string QtMultimedia::Playlist::errorString |
304 | |
305 | This property holds a string describing the current error condition of the playlist. |
306 | */ |
307 | QString QDeclarativePlaylist::errorString() const |
308 | { |
309 | return m_errorString; |
310 | } |
311 | |
312 | /*! |
313 | \qmlmethod url QtMultimedia::Playlist::itemSource(index) |
314 | |
315 | Returns the source URL of the item at the given \a index in the playlist. |
316 | */ |
317 | QUrl QDeclarativePlaylist::itemSource(int index) |
318 | { |
319 | return m_playlist->media(index).request().url(); |
320 | } |
321 | |
322 | /*! |
323 | \qmlmethod int QtMultimedia::Playlist::nextIndex(steps) |
324 | |
325 | Returns the index of the item in the playlist which would be current after calling next() |
326 | \a steps times. |
327 | |
328 | Returned value depends on the size of the playlist, the current position and the playback mode. |
329 | |
330 | \sa playbackMode, previousIndex() |
331 | */ |
332 | int QDeclarativePlaylist::nextIndex(int steps) |
333 | { |
334 | return m_playlist->nextIndex(steps); |
335 | } |
336 | |
337 | /*! |
338 | \qmlmethod int QtMultimedia::Playlist::previousIndex(steps) |
339 | |
340 | Returns the index of the item in the playlist which would be current after calling previous() |
341 | \a steps times. |
342 | |
343 | Returned value depends on the size of the playlist, the current position and the playback mode. |
344 | |
345 | \sa playbackMode, nextIndex() |
346 | */ |
347 | int QDeclarativePlaylist::previousIndex(int steps) |
348 | { |
349 | return m_playlist->previousIndex(steps); |
350 | } |
351 | |
352 | /*! |
353 | \qmlmethod QtMultimedia::Playlist::next() |
354 | |
355 | Advances to the next item in the playlist. |
356 | */ |
357 | void QDeclarativePlaylist::next() |
358 | { |
359 | m_playlist->next(); |
360 | } |
361 | |
362 | /*! |
363 | \qmlmethod QtMultimedia::Playlist::previous() |
364 | |
365 | Returns to the previous item in the playlist. |
366 | */ |
367 | void QDeclarativePlaylist::previous() |
368 | { |
369 | m_playlist->previous(); |
370 | } |
371 | |
372 | /*! |
373 | \qmlmethod QtMultimedia::Playlist::shuffle() |
374 | |
375 | Shuffles items in the playlist. |
376 | */ |
377 | void QDeclarativePlaylist::shuffle() |
378 | { |
379 | m_playlist->shuffle(); |
380 | } |
381 | |
382 | /*! |
383 | \qmlmethod QtMultimedia::Playlist::load(location, format) |
384 | |
385 | Loads a playlist from the given \a location. If \a format is specified, it is used, otherwise |
386 | the format is guessed from the location name and the data. |
387 | |
388 | New items are appended to the playlist. |
389 | |
390 | \c onloaded() is emitted if the playlist loads successfully, otherwise \c onLoadFailed() is |
391 | emitted with \l error and \l errorString defined accordingly. |
392 | */ |
393 | void QDeclarativePlaylist::load(const QUrl &location, const QString &format) |
394 | { |
395 | m_error = QMediaPlaylist::NoError; |
396 | m_errorString = QString(); |
397 | emit errorChanged(); |
398 | m_playlist->load(location, format: format.toLatin1().constData()); |
399 | } |
400 | |
401 | /*! |
402 | \qmlmethod bool QtMultimedia::Playlist::save(location, format) |
403 | |
404 | Saves the playlist to the given \a location. If \a format is specified, it is used, otherwise |
405 | the format is guessed from the location name. |
406 | |
407 | Returns true if the playlist is saved successfully. |
408 | */ |
409 | bool QDeclarativePlaylist::save(const QUrl &location, const QString &format) |
410 | { |
411 | return m_playlist->save(location, format: format.toLatin1().constData()); |
412 | } |
413 | |
414 | /*! |
415 | \qmlmethod bool QtMultimedia::Playlist::addItem(source) |
416 | |
417 | Appends the \a source URL to the playlist. |
418 | |
419 | Returns true if the \a source is added successfully. |
420 | */ |
421 | bool QDeclarativePlaylist::addItem(const QUrl &source) |
422 | { |
423 | return m_playlist->addMedia(content: QMediaContent(source)); |
424 | } |
425 | |
426 | /*! |
427 | \qmlmethod bool QtMultimedia::Playlist::addItems(sources) |
428 | |
429 | Appends the list of URLs in \a sources to the playlist. |
430 | |
431 | Returns true if the \a sources are added successfully. |
432 | |
433 | \since 5.7 |
434 | */ |
435 | bool QDeclarativePlaylist::addItems(const QList<QUrl> &sources) |
436 | { |
437 | if (sources.isEmpty()) |
438 | return false; |
439 | |
440 | QList<QMediaContent> contents; |
441 | QList<QUrl>::const_iterator it = sources.constBegin(); |
442 | while (it != sources.constEnd()) { |
443 | contents.push_back(t: QMediaContent(*it)); |
444 | ++it; |
445 | } |
446 | return m_playlist->addMedia(items: contents); |
447 | } |
448 | |
449 | /*! |
450 | \qmlmethod bool QtMultimedia::Playlist::insertItem(index, source) |
451 | |
452 | Inserts the \a source URL to the playlist at the given \a index. |
453 | |
454 | Returns true if the \a source is added successfully. |
455 | */ |
456 | bool QDeclarativePlaylist::insertItem(int index, const QUrl &source) |
457 | { |
458 | return m_playlist->insertMedia(index, content: QMediaContent(source)); |
459 | } |
460 | |
461 | /*! |
462 | \qmlmethod bool QtMultimedia::Playlist::insertItems(index, sources) |
463 | |
464 | Inserts the list of URLs in \a sources to the playlist at the given \a index. |
465 | |
466 | Returns true if the \a sources are added successfully. |
467 | |
468 | \since 5.7 |
469 | */ |
470 | bool QDeclarativePlaylist::insertItems(int index, const QList<QUrl> &sources) |
471 | { |
472 | if (sources.empty()) |
473 | return false; |
474 | |
475 | QList<QMediaContent> contents; |
476 | QList<QUrl>::const_iterator it = sources.constBegin(); |
477 | while (it != sources.constEnd()) { |
478 | contents.push_back(t: QMediaContent(*it)); |
479 | ++it; |
480 | } |
481 | return m_playlist->insertMedia(index, items: contents); |
482 | } |
483 | |
484 | /*! |
485 | \qmlmethod bool QtMultimedia::Playlist::moveItem(from, to) |
486 | |
487 | Moves the item at index position \a from to index position \a to. |
488 | |
489 | Returns \c true if the item is moved successfully. |
490 | |
491 | \since 5.7 |
492 | */ |
493 | bool QDeclarativePlaylist::moveItem(int from, int to) |
494 | { |
495 | return m_playlist->moveMedia(from, to); |
496 | } |
497 | |
498 | /*! |
499 | \qmlmethod bool QtMultimedia::Playlist::removeItem(index) |
500 | |
501 | Removes the item at the given \a index from the playlist. |
502 | |
503 | Returns \c true if the item is removed successfully. |
504 | */ |
505 | bool QDeclarativePlaylist::removeItem(int index) |
506 | { |
507 | return m_playlist->removeMedia(pos: index); |
508 | } |
509 | |
510 | /*! |
511 | \qmlmethod bool QtMultimedia::Playlist::removeItems(int start, int end) |
512 | |
513 | Removes items in the playlist from \a start to \a end inclusive. |
514 | |
515 | Returns \c true if the items are removed successfully. |
516 | |
517 | \since 5.7 |
518 | */ |
519 | bool QDeclarativePlaylist::removeItems(int start, int end) |
520 | { |
521 | return m_playlist->removeMedia(start, end); |
522 | } |
523 | |
524 | /*! |
525 | \qmlmethod bool QtMultimedia::Playlist::clear() |
526 | |
527 | Removes all the items from the playlist. |
528 | |
529 | Returns \c true if the operation is successful. |
530 | */ |
531 | bool QDeclarativePlaylist::clear() |
532 | { |
533 | return m_playlist->clear(); |
534 | } |
535 | |
536 | int QDeclarativePlaylist::rowCount(const QModelIndex &parent) const |
537 | { |
538 | if (parent.isValid()) |
539 | return 0; |
540 | |
541 | return m_playlist->mediaCount(); |
542 | } |
543 | |
544 | QVariant QDeclarativePlaylist::data(const QModelIndex &index, int role) const |
545 | { |
546 | Q_UNUSED(role); |
547 | |
548 | if (!index.isValid()) |
549 | return QVariant(); |
550 | |
551 | return m_playlist->media(index: index.row()).request().url(); |
552 | } |
553 | |
554 | QHash<int, QByteArray> QDeclarativePlaylist::roleNames() const |
555 | { |
556 | QHash<int, QByteArray> roleNames; |
557 | roleNames[SourceRole] = "source" ; |
558 | return roleNames; |
559 | } |
560 | |
561 | void QDeclarativePlaylist::classBegin() |
562 | { |
563 | m_playlist = new QMediaPlaylist(this); |
564 | |
565 | connect(sender: m_playlist, SIGNAL(currentIndexChanged(int)), |
566 | receiver: this, SIGNAL(currentIndexChanged())); |
567 | connect(sender: m_playlist, SIGNAL(playbackModeChanged(QMediaPlaylist::PlaybackMode)), |
568 | receiver: this, SIGNAL(playbackModeChanged())); |
569 | connect(sender: m_playlist, SIGNAL(currentMediaChanged(QMediaContent)), |
570 | receiver: this, SIGNAL(currentItemSourceChanged())); |
571 | connect(sender: m_playlist, SIGNAL(mediaAboutToBeInserted(int,int)), |
572 | receiver: this, SLOT(_q_mediaAboutToBeInserted(int,int))); |
573 | connect(sender: m_playlist, SIGNAL(mediaInserted(int,int)), |
574 | receiver: this, SLOT(_q_mediaInserted(int,int))); |
575 | connect(sender: m_playlist, SIGNAL(mediaAboutToBeRemoved(int,int)), |
576 | receiver: this, SLOT(_q_mediaAboutToBeRemoved(int,int))); |
577 | connect(sender: m_playlist, SIGNAL(mediaRemoved(int,int)), |
578 | receiver: this, SLOT(_q_mediaRemoved(int,int))); |
579 | connect(sender: m_playlist, SIGNAL(mediaChanged(int,int)), |
580 | receiver: this, SLOT(_q_mediaChanged(int,int))); |
581 | connect(sender: m_playlist, SIGNAL(loaded()), |
582 | receiver: this, SIGNAL(loaded())); |
583 | connect(sender: m_playlist, SIGNAL(loadFailed()), |
584 | receiver: this, SLOT(_q_loadFailed())); |
585 | |
586 | if (m_playlist->isReadOnly()) { |
587 | m_readOnly = true; |
588 | emit readOnlyChanged(); |
589 | } |
590 | } |
591 | |
592 | void QDeclarativePlaylist::componentComplete() |
593 | { |
594 | } |
595 | |
596 | /*! |
597 | \qmlsignal QtMultimedia::Playlist::itemAboutToBeInserted(start, end) |
598 | |
599 | This signal is emitted when items are to be inserted into the playlist at \a start and ending at |
600 | \a end. |
601 | |
602 | The corresponding handler is \c onItemAboutToBeInserted. |
603 | */ |
604 | |
605 | /*! |
606 | \qmlsignal QtMultimedia::Playlist::itemInserted(start, end) |
607 | |
608 | This signal is emitted after items have been inserted into the playlist. The new items are those |
609 | between \a start and \a end inclusive. |
610 | |
611 | The corresponding handler is \c onItemInserted. |
612 | */ |
613 | |
614 | /*! |
615 | \qmlsignal QtMultimedia::Playlist::itemAboutToBeRemoved(start, end) |
616 | |
617 | This signal emitted when items are to be deleted from the playlist at \a start and ending at |
618 | \a end. |
619 | |
620 | The corresponding handler is \c onItemAboutToBeRemoved. |
621 | */ |
622 | |
623 | /*! |
624 | \qmlsignal QtMultimedia::Playlist::itemRemoved(start, end) |
625 | |
626 | This signal is emitted after items have been removed from the playlist. The removed items are |
627 | those between \a start and \a end inclusive. |
628 | |
629 | The corresponding handler is \c onMediaRemoved. |
630 | */ |
631 | |
632 | /*! |
633 | \qmlsignal QtMultimedia::Playlist::itemChanged(start, end) |
634 | |
635 | This signal is emitted after items have been changed in the playlist between \a start and |
636 | \a end positions inclusive. |
637 | |
638 | The corresponding handler is \c onItemChanged. |
639 | */ |
640 | |
641 | /*! |
642 | \qmlsignal QtMultimedia::Playlist::loaded() |
643 | |
644 | This signal is emitted when the playlist loading succeeded. |
645 | |
646 | The corresponding handler is \c onLoaded. |
647 | */ |
648 | |
649 | /*! |
650 | \qmlsignal QtMultimedia::Playlist::loadFailed() |
651 | |
652 | This signal is emitted when the playlist loading failed. \l error and \l errorString can be |
653 | checked for more information on the failure. |
654 | |
655 | The corresponding handler is \c onLoadFailed. |
656 | */ |
657 | |
658 | QT_END_NAMESPACE |
659 | |
660 | #include "moc_qdeclarativeplaylist_p.cpp" |
661 | |