TTKMusicPlayer  4.3.0.0
TTKMusicPlayer imitates Kugou UI, the music player uses of qmmp core library based on Qt for windows and linux
musicsongscontainerwidget.cpp
Go to the documentation of this file.
5 #include "musicmessagebox.h"
6 #include "musicprogresswidget.h"
10 #include "musicapplication.h"
11 #include "musictoastlabel.h"
12 #include "musicfileutils.h"
13 #include "musicformats.h"
14 
15 #include <QMimeData>
16 
17 static constexpr int ITEM_MIN_COUNT = MIN_ITEM_COUNT;
18 static constexpr int ITEM_MAX_COUNT = 10;
19 static constexpr int RECENT_ITEM_MAX_COUNT = 50;
20 
22 
24  : MusicSongsToolBoxWidget(parent),
26  m_playRowIndex(MUSIC_NORMAL_LIST),
27  m_lastSearchIndex(MUSIC_NORMAL_LIST),
28  m_selectDeleteIndex(MUSIC_NONE_LIST),
29  m_functionWidget(nullptr),
30  m_songSearchWidget(nullptr)
31 {
32  m_instance = this;
33 
36  setAcceptDrops(true);
37 
38  connect(m_topMaskWidget, SIGNAL(itemIndexChanged(int)), SLOT(itemIndexChanged(int)));
39  connect(m_scrollArea->verticalScrollBar(), SIGNAL(valueChanged(int)), SLOT(sliderValueChanaged(int)));
40 }
41 
43 {
44  delete m_topMaskWidget;
45  delete m_functionWidget;
46  delete m_songSearchWidget;
47 
48  while(!m_containerItems.isEmpty())
49  {
50  delete m_containerItems.takeLast().m_itemWidget;
51  }
52 }
53 
55 {
56  return m_instance;
57 }
58 
60 {
61  int index = -1;
62  for(int i = 0; i < m_containerItems.count(); ++i)
63  {
64  if(item.m_id == m_containerItems[i].m_id)
65  {
66  index = i;
67  break;
68  }
69  }
70 
71  if(index == -1)
72  {
73  appendSongItemList({item});
74  }
75  else
76  {
77  MusicSongItem *it = &m_containerItems[index];
78  it->m_sort = item.m_sort;
79  it->m_songs = item.m_songs;
80  it->m_itemName = item.m_itemName;
83  setItemTitle(it);
84 
86  {
89  }
90  }
91 }
92 
93 bool MusicSongsContainerWidget::addSongItemList(const MusicSongItemList &items)
94 {
95  TTKIntSet inNeed;
97  for(const MusicSongItem &item : qAsConst(items))
98  {
99  inNeed.remove(item.m_itemIndex);
100  }
101  //
102  if(!inNeed.isEmpty())
103  {
104  //if less than four count(0, 1, 2, 3), find and add default items
106  //
107  for(const int item : qAsConst(inNeed))
108  {
109  MusicSongItem it;
110  switch(item)
111  {
112  case MUSIC_NORMAL_LIST:
113  {
114  it.m_itemIndex = item;
115  it.m_itemName = tr("Default Item");
116  break;
117  }
118  case MUSIC_LOVEST_LIST:
119  {
120  it.m_itemIndex = item;
121  it.m_itemName = tr("Lovest Item");
122  break;
123  }
124  case MUSIC_NETWORK_LIST:
125  {
126  it.m_itemIndex = item;
127  it.m_itemName = tr("Net Item");
128  break;
129  }
130  case MUSIC_RECENT_LIST:
131  {
132  it.m_itemIndex = item;
133  it.m_itemName = tr("Recent Item");
134  break;
135  }
136  default: break;
137  }
138  m_containerItems << it;
139  }
140  std::sort(m_containerItems.begin(), m_containerItems.end());
141  }
142  else
143  {
145  }
146 
147  for(int i = 0; i < m_containerItems.count(); ++i)
148  {
150  }
151 
152  return inNeed.isEmpty();
153 }
154 
155 void MusicSongsContainerWidget::appendSongItemList(const MusicSongItemList &items)
156 {
157  for(int i = 0; i < items.count(); ++i)
158  {
159  MusicSongItem item = items[i];
161  createContainerItem(item);
162  }
163 }
164 
165 void MusicSongsContainerWidget::importSongsByUrl(const QString &path, int playlistRow)
166 {
167  if(path.isEmpty())
168  {
169  return;
170  }
171 
172  if(playlistRow < 0)
173  {
174  playlistRow = makeValidIndex();
175  }
176 
177  const QFileInfo fin(path);
178  if(fin.isDir())
179  {
180  QStringList files;
181  for(const QFileInfo &fin : TTK::File::fileInfoListByPath(path))
182  {
184  {
185  files << fin.absoluteFilePath();
186  }
187  }
188 
189  importSongsByPath(files, playlistRow);
190  }
191  else if(TTK::String::isNetworkUrl(path))
192  {
194 
195  MusicResultDataItem item;
196  item.m_name = TTK::String::splitToken(path, "?", TTK_SEPARATOR, false);
197  item.m_id = item.m_name;
198  item.m_nickName = path;
200  item.m_title = MUSIC_PLAY_LATER;
201 
202  const qint64 size = TTK::fetchFileSizeByUrl(path);
203  item.m_count = QString::number(size);
204 
205  if(item.m_description == MP3_FILE_SUFFIX)
206  {
207  item.m_time = TTKTime::formatDuration(size * 8 / TTK_BN_128);
208  }
209  else if(item.m_description == APE_FILE_SUFFIX || item.m_description == FLAC_FILE_SUFFIX)
210  {
211  item.m_time = TTKTime::formatDuration(size * 8 / TTK_BN_1000);
212  }
213 
215  }
216  else
217  {
218  QStringList files;
220  {
221  files << fin.absoluteFilePath();
222  }
223 
224  importSongsByPath(files, playlistRow);
225  }
226 }
227 
228 void MusicSongsContainerWidget::importSongsByPath(const QStringList &files, int playlistRow)
229 {
230  if(files.isEmpty())
231  {
232  return;
233  }
234 
235  MusicProgressWidget progress;
236  progress.setTitle(tr("Import file mode"));
237  progress.setRange(0, files.count());
238  progress.show();
239 
240  importSongsWithProgress(&progress, 0, files, playlistRow);
241 
242  MusicToastLabel::popup(tr("Import music songs done"));
243 }
244 
245 QStringList MusicSongsContainerWidget::songsFileName(int index) const
246 {
247  QStringList list;
248 
249  if(index < 0 || index >= m_containerItems.count())
250  {
251  return list;
252  }
253 
254  for(const MusicSong &song : qAsConst(m_containerItems[index].m_songs))
255  {
256  list << song.name();
257  }
258  return list;
259 }
260 
261 QStringList MusicSongsContainerWidget::songsFilePath(int index) const
262 {
263  QStringList list;
264  if(index < 0 || index >= m_containerItems.count())
265  {
266  return list;
267  }
268 
269  for(const MusicSong &song : qAsConst(m_containerItems[index].m_songs))
270  {
271  list << song.path();
272  }
273  return list;
274 }
275 
276 int MusicSongsContainerWidget::mapSongIndexByFilePath(int playlistRow, const QString &path) const
277 {
278  if(playlistRow < 0 || playlistRow >= m_containerItems.count() || path.isEmpty())
279  {
280  return -1;
281  }
282 
283  const MusicSongList *songs = &m_containerItems[playlistRow].m_songs;
284  for(int i = 0; i < songs->count(); ++i)
285  {
286  if(MusicSong(path) == songs->at(i))
287  {
288  return i;
289  }
290  }
291  return -1;
292 }
293 
294 QString MusicSongsContainerWidget::mapFilePathBySongIndex(int playlistRow, int index) const
295 {
296  if(playlistRow < 0 || playlistRow >= m_containerItems.count())
297  {
298  return {};
299  }
300 
301  const MusicSongList *songs = &m_containerItems[playlistRow].m_songs;
302  if(index < 0 || index >= songs->count())
303  {
304  return {};
305  }
306 
307  return songs->at(index).path();
308 }
309 
311 {
313  {
314  return;
315  }
316 
317  const TTKIntList &result = m_searchResultItems.value(m_searchResultLevel);
318  if(row >= result.count() || row < 0)
319  {
320  row = -1;
321  return;
322  }
323 
324  row = result[row];
325 
328 }
329 
331 {
332  const int before = m_playRowIndex;
333  m_playRowIndex = index;
334 
335  if(before >= 0)
336  {
338  if(widget && !m_containerItems[before].m_songs.isEmpty())
339  {
340  widget->adjustPlayWidgetRow();
341  }
342  }
343 }
344 
346 {
347  if(!hasSearchResult())
348  {
349  selectRow(index);
350  resizeScrollIndex(index * 30);
351  }
352 }
353 
355 {
357  if(m_playRowIndex < 0)
358  {
359  return;
360  }
361 
363  m_containerItems[m_playRowIndex].m_itemWidget->selectRow(index);
364 }
365 
366 void MusicSongsContainerWidget::updateDurationLabel(const QString &current, const QString &total) const
367 {
368  if(m_playRowIndex < 0)
369  {
370  return;
371  }
372  TTKObjectCast(MusicSongsListPlayTableWidget*, m_containerItems[m_playRowIndex].m_itemWidget)->updateDurationLabel(current, total);
373 }
374 
376 {
377  if(m_containerItems.count() <= ITEM_MAX_COUNT)
378  {
379  int id = 0;
380  for(const MusicSongItem &item : qAsConst(m_containerItems))
381  {
382  if(id < item.m_id)
383  {
384  id = item.m_id;
385  }
386  }
387 
388  MusicSongItem item;
389  item.m_id = id + 1;
390  item.m_itemName = tr("Default Item");
391 
393  createContainerItem(item);
394  }
395  else
396  {
397  MusicToastLabel::popup(tr("Exceeded the maximum number limit"));
398  }
399 }
400 
402 {
403  const int id = foundMappedIndex(index);
404  if(id == -1)
405  {
406  return;
407  }
408 
410  message.setText(tr("Are you sure to delete?"));
411  if(!message.exec())
412  {
413  return;
414  }
415 
416  const MusicSongItem *item = &m_containerItems[id];
418 
419  if(m_playRowIndex == id)
420  {
422  m_items.first().m_itemWidget->setExpand(false);
424  }
425  else if(m_playRowIndex > id)
426  {
428  }
429 
430  m_containerItems.takeAt(id);
431  removeItem(item->m_itemWidget);
432  delete item->m_itemWidget;
433 
434  updatePlayedRows(id, -1);
435 }
436 
438 {
440  message.setText(tr("Are you sure to delete?"));
441  if(!message.exec())
442  {
443  return;
444  }
445 
447  {
449  m_items.first().m_itemWidget->setExpand(false);
451  }
452 
453  for(int i = m_containerItems.count() - 1; i > 3; --i)
454  {
455  MusicSongItem item = m_containerItems.takeLast();
456  removeItem(item.m_itemWidget);
457  delete item.m_itemWidget;
458  }
459 }
460 
462 {
463  const int id = foundMappedIndex(index);
464  if(id == -1)
465  {
466  return;
467  }
468 
470 
471  m_selectDeleteIndex = id;
472  //
474  if(widget->rowCount() > 0)
475  {
476  widget->setCurrentCell(0, 1);
477  widget->clearItems();
478  }
479  //
481 
482  if(m_containerItems[id].m_songs.isEmpty() && m_playRowIndex == id)
483  {
485  }
486 }
487 
488 void MusicSongsContainerWidget::changRowItemName(int index, const QString &name)
489 {
490  const int id = foundMappedIndex(index);
491  if(id == -1)
492  {
493  return;
494  }
495 
496  bool found = false;
497  for(int i = 0; i < m_containerItems.count(); ++i)
498  {
499  if(i == id)
500  {
501  continue;
502  }
503 
504  if(m_containerItems[i].m_itemName == name)
505  {
506  found = true;
507  }
508  }
509 
510  MusicSongItem *item = &m_containerItems[id];
511  if(found)
512  {
513  MusicToastLabel::popup(tr("Item name is already exists"));
514  setItemTitle(item);
515  return;
516  }
517 
518  item->m_itemName = name;
519  setItemTitle(item);
520 }
521 
523 {
524  start = foundMappedIndex(start);
525  end = foundMappedIndex(end);
526  if(start == end)
527  {
528  return;
529  }
530 
531  //adjust the m_playRowIndex while the item has dragged and dropped
532  if(start < end)
533  {
534  if((start < m_playRowIndex && m_playRowIndex < end) || m_playRowIndex == end)
535  {
537  }
538  else if(m_playRowIndex == start)
539  {
541  }
542  }
543  else
544  {
545  if((end < m_playRowIndex && m_playRowIndex < start) || m_playRowIndex == end)
546  {
548  }
549  else if(m_playRowIndex == start)
550  {
552  }
553  }
554 
555  swapItem(start, end);
556  const MusicSongItem &item = m_containerItems.takeAt(start);
557  m_containerItems.insert(end, item);
558 
559  updatePlayedRows(start, end);
560 }
561 
563 {
564  const int id = foundMappedIndex(index);
565  if(id == -1)
566  {
567  return;
568  }
569 
570  const MusicSongItem *item = &m_containerItems[id];
572 }
573 
575 {
576  const int id = foundMappedIndex(index);
577  if(id == -1)
578  {
579  return;
580  }
581 
582  const MusicSongItem *item = &m_containerItems[id];
584 }
585 
587 {
588  if(index == TTK_LOW_LEVEL)
589  {
590  index = m_currentIndex;
591  }
592  else
593  {
594  const int id = foundMappedIndex(index);
595  if(id == -1)
596  {
597  return;
598  }
599 
600  index = id;
601  }
602 
604 }
605 
607 {
608  if(index == TTK_LOW_LEVEL)
609  {
610  index = m_currentIndex;
611  }
612  else
613  {
614  const int id = foundMappedIndex(index);
615  if(id == -1)
616  {
617  return;
618  }
619 
620  index = id;
621  }
622 
623  const QString &path = TTK::File::getExistingDirectory(this);
624  if(path.isEmpty())
625  {
626  return;
627  }
628 
629  importSongsByUrl(path, index);
630 }
631 
633 {
635 }
636 
638 {
640 }
641 
643 {
644  if(m_currentIndex == -1)
645  {
646  return;
647  }
648 
649  if(!isSearchedPlayIndex())
650  {
651  const QStringList searchedSongs(songsFileName(m_lastSearchIndex));
652  TTKIntList result;
653  for(int i = 0; i < searchedSongs.count(); ++i)
654  {
655  result << i;
656  }
657 
659  TTKObjectCast(MusicSongsListPlayTableWidget*, item->m_itemWidget)->updateSearchFileName(&item->m_songs, result);
660 
661  if(item->m_songs.isEmpty())
662  {
663  item->m_itemWidget->updateSongsList(item->m_songs);
664  }
665 
668  }
669 
670  const QString &text = m_songSearchWidget->text();
671  const QStringList searchedSongs(songsFileName(m_currentIndex));
672 
673  TTKIntList result;
674  for(int i = 0; i < searchedSongs.count(); ++i)
675  {
676  if(searchedSongs[i].contains(text, Qt::CaseInsensitive))
677  {
678  result << i;
679  }
680  }
681 
682  m_searchResultLevel = column;
683  m_searchResultItems.insert(column, result);
684 
686  TTKObjectCast(MusicSongsListPlayTableWidget*, item->m_itemWidget)->updateSearchFileName(&item->m_songs, result);
687 
688  if(column == 0)
689  {
690  if(item->m_songs.isEmpty())
691  {
692  item->m_itemWidget->updateSongsList(item->m_songs);
693  }
694 
696  }
697 }
698 
700 {
701  const QStringList &lastPlayIndex = G_SETTING_PTR->value(MusicSettingManager::LastPlayIndex).toStringList();
702  if(lastPlayIndex.count() != 3)
703  {
704  return;
705  }
706 
707  m_playRowIndex = lastPlayIndex[1].toInt();
708  const int index = lastPlayIndex[2].toInt();
709  setCurrentIndex(index);
710  setSongPlayCount(index);
711 
713 }
714 
716 {
717  if(m_playRowIndex < 0 || m_playRowIndex >= m_containerItems.count())
718  {
719  return;
720  }
721 
722  const MusicSong &song = m_containerItems[m_playRowIndex].m_songs[row];
724 
726  if(state)
727  {
728  item->m_songs << song;
729  widget->updateSongsList(item->m_songs);
730  setItemTitle(item);
731  }
732  else
733  {
734  if(item->m_songs.removeOne(song))
735  {
736  widget->removeItems();
737  widget->updateSongsList(item->m_songs);
738  setItemTitle(item);
740  }
741  }
742 }
743 
745 {
746  if(m_currentIndex < 0 || m_currentIndex >= m_containerItems.count() || hasSearchResult())
747  {
748  return;
749  }
750 
751  const MusicSong &song = m_containerItems[m_currentIndex].m_songs[row];
753 
755  if(MusicSong(MusicApplication::instance()->currentFilePath()) == song)
756  {
758  return;
759  }
760 
762  if(state)
763  {
764  item->m_songs << song;
765  widget->updateSongsList(item->m_songs);
766  setItemTitle(item);
767  }
768  else
769  {
770  if(item->m_songs.removeOne(song))
771  {
772  widget->removeItems();
773  widget->updateSongsList(item->m_songs);
774  setItemTitle(item);
776  }
777  }
778 }
779 
781 {
783  const QByteArray &md5 = TTK::Algorithm::md5(item.m_id.toUtf8());
784  MusicSong song(item.m_nickName + "#" + md5 + "." + item.m_description, item.m_time, item.m_name);
785  song.setFormat(item.m_description);
786  song.setSizeStr(item.m_count);
787 
788  int index = it->m_songs.indexOf(song);
789  if(index == -1)
790  {
791  it->m_songs << song;
793  setItemTitle(it);
794  index = it->m_songs.count() - 1;
795 
796  // write song meta info to file
797  TTK::generateNetworkSongMeta(md5, item);
798  }
799 
800  if(item.m_title == MUSIC_PLAY_NOW)
801  {
805  }
806 }
807 
808 void MusicSongsContainerWidget::addSongToPlaylist(const QStringList &items)
809 {
810  if(items.isEmpty())
811  {
812  MusicToastLabel::popup(tr("Import music songs is empty"));
813  return;
814  }
815 
816  QStringList files(items);
817  const int row = makeValidIndex();
818  importSongsByPath(files, row);
819 
820  const MusicSongItem *item = &m_containerItems[row];
821  const MusicSongList *musicSongs = &item->m_songs;
822  const MusicSong &song = MusicSong(items.last());
823 
824  int index = musicSongs->count() - 1;
825  if(musicSongs->contains(song))
826  {
827  index = musicSongs->indexOf(song);
828  }
829 
831  setCurrentIndex(row);
833 }
834 
835 void MusicSongsContainerWidget::removeItemAt(const TTKIntList &rows, bool fileRemove)
836 {
837  if(rows.isEmpty() || hasSearchResult())
838  {
839  return;
840  }
841 
844  QStringList deleteFiles;
845  for(int i = rows.count() - 1; i >= 0; --i)
846  {
847  const MusicSong &song = item->m_songs.takeAt(rows[i]);
848  deleteFiles << song.path();
849  if(currentIndex != m_playRowIndex && currentIndex == MUSIC_LOVEST_LIST)
850  {
851  const int playIndex = m_containerItems[m_playRowIndex].m_itemWidget->playRowIndex();
852  const MusicSongList &songs = m_containerItems[m_playRowIndex].m_songs;
853  if(playIndex > -1 && playIndex < songs.count())
854  {
855  if(songs[playIndex] == song)
856  {
858  }
859  }
860  }
861 
862  if(fileRemove)
863  {
864  if(currentIndex != MUSIC_NETWORK_LIST)
865  {
866  QFile::remove(song.path());
867  }
868  else
869  {
870  QFile::remove(TTK::generateNetworkSongPath(song.path()));
871  QFile::remove(TTK::generateNetworkSongMetaPath(song.path()));
872  }
873  }
874  }
875 
876  MusicApplication::instance()->removeItemAt(deleteFiles, fileRemove, currentIndex == m_playRowIndex, currentIndex);
877 
878  setItemTitle(item);
879  //create upload file widget if current items is all been deleted
880  TTKObjectCast(MusicSongsListPlayTableWidget*, item->m_itemWidget)->createUploadFileModule();
881 }
882 
883 void MusicSongsContainerWidget::itemRowSwaped(int start, int end, int play, MusicSongList &songs)
884 {
885  MusicSongList *names = &m_containerItems[m_currentIndex].m_songs;
886  if(start > end)
887  {
888  for(int i = start; i > end; --i)
889  {
890  QtContainerSwap(names, i, i - 1);
891  }
892  }
893  else
894  {
895  for(int i = start; i < end; ++i)
896  {
897  QtContainerSwap(names, i, i + 1);
898  }
899  }
900 
901  songs = *names;
902 
904  {
906  }
907 }
908 
910 {
912  state = (currentIndex == m_playRowIndex);
913 }
914 
916 {
917  empty = !hasSearchResult();
918 }
919 
921 {
922  if(index < 0 || m_playRowIndex < 0)
923  {
924  return;
925  }
926 
927  MusicSongList &songs = m_containerItems[m_playRowIndex].m_songs;
928  if(!songs.isEmpty() && index < songs.count())
929  {
930  MusicSong &song = songs[index];
931  song.setPlayCount(song.playCount() + 1);
932  }
933 }
934 
936 {
938  {
939  return;
940  }
941 
942  MusicSongList *songs = &m_containerItems[m_playRowIndex].m_songs;
943  if(songs->isEmpty() || index >= songs->count())
944  {
945  return;
946  }
947 
949  MusicSong recentSong(songs->at(index));
950  MusicSongList *recentSongs = &item->m_songs;
952  if(!recentSongs->contains(recentSong))
953  {
954  if(recentSongs->count() >= RECENT_ITEM_MAX_COUNT)
955  {
956  recentSongs->takeFirst();
957  widget->removeItems();
958  }
959 
960  recentSong.setPlayCount(recentSong.playCount() + 1);
961  recentSongs->append(recentSong);
962  widget->updateSongsList(*recentSongs);
963 
964  const QString title(QString("%1[%2]").arg(item->m_itemName).arg(recentSongs->count()));
965  setTitle(widget, title);
966  }
967  else
968  {
969  for(MusicSong &song : *recentSongs)
970  {
971  if(recentSong == song)
972  {
973  song.setPlayCount(song.playCount() + 1);
974  break;
975  }
976  }
977  }
978 }
979 
980 void MusicSongsContainerWidget::querySongItemList(MusicSongItemList &songs)
981 {
982  songs = m_containerItems;
983 }
984 
986 {
987  if(m_playRowIndex < 0)
988  {
989  return;
990  }
991 
992  TTKObjectCast(MusicSongsListPlayTableWidget*, m_containerItems[m_playRowIndex].m_itemWidget)->updateCurrentArtist();
993 }
994 
996 {
997  if(m_functionWidget == nullptr)
998  {
1000  connect(m_functionWidget, SIGNAL(deleteSelfObject()), SLOT(deleteFunctionWidget()));
1001  resizeGeometry();
1002  m_functionWidget->show();
1003  }
1004  else
1005  {
1006  resizeGeometry();
1008  }
1009 }
1010 
1012 {
1013  const int id = foundMappedIndex(index);
1014  if(id == -1)
1015  {
1016  return;
1017  }
1018 
1020 
1023  index = m_containerItems[id].m_sort.m_type;
1024  if(index != -1)
1025  {
1026  sort = TTKStaticCast(MusicSong::Sort, index);
1027  }
1028 
1029  MusicSongList *songs = &m_containerItems[id].m_songs;
1030  const MusicSong song(MusicApplication::instance()->currentFilePath());
1031 
1032  for(int i = 0; i < songs->count(); ++i)
1033  {
1034  (*songs)[i].setSort(sort);
1035  }
1036 
1037  if(m_containerItems[id].m_sort.m_order == Qt::DescendingOrder)
1038  {
1039  std::sort(songs->begin(), songs->end());
1040  }
1041  else
1042  {
1043  std::sort(songs->begin(), songs->end(), std::greater<MusicSong>());
1044  }
1045 
1046  widget->removeItems();
1047  widget->setSongsList(songs);
1048 
1049  index = songs->indexOf(song);
1051  {
1053  }
1054 }
1055 
1057 {
1058  if(m_songSearchWidget == nullptr)
1059  {
1061  resizeGeometry();
1062  }
1063 
1065 }
1066 
1068 {
1069  if(value >= 40 * (m_currentIndex + 1) && m_currentIndex > -1 && m_currentIndex < m_containerItems.count())
1070  {
1074  m_topMaskWidget->setTitle(QString("%1[%2]").arg(item->m_itemName).arg(item->m_songs.count()));
1075  m_topMaskWidget->setExpand(true);
1076  m_topMaskWidget->raise();
1077  m_topMaskWidget->show();
1078  }
1079  else
1080  {
1081  m_topMaskWidget->hide();
1082  }
1083 }
1084 
1086 {
1087  delete m_functionWidget;
1088  m_functionWidget = nullptr;
1089 }
1090 
1092 {
1093  MusicSongsToolBoxWidget::resizeEvent(event);
1094  resizeGeometry();
1095 }
1096 
1097 void MusicSongsContainerWidget::dragEnterEvent(QDragEnterEvent *event)
1098 {
1099  MusicSongsToolBoxWidget::dragEnterEvent(event);
1100  event->setDropAction(Qt::IgnoreAction);
1101  event->accept();
1102 }
1103 
1104 void MusicSongsContainerWidget::dragMoveEvent(QDragMoveEvent *event)
1105 {
1106  MusicSongsToolBoxWidget::dragMoveEvent(event);
1107 
1108  for(const Data &data : qAsConst(m_items))
1109  {
1110  if(!TTK::playlistRowValid(data.m_itemIndex))
1111  {
1112  continue;
1113  }
1114 
1115  data.m_itemWidget->isActive();
1116  }
1117 
1118  event->setDropAction(Qt::CopyAction);
1119  event->accept();
1120 }
1121 
1123 {
1124  MusicSongsToolBoxWidget::dropEvent(event);
1125 
1126  QStringList dirs, files;
1127  const QMimeData *data = event->mimeData();
1128 
1129  for(const QUrl &url : data->urls())
1130  {
1131  const QString &path = url.toLocalFile();
1132  if(QFileInfo(path).isDir())
1133  {
1134  dirs << path;
1135  }
1136  else
1137  {
1138  files << path;
1139  }
1140  }
1141 
1142  bool contains = false;
1143  for(const Data &data : qAsConst(m_items))
1144  {
1145  if(!TTK::playlistRowValid(data.m_itemIndex))
1146  {
1147  continue;
1148  }
1149 
1150  if(data.m_itemWidget->isExpand() || data.m_itemWidget->isActive())
1151  {
1152  contains = true;
1153  importSongsByPath(files, foundMappedIndex(data.m_itemIndex));
1154  break;
1155  }
1156  }
1157 
1158  if(!contains)
1159  {
1161  }
1162 
1163  if(!dirs.isEmpty())
1164  {
1165  int count = 0;
1166  QList<QStringList> files;
1167 
1168  for(const QString &dir : qAsConst(dirs))
1169  {
1171  count += files.last().count();
1172  }
1173 
1174  MusicProgressWidget progress;
1175  progress.setTitle(tr("Import dir mode"));
1176  progress.setRange(0, count);
1177  progress.show();
1178 
1179  count = 0;
1180  for(int i = 0; i < dirs.count(); ++i)
1181  {
1182  MusicSongItem item;
1183  item.m_itemName = QFileInfo(dirs[i]).baseName();
1184 
1186  item = *createContainerItem(item);
1187 
1188  importSongsWithProgress(&progress, count, files[i], foundMappedIndex(item.m_itemIndex));
1189 
1190  count += files[i].count();
1191  }
1192 
1193  MusicToastLabel::popup(tr("Import music songs done"));
1194  }
1195 }
1196 
1197 void MusicSongsContainerWidget::contextMenuEvent(QContextMenuEvent *event)
1198 {
1199  MusicSongsToolBoxWidget::contextMenuEvent(event);
1200 
1201  QMenu menu(this);
1202  menu.setStyleSheet(TTK::UI::MenuStyle02);
1203  menu.addAction(tr("Create Item"), this, SLOT(addNewRowItem()));
1204  menu.addAction(tr("Import Item"), MusicApplication::instance(), SLOT(importSongsItemList()));
1205  menu.addAction(tr("Export Items"), MusicApplication::instance(), SLOT(exportSongsItemList()));
1206  menu.addAction(tr("Delete All"), this, SLOT(deleteRowItems()))->setEnabled(m_containerItems.count() > ITEM_MIN_COUNT);
1207  menu.addAction(tr("Music Test Tools"), this, SLOT(showSongCheckToolsWidget()));
1208  menu.addAction(tr("Lrc Batch Download"), this, SLOT(showLrcDownloadBatchWidget()));
1209 
1211  menu.exec(QCursor::pos());
1212 }
1213 
1215 {
1216  if(m_songSearchWidget)
1217  {
1219  }
1220 }
1221 
1223 {
1224  if(hasSearchResult())
1225  {
1227  }
1228 }
1229 
1231 {
1232  QString check = name;
1233  for(int i = 1; i <= ITEM_MAX_COUNT; ++i)
1234  {
1235  bool found = false;
1236  for(const MusicSongItem &item : qAsConst(m_containerItems))
1237  {
1238  if(check == item.m_itemName)
1239  {
1240  found = true;
1241  check = name + QString::number(i);
1242  break;
1243  }
1244  }
1245 
1246  if(!found)
1247  {
1248  name = check;
1249  break;
1250  }
1251  }
1252 }
1253 
1255 {
1257  widget->setMovedScrollBar(m_scrollArea->verticalScrollBar());
1258  widget->setSongSort(&item->m_sort);
1259 
1260  item->m_itemWidget = widget;
1261  item->m_itemIndex = m_itemIndexRaise;
1262 
1263  addCellItem(widget, item->m_itemName);
1264  setSongSort(widget, &item->m_sort);
1266 
1267  connect(widget, SIGNAL(isCurrentPlaylistRow(bool&)), SLOT(isCurrentPlaylistRow(bool&)));
1268  connect(widget, SIGNAL(isSearchedResultEmpty(bool&)), SLOT(isSearchedResultEmpty(bool&)));
1269  connect(widget, SIGNAL(deleteItemAt(TTKIntList,bool)), SLOT(removeItemAt(TTKIntList,bool)));
1270  connect(widget, SIGNAL(itemRowSwaped(int,int,int,MusicSongList&)), SLOT(itemRowSwaped(int,int,int,MusicSongList&)));
1271  connect(widget, SIGNAL(addSongToLovestList(bool,int)), SLOT(addSongToLovestList(bool,int)));
1272  connect(widget, SIGNAL(showFloatWidget()), SLOT(showFloatWidget()));
1273  connect(widget, SIGNAL(songListSortBy(int)), SLOT(songListSortBy(int)));
1274 
1276  setInputModule(m_items.last().m_itemWidget);
1277 
1278  widget->setSongsList(&item->m_songs);
1279  setTitle(widget, QString("%1[%2]").arg(item->m_itemName).arg(item->m_songs.count()));
1280 }
1281 
1283 {
1284  m_containerItems << item;
1285  MusicSongItem *it = &m_containerItems.last();
1286  createWidgetItem(it);
1287  return it;
1288 }
1289 
1291 {
1292  const QString title(QString("%1[%2]").arg(item->m_itemName).arg(item->m_songs.count()));
1293  setTitle(item->m_itemWidget, title);
1294 
1295  if(m_topMaskWidget->isVisible() && m_topMaskWidget->index() == item->m_itemIndex)
1296  {
1297  m_topMaskWidget->setTitle(title);
1298  }
1299 }
1300 
1302 {
1303  connect(object, SIGNAL(addNewRowItem()), SLOT(addNewRowItem()));
1304  connect(object, SIGNAL(deleteRowItem(int)), SLOT(deleteRowItem(int)));
1305  connect(object, SIGNAL(deleteAllItems(int)), SLOT(deleteAllItems(int)));
1306  connect(object, SIGNAL(changRowItemName(int,QString)), SLOT(changRowItemName(int,QString)));
1307  connect(object, SIGNAL(addNewFiles(int)), SLOT(importSongsByFiles(int)));
1308  connect(object, SIGNAL(addNewDir(int)), SLOT(importSongsByDir(int)));
1309  connect(object, SIGNAL(songListSortBy(int)), SLOT(songListSortBy(int)));
1310  connect(object, SIGNAL(swapDragItemIndex(int,int)), SLOT(swapDragItemIndex(int,int)));
1311  connect(object, SIGNAL(addToPlayLater(int)), SLOT(addToPlayLater(int)));
1312  connect(object, SIGNAL(addToPlayedList(int)), SLOT(addToPlayedList(int)));
1313 }
1314 
1316 {
1317  if(m_functionWidget)
1318  {
1319  m_functionWidget->move(width() - m_functionWidget->width() - 15, height() - 40 - m_functionWidget->height());
1320  }
1321 
1322  if(m_songSearchWidget)
1323  {
1324  m_songSearchWidget->move(0, height() - m_songSearchWidget->height());
1325  }
1326 }
1327 
1329 {
1330  for(const MusicSongItem &item : qAsConst(m_containerItems))
1331  {
1332  const int index = foundMappedIndex(item.m_itemIndex);
1333  item.m_itemWidget->setPlaylistRow(index);
1334  }
1335 
1336  TTK::IndexPropertyList items;
1337  if(end == -1)
1338  {
1339  for(int i = begin + 1; i < m_containerItems.count(); ++i)
1340  {
1341  items << TTK::IndexProperty(i + 1, i);
1342  }
1343  }
1344  else
1345  {
1346  items << TTK::IndexProperty(begin, end);
1347 
1348  if(begin > end)
1349  {
1350  for(int i = begin; i < end; ++i)
1351  {
1352  items << TTK::IndexProperty(i + 1, i);
1353  }
1354  }
1355  else
1356  {
1357  for(int i = end; i > begin; --i)
1358  {
1359  items << TTK::IndexProperty(i, i - 1);
1360  }
1361  }
1362  }
1363 
1365 }
1366 
1367 void MusicSongsContainerWidget::importSongsWithProgress(QWidget *progress, int offset, const QStringList &files, int playlistRow)
1368 {
1369  if(!progress)
1370  {
1371  return;
1372  }
1373 
1374  if(playlistRow < 0)
1375  {
1376  playlistRow = makeValidIndex();
1377  }
1378 
1380 
1381  MusicSongItem *item = &m_containerItems[playlistRow];
1383 
1384  int i = 0;
1385  for(const QString &path : qAsConst(files))
1386  {
1387  if(item->m_songs.contains(MusicSong(path)))
1388  {
1389  continue;
1390  }
1391 
1392  widget->setValue(++i + offset);
1393  item->m_songs << TTK::generateSongList(path);
1394  }
1395 
1396  if(i != 0)
1397  {
1398  item->m_itemWidget->updateSongsList(item->m_songs);
1399  setItemTitle(item);
1400  setCurrentIndex(playlistRow);
1401  }
1402 }
TTK_MODULE_EXPORT qint64 fetchFileSizeByUrl(const QString &url)
void setTitle(QWidget *item, const QString &text)
TTK_MODULE_EXPORT QString getExistingDirectory(QWidget *parent)
#define TTKStaticCast(x, y)
Definition: ttkglobal.h:231
void importSongsByDir(int index=TTK_LOW_LEVEL)
The class of the songs container widget.
The class of the search result data item.
void updateDurationLabel(const QString &current, const QString &total) const
static QString message(const TTK::Attribute attr)
static MusicApplication * instance()
virtual void setVisible(bool visible) overridefinal
void itemRowSwaped(int start, int end, int play, MusicSongList &songs)
virtual void dragEnterEvent(QDragEnterEvent *event) overridefinal
void setText(const QString &text) const
TTK_MODULE_EXPORT void generateNetworkSongMeta(const QString &id, const MusicResultDataItem &item)
void removeItemAt(const TTKIntList &rows, bool fileRemove)
virtual void contextMenuEvent(QContextMenuEvent *event) overridefinal
#define QtContainerSwap(p, i, j)
Container swap.
Definition: ttkqtcompat.h:52
#define TTK_LOW_LEVEL
Definition: ttkglobal.h:332
The class of the show progress widget.
void setSongSort(MusicSongSort *sort) noexcept
void addSongToLovestList(bool state=true)
QString path() const noexcept
Definition: musicsong.h:89
static QStringList supportMusicInputFilterFormats() noexcept
void setSongSort(MusicSongSort *sort) noexcept
void songToLovestListAt(bool state, int row)
void importSongsByFiles(int index=TTK_LOW_LEVEL)
#define MUSIC_NONE_LIST
QStringList songsFilePath(int index) const
voidpf void uLong size
Definition: ioapi.h:136
void playIndexBy(int row)
void playSortBy(int row)
QStringList songsFileName(int index) const
bool addSongItemList(const MusicSongItemList &items)
static constexpr int RECENT_ITEM_MAX_COUNT
The class of the messagebox widget.
#define MUSIC_RECENT_LIST
The class of the lrc batch download widget.
MusicSongSearchDialog * m_songSearchWidget
#define TTK_BN_1000
Definition: ttkglobal.h:434
static MusicPlayedListPopWidget * instance()
void removeItemAt(const QStringList &path, bool remove, bool current, int playlistRow)
void setSongSort(QWidget *item, MusicSongSort *sort)
TTK_MODULE_EXPORT QString generateNetworkSongMetaPath(const QString &path)
The class of the index property.
Definition: musicobject.h:223
#define MP3_FILE_SUFFIX
Definition: musicobject.h:58
void updateSongItem(const MusicSongItem &item)
void setMovedScrollBar(QScrollBar *bar=nullptr)
TTK_MODULE_EXPORT QByteArray md5(const QByteArray &data, bool base64=false)
int mapSongIndexByFilePath(int playlistRow, const QString &path) const
void swapDragItemIndex(int start, int end)
void setTitle(const QString &name)
The class of the tool box mask widget.
static void popup(const QString &text)
MusicSongsListFunctionWidget * m_functionWidget
The class of the song check tools widget.
void addSongToPlaylist(const QStringList &items)
TTK_MODULE_EXPORT QString generateNetworkSongPath(const QString &path)
voidpf uLong offset
Definition: ioapi.h:142
virtual void updateSongsList(const MusicSongList &songs) overridefinal
#define MUSIC_PLAY_NOW
static constexpr int ITEM_MAX_COUNT
const MusicSongItemList & items() const noexcept
The class of the songs list function widget.
QList< int > TTKIntList
Definition: ttkqtglobal.h:200
#define qAsConst
Definition: ttkqtglobal.h:57
const char * name
Definition: http_parser.c:458
TTK_MODULE_EXPORT bool isNetworkUrl(const QString &path) noexcept
#define TTK_GENERATE_SINGLE_WIDGET(...)
void setInputModule(QObject *object) const
TTK_MODULE_EXPORT bool playlistRowValid(int index) noexcept
virtual void setSongsList(MusicSongList *songs)
static QString supportMusicInputFormats() noexcept
MusicAbstractSongsListTableWidget * m_itemWidget
Definition: musicsong.h:167
void addSongToLovestList(bool state, int row)
static QStringList supportMusicFormats() noexcept
void addCellItem(QWidget *item, const QString &text)
#define TTK_NORMAL_LEVEL
Definition: ttkglobal.h:333
void importSongsByPath(const QStringList &files, int playlistRow)
MusicSongsToolBoxMaskWidget * m_topMaskWidget
#define TTK_SEPARATOR
Definition: ttkglobal.h:269
void changRowItemName(int index, const QString &name)
QSet< int > TTKIntSet
Definition: ttkqtglobal.h:199
void playIndexClicked(int row, int column)
static MusicSongsContainerWidget * instance()
MusicSongsContainerWidget(QWidget *parent=nullptr)
TTK_MODULE_EXPORT QFileInfoList fileInfoListByPath(const QString &dpath, const QStringList &filter={}, bool recursively=true)
virtual void updateSongsList(const MusicSongList &songs)
void importSongsByUrl(const QString &path, int playlistRow)
void addSongBufferToPlaylist(const MusicResultDataItem &item)
void setItemTitle(MusicSongItem *item)
void querySongItemList(MusicSongItemList &songs)
void updatePlayedRows(int start, int end)
TTK_MODULE_EXPORT QString splitToken(const QString &name)
void updatePlayedRows(const TTK::IndexPropertyList &rows)
virtual void dropEvent(QDropEvent *event) overridefinal
static const QString MenuStyle02
void append(const MusicSongList &songs)
virtual void resizeEvent(QResizeEvent *event) overridefinal
The class of the tool box widget.
The class of the music song item.
Definition: musicsong.h:160
#define MUSIC_PLAY_LATER
MusicSongList m_songs
Definition: musicsong.h:166
#define MUSIC_NETWORK_LIST
#define MUSIC_LOVEST_LIST
void setPlayCount(const int c) noexcept
Definition: musicsong.h:113
#define MIN_ITEM_COUNT
void insert(int playlistRow, const MusicSong &song)
QString m_itemName
Definition: musicsong.h:164
void importSongsWithProgress(QWidget *progress, int offset, const QStringList &files, int playlistRow)
TTK_MODULE_EXPORT QStringList getOpenFileNames(QWidget *parent, const QString &filter="Image Files (*.png *.bmp *.jpg)")
TTK_MODULE_EXPORT QStringList fileListByPath(const QString &dpath, const QStringList &filter={}, bool recursively=true)
#define TTK_FILE_SUFFIX(fin)
Definition: ttkqtglobal.h:185
The class of the music song info.
Definition: musicsong.h:28
The class of the song search widget.
static MusicSongsContainerWidget * m_instance
void removeLoveItemAt(const QString &path, bool current)
void appendSongItemList(const MusicSongItemList &items)
MusicSongSort m_sort
Definition: musicsong.h:165
#define MUSIC_NORMAL_LIST
#define FLAC_FILE_SUFFIX
Definition: musicobject.h:60
MusicSongItem * createContainerItem(const MusicSongItem &item)
int playCount() const noexcept
Definition: musicsong.h:117
TTK_MODULE_EXPORT void adjustMenuPosition(QMenu *menu)
virtual void dragMoveEvent(QDragMoveEvent *event) overridefinal
QString mapFilePathBySongIndex(int playlistRow, int index) const
void searchResultChanged(int row, int column)
bool isSearchedPlayIndex() const noexcept
static constexpr int ITEM_MIN_COUNT
void createWidgetItem(MusicSongItem *item)
#define APE_FILE_SUFFIX
Definition: musicobject.h:59
state
Definition: http_parser.c:279
The class of the songs list play table widget.
#define TTK_BN_128
Definition: ttkglobal.h:428
#define G_SETTING_PTR
static qint64 formatDuration(const QString &time) noexcept
Definition: ttktime.cpp:123
TTK_MODULE_EXPORT MusicSongList generateSongList(const QString &path)
Definition: musicsong.cpp:92
#define TTKObjectCast(x, y)
Definition: ttkqtglobal.h:82