Merge lp:~daschuer/mixxx/features_ipod into lp:~mixxxdevelopers/mixxx/trunk

Proposed by Daniel Schürmann
Status: Needs review
Proposed branch: lp:~daschuer/mixxx/features_ipod
Merge into: lp:~mixxxdevelopers/mixxx/trunk
Diff against target: 1887 lines (+1709/-10)
13 files modified
mixxx/build/features.py (+9/-2)
mixxx/res/mixxx.qrc (+1/-0)
mixxx/src/dlgprefplaylist.cpp (+12/-0)
mixxx/src/dlgprefplaylistdlg.ui (+10/-0)
mixxx/src/library/ipod/gpoditdb.cpp (+64/-0)
mixxx/src/library/ipod/gpoditdb.h (+49/-0)
mixxx/src/library/ipod/ipodfeature.cpp (+405/-0)
mixxx/src/library/ipod/ipodfeature.h (+90/-0)
mixxx/src/library/ipod/ipodplaylistmodel.cpp (+901/-0)
mixxx/src/library/ipod/ipodplaylistmodel.h (+146/-0)
mixxx/src/library/library.cpp (+9/-0)
mixxx/src/widget/wlibrarysidebar.cpp (+11/-6)
mixxx/src/widget/wtracktableview.cpp (+2/-2)
To merge this branch: bzr merge lp:~daschuer/mixxx/features_ipod
Reviewer Review Type Date Requested Status
Mixxx Development Team Pending
Review via email: mp+80743@code.launchpad.net

Description of the change

Features:
* New library item "iPod"
* Sorting and Filtering
* new icon for Rhythmbox
* iPod auto detection
* disable iPod feature in preferences

To post a comment you must log in.
Revision history for this message
RAFFI TEA (raffitea) wrote :

Hey Daniel,

Can you just say a word about your ipod auto detection?
What library have you used for that? Do we have dependencies to GTK now?
Have you tested on Windows, too?

Revision history for this message
Daniel Schürmann (daschuer) wrote :

I have tested it only with Ubuntu Lucid, Natty and Oneiric.

---

On Ubuntu the Ipod mounted automatically in /media/IPOD[x]
The ipod feature is looking there for default. "Auto detection" is just pushing it a bit :-).
It is based on code, which I have already found in Mixxx Sources and which also has untested parts for Mac and Windows.

---

The ipod feature drew dependencies to the Glib, not to whole GTK.
The libgpod is based on Glib, which implements the object model for GTK.
On Gnome based systems, Mixxx has GTK dependencies anyway.

lp:~daschuer/mixxx/features_ipod updated
2925. By Daniel Schürmann

merged with lp:mixxx

2926. By Daniel Schürmann

merged with lp:mixxx

2927. By Daniel Schürmann

merged with lp:mixxx

2928. By Daniel Schürmann

merged with lp:mixxx

Unmerged revisions

2928. By Daniel Schürmann

merged with lp:mixxx

2927. By Daniel Schürmann

merged with lp:mixxx

2926. By Daniel Schürmann

merged with lp:mixxx

2925. By Daniel Schürmann

merged with lp:mixxx

2924. By Daniel Schürmann

merged with lp:mixxx

2923. By Daniel Schürmann

brach now working

2922. By Daniel Schürmann

added ipod files

2921. By Daniel Schürmann

merged from daschuers_trunk

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'mixxx/build/features.py'
--- mixxx/build/features.py 2012-09-26 05:07:07 +0000
+++ mixxx/build/features.py 2012-11-18 17:09:22 +0000
@@ -276,7 +276,7 @@
276276
277class IPod(Feature):277class IPod(Feature):
278 def description(self):278 def description(self):
279 return "NOT-WORKING iPod Support"279 return "Guest's iPod Support"
280280
281 def enabled(self, build):281 def enabled(self, build):
282 build.flags['ipod'] = util.get_flags(build.env, 'ipod', 0)282 build.flags['ipod'] = util.get_flags(build.env, 'ipod', 0)
@@ -291,7 +291,12 @@
291 if not self.enabled(build):291 if not self.enabled(build):
292 return292 return
293293
294 gpod_found = conf.CheckLib(['libgpod','gpod'])
294 build.env.Append(CPPDEFINES = '__IPOD__')295 build.env.Append(CPPDEFINES = '__IPOD__')
296
297 if not gpod_found:
298 raise Exception('Could not find libgpod or its development headers. Please install it or compile Mixxx without iPod support using the ipod=0 flag.')
299
295 if build.platform_is_windows:300 if build.platform_is_windows:
296 build.env.Append(LIBS = 'gpod');301 build.env.Append(LIBS = 'gpod');
297 # You must check v-this-v directory out from http://publicsvn.songbirdnest.com/vendor-binaries/trunk/windows-i686-msvc8/libgpod/302 # You must check v-this-v directory out from http://publicsvn.songbirdnest.com/vendor-binaries/trunk/windows-i686-msvc8/libgpod/
@@ -309,7 +314,9 @@
309 build.env.ParseConfig('pkg-config glib-2.0 --silence-errors --cflags --libs')314 build.env.ParseConfig('pkg-config glib-2.0 --silence-errors --cflags --libs')
310315
311 def sources(self, build):316 def sources(self, build):
312 return ['wipodtracksmodel.cpp']317 return ['library/ipod/ipodfeature.cpp',
318 'library/ipod/ipodplaylistmodel.cpp',
319 'library/ipod/gpoditdb.cpp']
313320
314class MSVCDebug(Feature):321class MSVCDebug(Feature):
315 # FIXME: this flag is also detected in mixxx.py at line 100 because it's needed sooner than this is processed322 # FIXME: this flag is also detected in mixxx.py at line 100 because it's needed sooner than this is processed
316323
=== added file 'mixxx/res/images/library/ic_library_ipod.png'
317Binary files mixxx/res/images/library/ic_library_ipod.png 1970-01-01 00:00:00 +0000 and mixxx/res/images/library/ic_library_ipod.png 2012-11-18 17:09:22 +0000 differ324Binary files mixxx/res/images/library/ic_library_ipod.png 1970-01-01 00:00:00 +0000 and mixxx/res/images/library/ic_library_ipod.png 2012-11-18 17:09:22 +0000 differ
=== removed file 'mixxx/res/images/library/ic_library_rhythmbox.png'
318Binary files mixxx/res/images/library/ic_library_rhythmbox.png 2012-07-06 08:24:39 +0000 and mixxx/res/images/library/ic_library_rhythmbox.png 1970-01-01 00:00:00 +0000 differ325Binary files mixxx/res/images/library/ic_library_rhythmbox.png 2012-07-06 08:24:39 +0000 and mixxx/res/images/library/ic_library_rhythmbox.png 1970-01-01 00:00:00 +0000 differ
=== modified file 'mixxx/res/mixxx.qrc'
--- mixxx/res/mixxx.qrc 2012-09-06 05:11:40 +0000
+++ mixxx/res/mixxx.qrc 2012-11-18 17:09:22 +0000
@@ -34,6 +34,7 @@
34 <file>images/library/ic_library_recordings.png</file>34 <file>images/library/ic_library_recordings.png</file>
35 <file>images/library/ic_library_rhythmbox.png</file>35 <file>images/library/ic_library_rhythmbox.png</file>
36 <file>images/library/ic_library_traktor.png</file>36 <file>images/library/ic_library_traktor.png</file>
37 <file>images/library/ic_library_ipod.png</file>
37 <file>shaders/computemaxsignal.frag</file>38 <file>shaders/computemaxsignal.frag</file>
38 <file>shaders/filteredsignal.frag</file>39 <file>shaders/filteredsignal.frag</file>
39 <file>shaders/passthrough.vert</file>40 <file>shaders/passthrough.vert</file>
4041
=== modified file 'mixxx/src/dlgprefplaylist.cpp'
--- mixxx/src/dlgprefplaylist.cpp 2012-07-09 21:38:03 +0000
+++ mixxx/src/dlgprefplaylist.cpp 2012-11-18 17:09:22 +0000
@@ -51,6 +51,10 @@
51 //connect(pushButtonM4A, SIGNAL(clicked()), this, SLOT(slotM4ACheck()));51 //connect(pushButtonM4A, SIGNAL(clicked()), this, SLOT(slotM4ACheck()));
52 connect(pushButtonExtraPlugins, SIGNAL(clicked()), this, SLOT(slotExtraPlugins()));52 connect(pushButtonExtraPlugins, SIGNAL(clicked()), this, SLOT(slotExtraPlugins()));
5353
54#ifndef __IPOD__
55 checkBox_show_ipod->hide();
56#endif // __IPOD__
57
54 if (!PromoTracksFeature::isSupported(config))58 if (!PromoTracksFeature::isSupported(config))
55 {59 {
56 groupBoxBundledSongs->hide();60 groupBoxBundledSongs->hide();
@@ -146,6 +150,9 @@
146 checkBox_use_relative_path->setChecked((bool)config->getValueString(ConfigKey("[Library]","UseRelativePathOnExport")).toInt());150 checkBox_use_relative_path->setChecked((bool)config->getValueString(ConfigKey("[Library]","UseRelativePathOnExport")).toInt());
147 checkBox_show_rhythmbox->setChecked((bool)config->getValueString(ConfigKey("[Library]","ShowRhythmboxLibrary"),"1").toInt());151 checkBox_show_rhythmbox->setChecked((bool)config->getValueString(ConfigKey("[Library]","ShowRhythmboxLibrary"),"1").toInt());
148 checkBox_show_itunes->setChecked((bool)config->getValueString(ConfigKey("[Library]","ShowITunesLibrary"),"1").toInt());152 checkBox_show_itunes->setChecked((bool)config->getValueString(ConfigKey("[Library]","ShowITunesLibrary"),"1").toInt());
153 #ifdef __IPOD__
154 checkBox_show_ipod->setChecked((bool)config->getValueString(ConfigKey("[Library]","ShowIpod"),"1").toInt());
155 #endif // __IPOD__
149 checkBox_show_traktor->setChecked((bool)config->getValueString(ConfigKey("[Library]","ShowTraktorLibrary"),"1").toInt());156 checkBox_show_traktor->setChecked((bool)config->getValueString(ConfigKey("[Library]","ShowTraktorLibrary"),"1").toInt());
150}157}
151158
@@ -180,6 +187,11 @@
180 config->set(ConfigKey("[Library]","ShowITunesLibrary"),187 config->set(ConfigKey("[Library]","ShowITunesLibrary"),
181 ConfigValue((int)checkBox_show_itunes->isChecked()));188 ConfigValue((int)checkBox_show_itunes->isChecked()));
182189
190#ifdef __IPOD__
191 config->set(ConfigKey("[Library]","ShowIpod"),
192 ConfigValue((int)checkBox_show_ipod->isChecked()));
193#endif // __IPOD__
194
183 config->set(ConfigKey("[Library]","ShowTraktorLibrary"),195 config->set(ConfigKey("[Library]","ShowTraktorLibrary"),
184 ConfigValue((int)checkBox_show_traktor->isChecked()));196 ConfigValue((int)checkBox_show_traktor->isChecked()));
185197
186198
=== modified file 'mixxx/src/dlgprefplaylistdlg.ui'
--- mixxx/src/dlgprefplaylistdlg.ui 2012-06-09 22:03:56 +0000
+++ mixxx/src/dlgprefplaylistdlg.ui 2012-11-18 17:09:22 +0000
@@ -181,6 +181,16 @@
181 </property>181 </property>
182 </widget>182 </widget>
183 </item>183 </item>
184 <item row="4" column="0">
185 <widget class="QCheckBox" name="checkBox_show_ipod">
186 <property name="text">
187 <string>Show iPod</string>
188 </property>
189 <property name="checked">
190 <bool>true</bool>
191 </property>
192 </widget>
193 </item>
184 </layout>194 </layout>
185 </widget>195 </widget>
186 </item>196 </item>
187197
=== added directory 'mixxx/src/library/ipod'
=== added file 'mixxx/src/library/ipod/gpoditdb.cpp'
--- mixxx/src/library/ipod/gpoditdb.cpp 1970-01-01 00:00:00 +0000
+++ mixxx/src/library/ipod/gpoditdb.cpp 2012-11-18 17:09:22 +0000
@@ -0,0 +1,64 @@
1/*
2 * GPodItdb.cpp
3 *
4 * Created on: 01.02.2012
5 * Author: Daniel Schürmann
6 */
7
8#include <QLibrary>
9#include <QtDebug>
10#include <QDir>
11#include <QMessageBox>
12
13#include "gpoditdb.h"
14
15
16GPodItdb::GPodItdb() :
17 m_itdb(NULL),
18 m_libGPodLoaded(true) {
19
20 // Load shared library
21 QLibrary libGPod("libgpod");
22
23 fp_itdb_free = (itdb_free__)libGPod.resolve("itdb_free");
24 if(!fp_itdb_free) m_libGPodLoaded = false;
25
26 fp_itdb_playlist_mpl = (itdb_playlist_mpl__)libGPod.resolve("itdb_playlist_mpl");
27 if(!fp_itdb_playlist_mpl) m_libGPodLoaded = false;
28
29 fp_itdb_parse = (itdb_parse__)libGPod.resolve("itdb_parse");
30 if(!fp_itdb_parse) m_libGPodLoaded = false;
31
32 qDebug() << "GPodItdb: try to resolve libgpod functions: " << (m_libGPodLoaded?"success":"failed");
33}
34
35GPodItdb::~GPodItdb() {
36 if (m_itdb) {
37 fp_itdb_free(m_itdb);
38 }
39}
40
41void GPodItdb::parse(const QString& mount, GError **error) {
42
43 if (m_itdb) {
44 fp_itdb_free(m_itdb);
45 m_itdb = NULL;
46 }
47 m_itdb = fp_itdb_parse(QDir::toNativeSeparators(mount).toLocal8Bit(), error);
48}
49
50Itdb_Playlist* GPodItdb::getFirstPlaylist() {
51 m_playlistNode = g_list_first(m_itdb->playlists);
52 if (m_playlistNode) {
53 return (Itdb_Playlist*)m_playlistNode->data;
54 }
55 return NULL;
56}
57
58Itdb_Playlist* GPodItdb::getNextPlaylist() {
59 m_playlistNode = g_list_next(m_playlistNode);
60 if (m_playlistNode) {
61 return (Itdb_Playlist*)m_playlistNode->data;
62 }
63 return NULL;
64}
065
=== added file 'mixxx/src/library/ipod/gpoditdb.h'
--- mixxx/src/library/ipod/gpoditdb.h 1970-01-01 00:00:00 +0000
+++ mixxx/src/library/ipod/gpoditdb.h 2012-11-18 17:09:22 +0000
@@ -0,0 +1,49 @@
1/*
2 * gpoditdb.h
3 *
4 * Created on: 01.02.2012
5 * Author: daniel
6 */
7
8#include <QObject>
9
10extern "C"
11{
12#include <gpod/itdb.h>
13}
14
15#ifndef GPODITDB_H_
16#define GPODITDB_H_
17
18class GPodItdb {
19public:
20 GPodItdb();
21 virtual ~GPodItdb();
22 bool isSupported() { return m_libGPodLoaded; };
23 void parse(const QString& mount, GError **error);
24 Itdb_Playlist* getFirstPlaylist();
25 Itdb_Playlist* getNextPlaylist();
26 Itdb_Playlist* getMasterPlaylist() { return fp_itdb_playlist_mpl(m_itdb); };
27 Itdb_Playlist* getCurrentPlaylist() { return m_pCurrentPlaylist; };
28 void setCurrentPlaylist(Itdb_Playlist* pl) { m_pCurrentPlaylist = pl; };
29
30 typedef int (*itdb_free__)(Itdb_iTunesDB *itdb);
31 itdb_free__ fp_itdb_free;
32
33 typedef Itdb_Playlist* (*itdb_playlist_mpl__)(Itdb_iTunesDB *itdb);
34 itdb_playlist_mpl__ fp_itdb_playlist_mpl;
35
36 typedef Itdb_iTunesDB* (*itdb_parse__)(const gchar *mp, GError **error);
37 itdb_parse__ fp_itdb_parse;
38
39 typedef gboolean (*itdb_playlist_is_mpl__)(Itdb_Playlist *pl);
40 itdb_playlist_is_mpl__ fp_itdb_playlist_is_mpl;
41
42private:
43 Itdb_iTunesDB* m_itdb;
44 bool m_libGPodLoaded;
45 GList* m_playlistNode;
46 Itdb_Playlist *m_pCurrentPlaylist;
47};
48
49#endif /* GPODITDB_H_ */
050
=== added file 'mixxx/src/library/ipod/ipodfeature.cpp'
--- mixxx/src/library/ipod/ipodfeature.cpp 1970-01-01 00:00:00 +0000
+++ mixxx/src/library/ipod/ipodfeature.cpp 2012-11-18 17:09:22 +0000
@@ -0,0 +1,405 @@
1#include <QMessageBox>
2#include <QtDebug>
3#include <QXmlStreamReader>
4#include <QDesktopServices>
5#include <QFileDialog>
6#include <QMenu>
7#include <QAction>
8
9#include "library/ipod/ipodfeature.h"
10#include "library/ipod/gpoditdb.h"
11
12#include "library/itunes/itunestrackmodel.h"
13#include "library/ipod/ipodplaylistmodel.h"
14#include "library/dao/settingsdao.h"
15
16extern "C" {
17#include <glib-object.h> // g_type_init
18}
19
20const QString IPodFeature::IPOD_MOUNT_KEY = "mixxx.IPodFeature.mount";
21
22
23IPodFeature::IPodFeature(QObject* parent, TrackCollection* pTrackCollection)
24 : LibraryFeature(parent),
25 m_pTrackCollection(pTrackCollection),
26 m_isActivated(false),
27 m_cancelImport(false),
28 m_itdb(NULL)
29{
30 m_title = tr("iPod");
31 m_pIPodPlaylistModel = new IPodPlaylistModel(this, m_pTrackCollection);
32
33 m_gPodItdb = new GPodItdb();
34
35 connect(&m_future_watcher, SIGNAL(finished()), this, SLOT(onTrackCollectionLoaded()));
36
37 m_pAddToAutoDJAction = new QAction(tr("Add to Auto DJ bottom"),this);
38 connect(m_pAddToAutoDJAction, SIGNAL(triggered()),
39 this, SLOT(slotAddToAutoDJ()));
40
41 m_pAddToAutoDJTopAction = new QAction(tr("Add to Auto DJ top 2"),this);
42 connect(m_pAddToAutoDJTopAction, SIGNAL(triggered()),
43 this, SLOT(slotAddToAutoDJTop()));
44
45 m_pImportAsMixxxPlaylistAction = new QAction(tr("Import as Mixxx Playlist"), this);
46 connect(m_pImportAsMixxxPlaylistAction, SIGNAL(triggered()),
47 this, SLOT(slotImportAsMixxxPlaylist()));
48}
49
50IPodFeature::~IPodFeature() {
51 qDebug() << "~IPodFeature()";
52 // stop import thread, if still running
53 m_cancelImport = true;
54 if (m_future.isRunning()) {
55 qDebug() << "m_future still running";
56 m_future.waitForFinished();
57 qDebug() << "m_future finished";
58 }
59 if (m_itdb) {
60 itdb_free(m_itdb);
61 }
62 delete m_pIPodPlaylistModel;
63 delete m_pAddToAutoDJAction;
64 delete m_pAddToAutoDJTopAction;
65 delete m_pImportAsMixxxPlaylistAction;
66 delete m_gPodItdb;
67}
68
69// static
70bool IPodFeature::isSupported() {
71 // The iPod might be mount at any time at any location
72 return true; //m_gPodItdb->isSupported();
73}
74
75
76QVariant IPodFeature::title() {
77 return m_title;
78}
79
80QIcon IPodFeature::getIcon() {
81 return QIcon(":/images/library/ic_library_ipod.png");
82}
83
84void IPodFeature::activate() {
85 activate(false);
86}
87
88void IPodFeature::activate(bool forceReload) {
89 //qDebug("IPodFeature::activate()");
90
91 if (!m_isActivated || forceReload) {
92
93 SettingsDAO settings(m_pTrackCollection->getDatabase());
94
95 QString dbSetting(settings.getValue(IPOD_MOUNT_KEY));
96 // if a path exists in the database, use it
97 if (!dbSetting.isEmpty() && QFile::exists(dbSetting)) {
98 m_dbfile = dbSetting;
99 } else {
100 // No Path in settings
101 m_dbfile = "";
102 }
103
104 m_dbfile = detectMountPoint(m_dbfile);
105
106 if (!QFile::exists(m_dbfile)) {
107 m_dbfile = QFileDialog::getExistingDirectory(
108 NULL,
109 tr("Select your iPod mount"));
110 if (m_dbfile.isEmpty()) {
111 emit(showTrackModel(m_pIPodPlaylistModel));
112 return;
113 }
114 }
115
116 m_isActivated = true;
117
118 QThreadPool::globalInstance()->setMaxThreadCount(4); //Tobias decided to use 4
119 // Let a worker thread do the iPod parsing
120 m_future = QtConcurrent::run(this, &IPodFeature::importLibrary);
121 m_future_watcher.setFuture(m_future);
122 m_title = tr("(loading) iPod"); // (loading) at start in respect to small displays
123 //calls a slot in the sidebar model such that '(loading)iPod' is displayed.
124 emit (featureIsLoading(this));
125 }
126 else{
127 m_pIPodPlaylistModel->setPlaylist(itdb_playlist_mpl(m_itdb)); // Gets the master playlist
128 }
129 emit(showTrackModel(m_pIPodPlaylistModel));
130}
131
132QString IPodFeature::detectMountPoint( QString iPodMountPoint) {
133 QFileInfoList mountpoints;
134 #ifdef __WINDOWS__
135 // Windows iPod Detection
136 mountpoints = QDir::drives();
137 #elif __LINUX__
138 // Linux
139 mountpoints = QDir("/media").entryInfoList();
140 mountpoints += QDir("/mnt").entryInfoList();
141 #elif __APPLE__
142 // Mac OSX
143 mountpoints = QDir("/Volumes").entryInfoList();
144 #endif
145
146 QListIterator<QFileInfo> i(mountpoints);
147 QFileInfo mp;
148 while (i.hasNext()) {
149 mp = (QFileInfo) i.next();
150 qDebug() << "mp:" << mp.filePath();
151 if( mp.filePath() != iPodMountPoint ) {
152 if (QDir( QString(mp.filePath() + "/iPod_Control") ).exists() ) {
153 qDebug() << "iPod found at" << mp.filePath();
154
155 // Multiple iPods
156 if (!iPodMountPoint.isEmpty()) {
157 int ret = QMessageBox::warning(NULL, tr("Multiple iPods Detected"),
158 tr("Mixxx has detected another iPod. \n\n")+
159 tr("Choose Yes to use the newly found iPod @ ")+ mp.filePath()+
160 tr(" or to continue to search for other iPods. \n")+
161 tr("Choose No to use the existing iPod @ ")+ iPodMountPoint+
162 tr( " and end detection. \n"),
163 QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
164 if (ret == QMessageBox::No) {
165 break;
166 }
167 }
168 iPodMountPoint = mp.filePath();
169 }
170 }
171 }
172 return iPodMountPoint;
173}
174
175void IPodFeature::activateChild(const QModelIndex& index) {
176 TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
177 qDebug() << "IPodFeature::activateChild " << item->data() << " " << item->dataPath();
178 QString playlist = item->dataPath().toString();
179 Itdb_Playlist* pPlaylist = (Itdb_Playlist*)playlist.toUInt();
180
181 if (pPlaylist) {
182 qDebug() << "Activating " << QString::fromUtf8(pPlaylist->name);
183 }
184 m_pIPodPlaylistModel->setPlaylist(pPlaylist);
185 emit(showTrackModel(m_pIPodPlaylistModel));
186}
187
188TreeItemModel* IPodFeature::getChildModel() {
189 return &m_childModel;
190}
191
192void IPodFeature::onRightClick(const QPoint& globalPos) {
193 Q_UNUSED(globalPos);
194}
195
196void IPodFeature::onRightClickChild(const QPoint& globalPos, QModelIndex index) {
197 //Save the model index so we can get it in the action slots...
198 m_lastRightClickedIndex = index;
199
200 //Create the right-click menu
201 QMenu menu(NULL);
202 menu.addAction(m_pAddToAutoDJAction);
203 menu.addAction(m_pAddToAutoDJTopAction);
204 menu.addSeparator();
205 menu.addAction(m_pImportAsMixxxPlaylistAction);
206 menu.exec(globalPos);
207}
208
209bool IPodFeature::dropAccept(QList<QUrl> urls) {
210 Q_UNUSED(urls);
211 return false;
212}
213
214bool IPodFeature::dropAcceptChild(const QModelIndex& index, QList<QUrl> urls) {
215 Q_UNUSED(index);
216 Q_UNUSED(urls);
217 return false;
218}
219
220bool IPodFeature::dragMoveAccept(QUrl url) {
221 Q_UNUSED(url);
222 return false;
223}
224
225bool IPodFeature::dragMoveAcceptChild(const QModelIndex& index, QUrl url) {
226 Q_UNUSED(index);
227 Q_UNUSED(url);
228 return false;
229}
230
231/*
232 * This method is executed in a separate thread
233 * via QtConcurrent::run
234 */
235TreeItem* IPodFeature::importLibrary() {
236 // Give thread a low priority
237 QThread* thisThread = QThread::currentThread();
238 thisThread->setPriority(QThread::LowestPriority);
239
240 qDebug() << "IPodFeature::importLibrary() ";
241
242 TreeItem* playlist_root = new TreeItem();
243
244 GError* err = 0;
245 qDebug() << "Calling the libgpod db parser for:" << m_dbfile;
246 if (m_itdb) {
247 itdb_free(m_itdb);
248 m_itdb = NULL;
249 }
250 m_itdb = itdb_parse(QDir::toNativeSeparators(m_dbfile).toLocal8Bit(), &err);
251
252 if (err) {
253 qDebug() << "There was an error, attempting to free db: "
254 << err->message;
255 QMessageBox::warning(NULL, tr("Error Loading iPod database"),
256 err->message);
257 g_error_free(err);
258 if (m_itdb) {
259 itdb_free(m_itdb);
260 m_itdb = 0;
261 }
262 } else if (m_itdb) {
263 GList* playlist_node;
264
265 for (playlist_node = g_list_first(m_itdb->playlists);
266 playlist_node != NULL;
267 playlist_node = g_list_next(playlist_node))
268 {
269 Itdb_Playlist* pPlaylist;
270 pPlaylist = (Itdb_Playlist*)playlist_node->data;
271 if (!itdb_playlist_is_mpl(pPlaylist)) {
272 QString playlistname = QString::fromUtf8(pPlaylist->name);
273 qDebug() << playlistname;
274 // append the playlist to the child model
275 TreeItem *item = new TreeItem(playlistname, QString::number((uint)pPlaylist), this, playlist_root);
276 playlist_root->appendChild(item);
277 }
278 }
279 }
280 return playlist_root;
281}
282
283void IPodFeature::onTrackCollectionLoaded(){
284 TreeItem* root = m_future.result();
285 if(root){
286 m_childModel.setRootItem(root);
287 if (m_isActivated) {
288 activate();
289 }
290 qDebug() << "iPod library loaded: success";
291 SettingsDAO settings(m_pTrackCollection->getDatabase());
292 settings.setValue(IPOD_MOUNT_KEY, m_dbfile);
293 }
294 else{
295 QMessageBox::warning(
296 NULL,
297 tr("Error Loading iPod database"),
298 tr("There was an error loading your iPod database. Some of "
299 "your iPod tracks or playlists may not have loaded."));
300 }
301 //calls a slot in the sidebarmodel such that 'isLoading' is removed from the feature title.
302 m_title = tr("iPod");
303 emit(featureLoadingFinished(this));
304 activate();
305}
306
307void IPodFeature::onLazyChildExpandation(const QModelIndex &index){
308 Q_UNUSED(index);
309 //Nothing to do because the childmodel is not of lazy nature.
310}
311
312void IPodFeature::slotAddToAutoDJ() {
313 //qDebug() << "slotAddToAutoDJ() row:" << m_lastRightClickedIndex.data();
314 addToAutoDJ(false); // Top = True
315}
316
317
318void IPodFeature::slotAddToAutoDJTop() {
319 //qDebug() << "slotAddToAutoDJTop() row:" << m_lastRightClickedIndex.data();
320 addToAutoDJ(true); // bTop = True
321}
322
323void IPodFeature::addToAutoDJ(bool bTop) {
324 // qDebug() << "slotAddToAutoDJ() row:" << m_lastRightClickedIndex.data();
325
326 if (m_lastRightClickedIndex.isValid()) {
327 TreeItem *item = static_cast<TreeItem*>(m_lastRightClickedIndex.internalPointer());
328 qDebug() << "IPodFeature::addToAutoDJ " << item->data() << " " << item->dataPath();
329 QString playlist = item->dataPath().toString();
330 Itdb_Playlist* pPlaylist = (Itdb_Playlist*)playlist.toUInt();
331 if (pPlaylist) {
332 IPodPlaylistModel* pPlaylistModelToAdd = new IPodPlaylistModel(this, m_pTrackCollection);
333 pPlaylistModelToAdd->setPlaylist(pPlaylist);
334 PlaylistDAO &playlistDao = m_pTrackCollection->getPlaylistDAO();
335 int autoDJId = playlistDao.getPlaylistIdFromName(AUTODJ_TABLE);
336
337 int rows = pPlaylistModelToAdd->rowCount();
338 for(int i = 0; i < rows; ++i){
339 QModelIndex index = pPlaylistModelToAdd->index(i,0);
340 if (index.isValid()) {
341 TrackPointer track = pPlaylistModelToAdd->getTrack(index);
342 if (bTop) {
343 // Start at position 2 because position 1 was already loaded to the deck
344 playlistDao.insertTrackIntoPlaylist(track->getId(), autoDJId, i+2);
345 } else {
346 playlistDao.appendTrackToPlaylist(track->getId(), autoDJId);
347 }
348 }
349 }
350 delete pPlaylistModelToAdd;
351 }
352 }
353}
354
355void IPodFeature::slotImportAsMixxxPlaylist() {
356 // qDebug() << "slotAddToAutoDJ() row:" << m_lastRightClickedIndex.data();
357
358 if (m_lastRightClickedIndex.isValid()) {
359 TreeItem *item = static_cast<TreeItem*>(m_lastRightClickedIndex.internalPointer());
360 qDebug() << "IPodFeature::slotImportAsMixxxPlaylist " << item->data() << " " << item->dataPath();
361 QString playlist = item->dataPath().toString();
362 Itdb_Playlist* pPlaylist = (Itdb_Playlist*)playlist.toUInt();
363 playlist = QString::fromUtf8(pPlaylist->name);
364 if (pPlaylist) {
365 IPodPlaylistModel* pPlaylistModelToAdd = new IPodPlaylistModel(this, m_pTrackCollection);
366 pPlaylistModelToAdd->setPlaylist(pPlaylist);
367 PlaylistDAO &playlistDao = m_pTrackCollection->getPlaylistDAO();
368
369 int playlistId = playlistDao.getPlaylistIdFromName(playlist);
370 int i = 1;
371
372 if (playlistId != -1) {
373 // Calculate a unique name
374 playlist += "(%1)";
375 while (playlistId != -1) {
376 i++;
377 playlistId = playlistDao.getPlaylistIdFromName(playlist.arg(i));
378 }
379 playlist = playlist.arg(i);
380 }
381 playlistId = playlistDao.createPlaylist(playlist);
382
383 if (playlistId != -1) {
384 // Copy Tracks
385 int rows = pPlaylistModelToAdd->rowCount();
386 for (int i = 0; i < rows; ++i) {
387 QModelIndex index = pPlaylistModelToAdd->index(i,0);
388 if (index.isValid()) {
389 //qDebug() << pPlaylistModelToAdd->getTrackLocation(index);
390 TrackPointer track = pPlaylistModelToAdd->getTrack(index);
391 playlistDao.appendTrackToPlaylist(track->getId(), playlistId);
392 }
393 }
394 }
395 else {
396 QMessageBox::warning(NULL,
397 tr("Playlist Creation Failed"),
398 tr("An unknown error occurred while creating playlist: ")
399 + playlist);
400 }
401
402 delete pPlaylistModelToAdd;
403 }
404 }
405}
0406
=== added file 'mixxx/src/library/ipod/ipodfeature.h'
--- mixxx/src/library/ipod/ipodfeature.h 1970-01-01 00:00:00 +0000
+++ mixxx/src/library/ipod/ipodfeature.h 2012-11-18 17:09:22 +0000
@@ -0,0 +1,90 @@
1
2#ifndef IPODFEATURE_H
3#define IPODFEATURE_H
4
5#include <QStringListModel>
6#include <QtSql>
7#include <QFuture>
8#include <QtConcurrentRun>
9#include <QFutureWatcher>
10
11#include "library/libraryfeature.h"
12#include "library/trackcollection.h"
13#include "library/treeitemmodel.h"
14#include "library/treeitem.h"
15
16extern "C"
17{
18#include <gpod/itdb.h>
19}
20
21class IPodPlaylistModel;
22class GPodItdb;
23
24class IPodFeature : public LibraryFeature {
25 Q_OBJECT
26 public:
27 IPodFeature(QObject* parent, TrackCollection* pTrackCollection);
28 virtual ~IPodFeature();
29 static bool isSupported();
30
31 QVariant title();
32 QIcon getIcon();
33
34 bool dropAccept(QList<QUrl> urls);
35 bool dropAcceptChild(const QModelIndex& index, QList<QUrl> urls);
36 bool dragMoveAccept(QUrl url);
37 bool dragMoveAcceptChild(const QModelIndex& index, QUrl url);
38
39 TreeItemModel* getChildModel();
40
41 public slots:
42 void activate();
43 void activate(bool forceReload);
44 void activateChild(const QModelIndex& index);
45 void onRightClick(const QPoint& globalPos);
46 void onRightClickChild(const QPoint& globalPos, QModelIndex index);
47 void onLazyChildExpandation(const QModelIndex& index);
48 void onTrackCollectionLoaded();
49 void slotAddToAutoDJ();
50 void slotAddToAutoDJTop();
51 void slotImportAsMixxxPlaylist();
52
53 private:
54 static QString getiTunesMusicPath();
55 //returns the invisible rootItem for the sidebar model
56 TreeItem* importLibrary();
57 void addToAutoDJ(bool bTop);
58 QString detectMountPoint(QString iPodMountPoint);
59
60 QAction* m_pAddToAutoDJAction;
61 QAction* m_pAddToAutoDJTopAction;
62 QAction* m_pImportAsMixxxPlaylistAction;
63
64 QModelIndex m_lastRightClickedIndex;
65
66 IPodPlaylistModel* m_pIPodPlaylistModel;
67 TreeItemModel m_childModel;
68 QStringList m_playlists;
69 TrackCollection* m_pTrackCollection;
70 //a new DB connection for the worker thread
71 QSqlDatabase m_database;
72 bool m_isActivated;
73 QString m_dbfile;
74
75 QFutureWatcher<TreeItem*> m_future_watcher;
76 QFuture<TreeItem*> m_future;
77 QString m_title;
78 bool m_cancelImport;
79
80 QString m_dbItunesRoot;
81 QString m_mixxxItunesRoot;
82
83 GPodItdb* m_gPodItdb;
84
85 Itdb_iTunesDB* m_itdb;
86
87 static const QString IPOD_MOUNT_KEY;
88};
89
90#endif /* IPODFEATURE_H */
091
=== added file 'mixxx/src/library/ipod/ipodplaylistmodel.cpp'
--- mixxx/src/library/ipod/ipodplaylistmodel.cpp 1970-01-01 00:00:00 +0000
+++ mixxx/src/library/ipod/ipodplaylistmodel.cpp 2012-11-18 17:09:22 +0000
@@ -0,0 +1,901 @@
1#include <QtAlgorithms>
2#include <QtDebug>
3#include <QTime>
4
5#include "library/ipod/ipodplaylistmodel.h"
6#include "mixxxutils.cpp"
7#include "library/starrating.h"
8#include "track/beatfactory.h"
9#include "track/beats.h"
10
11extern "C" {
12#include <glib-object.h> // g_type_init
13}
14
15const bool sDebug = false;
16
17IPodPlaylistModel::IPodPlaylistModel(QObject* pParent, TrackCollection* pTrackCollection)
18 : TrackModel(pTrackCollection->getDatabase(),
19 "mixxx.db.model.ipod_playlist"),
20 QAbstractTableModel(pParent),
21 m_iSortColumn(0),
22 m_eSortOrder(Qt::AscendingOrder),
23 m_currentSearch(""),
24 m_pTrackCollection(pTrackCollection),
25 m_trackDAO(m_pTrackCollection->getTrackDAO()),
26 m_pPlaylist(NULL)
27{
28 initHeaderData();
29}
30
31IPodPlaylistModel::~IPodPlaylistModel() {
32
33}
34
35void IPodPlaylistModel::initHeaderData() {
36 // Set the column heading labels, rename them for translations and have
37 // proper capitalization
38
39 m_headerList.append(qMakePair(QString(tr("#")), (size_t)0xffff));
40 m_headerList.append(qMakePair(QString(tr("Artist")), offsetof(Itdb_Track, artist)));
41 m_headerList.append(qMakePair(QString(tr("Title")), offsetof(Itdb_Track, title)));
42 m_headerList.append(qMakePair(QString(tr("Album")), offsetof(Itdb_Track, album)));
43 m_headerList.append(qMakePair(QString(tr("Year")), offsetof(Itdb_Track, year)));
44 m_headerList.append(qMakePair(QString(tr("Duration")), offsetof(Itdb_Track, tracklen)));
45 m_headerList.append(qMakePair(QString(tr("Rating")), offsetof(Itdb_Track, rating)));
46 m_headerList.append(qMakePair(QString(tr("Genre")), offsetof(Itdb_Track, genre)));
47 m_headerList.append(qMakePair(QString(tr("Type")), offsetof(Itdb_Track, filetype)));
48 m_headerList.append(qMakePair(QString(tr("Track #")), offsetof(Itdb_Track, track_nr)));
49 m_headerList.append(qMakePair(QString(tr("Date Added")), offsetof(Itdb_Track, time_added)));
50 m_headerList.append(qMakePair(QString(tr("BPM")), offsetof(Itdb_Track, BPM)));
51 m_headerList.append(qMakePair(QString(tr("Bitrate")), offsetof(Itdb_Track, bitrate)));
52 m_headerList.append(qMakePair(QString(tr("Location")), offsetof(Itdb_Track, ipod_path)));
53 m_headerList.append(qMakePair(QString(tr("Comment")), offsetof(Itdb_Track, comment)));
54
55}
56
57void IPodPlaylistModel::initDefaultSearchColumns() {
58 QStringList searchColumns;
59 searchColumns << "artist"
60 << "album"
61 << "location"
62 << "comment"
63 << "title";
64 setSearchColumns(searchColumns);
65}
66
67void IPodPlaylistModel::setSearchColumns(const QStringList& searchColumns) {
68 m_searchColumns = searchColumns;
69
70 // Convert all the search column names to their field indexes because we use
71 // them a bunch.
72 m_searchColumnIndices.resize(m_searchColumns.size());
73 for (int i = 0; i < m_searchColumns.size(); ++i) {
74 m_searchColumnIndices[i] = fieldIndex(m_searchColumns[i]);
75 }
76}
77
78QVariant IPodPlaylistModel::headerData(int section, Qt::Orientation orientation, int role) const {
79 if (role != Qt::DisplayRole)
80 return QAbstractTableModel::headerData(section, orientation, role);
81
82 if ( orientation == Qt::Horizontal
83 && role == Qt::DisplayRole
84 && section < m_headerList.size()
85 ) {
86 return QVariant(m_headerList.at(section).first);
87 }
88 return QAbstractTableModel::headerData(section, orientation, role);
89}
90
91
92int IPodPlaylistModel::findSortInsertionPoint(int trackId, TrackPointer pTrack,
93 const QVector<QPair<int, QHash<int, QVariant> > >& rowInfo) {
94 QVariant trackValue = getTrackValueForColumn(trackId, m_iSortColumn, pTrack);
95
96 int min = 0;
97 int max = rowInfo.size()-1;
98
99 if (sDebug) {
100 qDebug() << this << "Trying to insertion sort:"
101 << trackValue << "min" << min << "max" << max;
102 }
103
104 while (min <= max) {
105 int mid = min + (max - min) / 2;
106 const QPair<int, QHash<int, QVariant> >& otherRowInfo = rowInfo[mid];
107 int otherTrackId = otherRowInfo.first;
108 // const QHash<int, QVariant>& otherRowCache = otherRowInfo.second; // not used
109
110
111 // This should not happen, but it's a recoverable error so we should only log it.
112 if (!m_recordCache.contains(otherTrackId)) {
113 qDebug() << "WARNING: track" << otherTrackId << "was not in index";
114// updateTrackInIndex(otherTrackId);
115 }
116
117 QVariant tableValue = getTrackValueForColumn(otherTrackId, m_iSortColumn);
118 int compare = compareColumnValues(m_iSortColumn, m_eSortOrder, trackValue, tableValue);
119
120 if (sDebug) {
121 qDebug() << this << "Comparing" << trackValue
122 << "to" << tableValue << ":" << compare;
123 }
124
125 if (compare == 0) {
126 // Alright, if we're here then we can insert it here and be
127 // "correct"
128 min = mid;
129 break;
130 } else if (compare > 0) {
131 min = mid + 1;
132 } else {
133 max = mid - 1;
134 }
135 }
136 return min;
137}
138
139
140const QString IPodPlaylistModel::currentSearch() const {
141 return m_currentSearch;
142}
143
144void IPodPlaylistModel::setSort(int column, Qt::SortOrder order) {
145 if (sDebug) {
146 qDebug() << this << "setSort()";
147 }
148
149 m_iSortColumn = column;
150 m_eSortOrder = order;
151}
152
153void IPodPlaylistModel::sort(int column, Qt::SortOrder order) {
154 if (sDebug) {
155 qDebug() << this << "sort()" << column << order;
156 }
157
158 m_iSortColumn = column;
159 m_eSortOrder = order;
160
161 emit layoutAboutToBeChanged();
162 qSort(m_sortedPlaylist.begin(), m_sortedPlaylist.end(), columnLessThan);
163 emit layoutChanged();
164}
165
166int IPodPlaylistModel::rowCount(const QModelIndex& parent) const {
167 if (m_pPlaylist && !parent.isValid()) {
168 return m_sortedPlaylist.size();
169 }
170 return 0;
171}
172
173int IPodPlaylistModel::columnCount(const QModelIndex& parent) const {
174 return parent.isValid() ? 0 : m_headerList.size();
175}
176
177int IPodPlaylistModel::fieldIndex(const QString& fieldName) const {
178 // Usually a small list, so O(n) is small
179 //return m_queryRecord.indexOf(fieldName);
180 //return m_columnNames.indexOf(fieldName);
181 QHash<QString, int>::const_iterator it = m_columnIndex.constFind(fieldName);
182 if (it != m_columnIndex.end()) {
183 return it.value();
184 }
185 return -1;
186}
187
188QVariant IPodPlaylistModel::data(const QModelIndex& index, int role) const {
189 //qDebug() << this << "data()";
190 QVariant value = QVariant();
191
192 if ( role != Qt::DisplayRole
193 && role != Qt::EditRole
194 && role != Qt::CheckStateRole
195 && role != Qt::ToolTipRole
196 ) {
197 return value;
198 }
199
200
201 Itdb_Track* pTrack = getPTrackFromModelIndex(index);
202 if (!pTrack) {
203 return value;
204 }
205
206 size_t structOffset = m_headerList.at(index.column()).second;
207
208
209 if (structOffset == 0xffff) { // "#"
210 value = m_sortedPlaylist.at(index.row()).pos;
211 } else if (structOffset == offsetof(Itdb_Track, year)) {
212 if (pTrack->year) {
213 value = QVariant(pTrack->year);
214 }
215 } else if (structOffset == offsetof(Itdb_Track, tracklen)) {
216 if (pTrack->tracklen) {
217 value = MixxxUtils::millisecondsToMinutes(pTrack->tracklen, true);
218 }
219 } else if (structOffset == offsetof(Itdb_Track, rating)) {
220 value = qVariantFromValue(StarRating(pTrack->rating));
221 } else if (structOffset == offsetof(Itdb_Track, track_nr)) {
222 if (pTrack->track_nr) {
223 value = QVariant(pTrack->track_nr);
224 }
225 } else if (structOffset == offsetof(Itdb_Track, BPM)) {
226 if (pTrack->BPM) {
227 value = QVariant(pTrack->BPM);
228 }
229 } else if (structOffset == offsetof(Itdb_Track, bitrate)) {
230 if (pTrack->bitrate) {
231 value = QVariant(pTrack->bitrate);
232 }
233 } else if (structOffset == offsetof(Itdb_Track, time_added)) {
234 if (pTrack->time_added) {
235 QDateTime timeAdded;
236 timeAdded.setTime_t(pTrack->time_added);
237 timeAdded.toString(Qt::ISODate);
238 value = QVariant(timeAdded.toString(Qt::ISODate));
239 }
240 } else {
241 // for the gchar* elements
242 //qDebug() << *(gchar**)((char*)(pTrack) + structOffset);
243 QString ret = QString::fromUtf8(*(gchar**)((char*)(pTrack) + structOffset));
244
245 value = QVariant(ret);
246 }
247
248
249/*
250 // This value is the value in its most raw form. It was looked up either
251 // from the SQL table or from the cached track layer.
252 QVariant value = getBaseValue(index, role);
253*/
254
255
256 // Format the value based on whether we are in a tooltip, display, or edit
257 // role
258 switch (role) {
259 case Qt::ToolTipRole:
260 case Qt::DisplayRole:
261 /*
262 if (column == fieldIndex(LIBRARYTABLE_DURATION)) {
263 if (qVariantCanConvert<int>(value))
264 value = MixxxUtils::secondsToMinutes(qVariantValue<int>(value));
265 } else if (column == fieldIndex(LIBRARYTABLE_RATING)) {
266 if (qVariantCanConvert<int>(value))
267 value = qVariantFromValue(StarRating(value.toInt()));
268 } else if (column == fieldIndex(LIBRARYTABLE_TIMESPLAYED)) {
269 if (qVariantCanConvert<int>(value))
270 value = QString("(%1)").arg(value.toInt());
271 } else if (column == fieldIndex(LIBRARYTABLE_PLAYED)) {
272 // Convert to a bool. Not really that useful since it gets converted
273 // right back to a QVariant
274 value = (value == "true") ? true : false;
275 }
276 */
277 break;
278 case Qt::EditRole:
279 /*
280 if (column == fieldIndex(LIBRARYTABLE_BPM)) {
281 return value.toDouble();
282 } else if (column == fieldIndex(LIBRARYTABLE_TIMESPLAYED)) {
283 return index.sibling(row, fieldIndex(LIBRARYTABLE_PLAYED)).data().toBool();
284 } else if (column == fieldIndex(LIBRARYTABLE_RATING)) {
285 if (qVariantCanConvert<int>(value))
286 value = qVariantFromValue(StarRating(value.toInt()));
287 }
288 */
289 break;
290 case Qt::CheckStateRole:
291 /*
292 if (column == fieldIndex(LIBRARYTABLE_TIMESPLAYED)) {
293 bool played = index.sibling(row, fieldIndex(LIBRARYTABLE_PLAYED)).data().toBool();
294 value = played ? Qt::Checked : Qt::Unchecked;
295 } else {
296 */
297 value = QVariant();
298 //}
299 break;
300 default:
301 break;
302 }
303 return value;
304}
305
306bool IPodPlaylistModel::setData(const QModelIndex& index, const QVariant& value, int role) {
307
308 if (!index.isValid())
309 return false;
310
311 int row = index.row();
312 int column = index.column();
313
314 if (sDebug) {
315 qDebug() << this << "setData() column:" << column << "value:" << value << "role:" << role;
316 }
317
318 // Over-ride sets to TIMESPLAYED and re-direct them to PLAYED
319 if (role == Qt::CheckStateRole) {
320 if (column == fieldIndex(LIBRARYTABLE_TIMESPLAYED)) {
321 QString val = value.toInt() > 0 ? QString("true") : QString("false");
322 QModelIndex playedIndex = index.sibling(index.row(), fieldIndex(LIBRARYTABLE_PLAYED));
323 return setData(playedIndex, val, Qt::EditRole);
324 }
325 return false;
326 }
327
328 if (row < 0 || row >= m_rowInfo.size()) {
329 return false;
330 }
331
332 const QPair<int, QHash<int, QVariant> >& rowInfo = m_rowInfo[row];
333 int trackId = rowInfo.first;
334
335 // You can't set something in the table columns because we have no way of
336 // persisting it.
337 const QHash<int, QVariant>& columns = rowInfo.second;
338 if (columns.contains(column)) {
339 return false;
340 }
341
342 TrackPointer pTrack = m_trackDAO.getTrack(trackId);
343 setTrackValueForColumn(pTrack, column, value);
344
345 // Do not save the track here. Changing the track dirties it and the caching
346 // system will automatically save the track once it is unloaded from
347 // memory. rryan 10/2010
348 //m_trackDAO.saveTrack(pTrack);
349
350 return true;
351}
352
353TrackModel::CapabilitiesFlags IPodPlaylistModel::getCapabilities() const {
354 return TRACKMODELCAPS_NONE
355 | TRACKMODELCAPS_ADDTOPLAYLIST
356 | TRACKMODELCAPS_ADDTOCRATE
357 | TRACKMODELCAPS_ADDTOAUTODJ
358 | TRACKMODELCAPS_LOADTODECK
359 | TRACKMODELCAPS_LOADTOSAMPLER;
360}
361
362Qt::ItemFlags IPodPlaylistModel::flags(const QModelIndex &index) const {
363 return readWriteFlags(index);
364}
365
366Qt::ItemFlags IPodPlaylistModel::readWriteFlags(const QModelIndex &index) const {
367 if (!index.isValid())
368 return Qt::ItemIsEnabled;
369
370 Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index);
371
372 //Enable dragging songs from this data model to elsewhere (like the waveform
373 //widget to load a track into a Player).
374 defaultFlags |= Qt::ItemIsDragEnabled;
375
376 //int row = index.row(); // not used
377 int column = index.column();
378
379 if ( column == fieldIndex(LIBRARYTABLE_FILETYPE)
380 || column == fieldIndex(LIBRARYTABLE_LOCATION)
381 || column == fieldIndex(LIBRARYTABLE_DURATION)
382 || column == fieldIndex(LIBRARYTABLE_BITRATE)
383 || column == fieldIndex(LIBRARYTABLE_DATETIMEADDED)) {
384 return defaultFlags;
385 } else if (column == fieldIndex(LIBRARYTABLE_TIMESPLAYED)) {
386 return defaultFlags | Qt::ItemIsUserCheckable;
387 } else {
388 return defaultFlags | Qt::ItemIsEditable;
389 }
390}
391
392Qt::ItemFlags IPodPlaylistModel::readOnlyFlags(const QModelIndex &index) const
393{
394 Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index);
395 if (!index.isValid())
396 return Qt::ItemIsEnabled;
397
398 //Enable dragging songs from this data model to elsewhere (like the waveform widget to
399 //load a track into a Player).
400 defaultFlags |= Qt::ItemIsDragEnabled;
401
402 return defaultFlags;
403}
404
405void IPodPlaylistModel::trackChanged(int trackId) {
406 if (sDebug) {
407 qDebug() << this << "trackChanged" << trackId;
408 }
409 // TODO(daschuer): eg. use that for played column
410 //m_trackOverrides.insert(trackId);
411 //QLinkedList<int> rows = getTrackRows(trackId);
412/*
413 foreach (int row, rows) {
414 //qDebug() << "Row in this result set was updated. Signalling update. track:" << trackId << "row:" << row;
415 QModelIndex left = index(row, 0);
416 QModelIndex right = index(row, columnCount());
417 emit(dataChanged(left, right));
418 }
419*/
420}
421
422//static
423bool IPodPlaylistModel::columnLessThan(const playlist_member &s1, const playlist_member &s2) {
424 bool ret;
425
426 size_t structOffset = s1.pClass->m_headerList.at(s1.pClass->m_iSortColumn).second;
427
428 if (structOffset == 0xffff) { // "#"
429 ret = s1.pos < s2.pos;
430 } else if (structOffset == offsetof(Itdb_Track, year)) {
431 ret = s1.pTrack->year < s2.pTrack->year;
432 } else if (structOffset == offsetof(Itdb_Track, tracklen)) {
433 ret = s1.pTrack->tracklen < s2.pTrack->tracklen;
434 } else if (structOffset == offsetof(Itdb_Track, rating)) {
435 ret = s1.pTrack->rating < s2.pTrack->rating;
436 } else if (structOffset == offsetof(Itdb_Track, track_nr)) {
437 ret = s1.pTrack->track_nr < s2.pTrack->track_nr;
438 } else if (structOffset == offsetof(Itdb_Track, BPM)) {
439 ret = s1.pTrack->BPM < s2.pTrack->BPM;
440 } else if (structOffset == offsetof(Itdb_Track, bitrate)) {
441 ret = s1.pTrack->bitrate < s2.pTrack->bitrate;
442 } else if (structOffset == offsetof(Itdb_Track, time_added)) {
443 ret = s1.pTrack->time_added < s2.pTrack->time_added;
444 } else {
445 // for the gchar* elements
446 QString string1 = QString::fromUtf8(*(gchar**)((char*)(s1.pTrack) + structOffset));
447 QString string2 = QString::fromUtf8(*(gchar**)((char*)(s2.pTrack) + structOffset));
448 ret = string1.toLower() < string2.toLower();
449 }
450
451 if (s1.pClass->m_eSortOrder != Qt::AscendingOrder) {
452 return !ret;
453 }
454 return ret;
455}
456
457int IPodPlaylistModel::compareColumnValues(int iColumnNumber, Qt::SortOrder eSortOrder,
458 QVariant val1, QVariant val2) {
459 int result = 0;
460
461 if (iColumnNumber == fieldIndex(PLAYLISTTRACKSTABLE_POSITION) ||
462 iColumnNumber == fieldIndex(LIBRARYTABLE_BITRATE) ||
463 iColumnNumber == fieldIndex(LIBRARYTABLE_BPM) ||
464 iColumnNumber == fieldIndex(LIBRARYTABLE_DURATION) ||
465 iColumnNumber == fieldIndex(LIBRARYTABLE_TIMESPLAYED) ||
466 iColumnNumber == fieldIndex(LIBRARYTABLE_RATING)) {
467 // Sort as floats.
468 double delta = val1.toDouble() - val2.toDouble();
469
470 if (fabs(delta) < .00001)
471 result = 0;
472 else if (delta > 0.0)
473 result = 1;
474 else
475 result = -1;
476 } else {
477 // Default to case-insensitive string comparison
478 result = val1.toString().compare(val2.toString(), Qt::CaseInsensitive);
479 }
480
481 // If we're in descending order, flip the comparison.
482 if (eSortOrder == Qt::DescendingOrder) {
483 result = -result;
484 }
485
486 return result;
487}
488
489
490QVariant IPodPlaylistModel::getTrackValueForColumn(int trackId, int column, TrackPointer pTrack) const {
491 QVariant result;
492
493 // The caller can optionally provide a pTrack if they already looked it
494 // up. This is just an optimization to help reduce the # of calls to
495 // lookupCachedTrack. If they didn't provide it, look it up.
496 if (pTrack) {
497 result = getTrackValueForColumn(pTrack, column);
498 }
499
500 // If the track lookup failed (could happen for track properties we dont
501 // keep track of in Track, like playlist position) look up the value in
502 // their SQL record.
503
504 // TODO(rryan) this code is flawed for columns that contains row-specific
505 // metadata. Currently the upper-levels will not delegate row-specific
506 // columns to this method, but there should still be a check here I think.
507 if (!result.isValid()) {
508 QHash<int, QVector<QVariant> >::const_iterator it =
509 m_recordCache.find(trackId);
510 if (it != m_recordCache.end()) {
511 const QVector<QVariant>& fields = it.value();
512 result = fields.value(column, result);
513 }
514 }
515 return result;
516}
517
518QVariant IPodPlaylistModel::getTrackValueForColumn(TrackPointer pTrack, int column) const {
519 if (!pTrack)
520 return QVariant();
521
522 // TODO(XXX) Qt properties could really help here.
523 if (fieldIndex(LIBRARYTABLE_ARTIST) == column) {
524 return QVariant(pTrack->getArtist());
525 } else if (fieldIndex(LIBRARYTABLE_TITLE) == column) {
526 return QVariant(pTrack->getTitle());
527 } else if (fieldIndex(LIBRARYTABLE_ALBUM) == column) {
528 return QVariant(pTrack->getAlbum());
529 } else if (fieldIndex(LIBRARYTABLE_YEAR) == column) {
530 return QVariant(pTrack->getYear());
531 } else if (fieldIndex(LIBRARYTABLE_GENRE) == column) {
532 return QVariant(pTrack->getGenre());
533 } else if (fieldIndex(LIBRARYTABLE_FILETYPE) == column) {
534 return QVariant(pTrack->getType());
535 } else if (fieldIndex(LIBRARYTABLE_TRACKNUMBER) == column) {
536 return QVariant(pTrack->getTrackNumber());
537 } else if (fieldIndex(LIBRARYTABLE_LOCATION) == column) {
538 return QVariant(pTrack->getLocation());
539 } else if (fieldIndex(LIBRARYTABLE_COMMENT) == column) {
540 return QVariant(pTrack->getComment());
541 } else if (fieldIndex(LIBRARYTABLE_DURATION) == column) {
542 return pTrack->getDuration();
543 } else if (fieldIndex(LIBRARYTABLE_BITRATE) == column) {
544 return QVariant(pTrack->getBitrate());
545 } else if (fieldIndex(LIBRARYTABLE_BPM) == column) {
546 return QVariant(pTrack->getBpm());
547 } else if (fieldIndex(LIBRARYTABLE_PLAYED) == column) {
548 return QVariant(pTrack->getPlayed());
549 } else if (fieldIndex(LIBRARYTABLE_TIMESPLAYED) == column) {
550 return QVariant(pTrack->getTimesPlayed());
551 } else if (fieldIndex(LIBRARYTABLE_RATING) == column) {
552 return pTrack->getRating();
553 } else if (fieldIndex(LIBRARYTABLE_KEY) == column) {
554 return pTrack->getKey();
555 }
556 return QVariant();
557}
558
559void IPodPlaylistModel::setTrackValueForColumn(TrackPointer pTrack, int column, QVariant value) {
560 // TODO(XXX) Qt properties could really help here.
561 if (fieldIndex(LIBRARYTABLE_ARTIST) == column) {
562 pTrack->setArtist(value.toString());
563 } else if (fieldIndex(LIBRARYTABLE_TITLE) == column) {
564 pTrack->setTitle(value.toString());
565 } else if (fieldIndex(LIBRARYTABLE_ALBUM) == column) {
566 pTrack->setAlbum(value.toString());
567 } else if (fieldIndex(LIBRARYTABLE_YEAR) == column) {
568 pTrack->setYear(value.toString());
569 } else if (fieldIndex(LIBRARYTABLE_GENRE) == column) {
570 pTrack->setGenre(value.toString());
571 } else if (fieldIndex(LIBRARYTABLE_FILETYPE) == column) {
572 pTrack->setType(value.toString());
573 } else if (fieldIndex(LIBRARYTABLE_TRACKNUMBER) == column) {
574 pTrack->setTrackNumber(value.toString());
575 } else if (fieldIndex(LIBRARYTABLE_LOCATION) == column) {
576 pTrack->setLocation(value.toString());
577 } else if (fieldIndex(LIBRARYTABLE_COMMENT) == column) {
578 pTrack->setComment(value.toString());
579 } else if (fieldIndex(LIBRARYTABLE_DURATION) == column) {
580 pTrack->setDuration(value.toInt());
581 } else if (fieldIndex(LIBRARYTABLE_BITRATE) == column) {
582 pTrack->setBitrate(value.toInt());
583 } else if (fieldIndex(LIBRARYTABLE_BPM) == column) {
584 //QVariant::toFloat needs >= QT 4.6.x
585 pTrack->setBpm((float) value.toDouble());
586 } else if (fieldIndex(LIBRARYTABLE_PLAYED) == column) {
587 pTrack->setPlayed(value.toBool());
588 } else if (fieldIndex(LIBRARYTABLE_TIMESPLAYED) == column) {
589 pTrack->setTimesPlayed(value.toInt());
590 } else if (fieldIndex(LIBRARYTABLE_RATING) == column) {
591 StarRating starRating = qVariantValue<StarRating>(value);
592 pTrack->setRating(starRating.starCount());
593 } else if (fieldIndex(LIBRARYTABLE_KEY) == column) {
594 pTrack->setKey(value.toString());
595 }
596}
597
598QVariant IPodPlaylistModel::getBaseValue(const QModelIndex& index, int role) const {
599 if (role != Qt::DisplayRole &&
600 role != Qt::ToolTipRole &&
601 role != Qt::EditRole) {
602 return QVariant();
603 }
604
605 int row = index.row();
606 int column = index.column();
607
608 if (row < 0 || row >= m_rowInfo.size()) {
609 return QVariant();
610 }
611
612 const QPair<int, QHash<int, QVariant> >& rowInfo = m_rowInfo[row];
613 int trackId = rowInfo.first;
614
615 // If the row info has the row-specific column, return that.
616 const QHash<int, QVariant>& columns = rowInfo.second;
617 if (columns.contains(column)) {
618 if (sDebug) {
619 qDebug() << "Returning table-column value" << columns[column] << "for column" << column;
620 }
621 return columns[column];
622 }
623
624 // Otherwise, return the information from the track record cache for the
625 // given track ID
626 return getTrackValueForColumn(trackId, column);
627}
628
629void IPodPlaylistModel::setPlaylist(Itdb_Playlist* pPlaylist) {
630 if (m_pPlaylist) {
631 beginRemoveRows(QModelIndex(), 0, m_sortedPlaylist.size()-1);
632 m_pPlaylist = NULL;
633 m_sortedPlaylist.clear();
634 endRemoveRows();
635 }
636
637 if (pPlaylist) {
638 beginInsertRows(QModelIndex(), 0, pPlaylist->num-1);
639 m_pPlaylist = pPlaylist;
640 // walk thought linked list and collect playlist position
641
642 GList* track_node;
643 uint pl_position = 0;
644
645 QByteArray search = m_currentSearch.toUtf8();
646
647 for (track_node = g_list_first(pPlaylist->members);
648 track_node != NULL;
649 track_node = g_list_next(track_node))
650 {
651 struct playlist_member plMember;
652 plMember.pClass = this;
653 plMember.pTrack = (Itdb_Track*)track_node->data;
654 plMember.pos = ++pl_position;
655
656 // Filter
657 bool add = false;
658 if( itdb_playlist_is_mpl(pPlaylist) && search.size()){
659 // Apply filter only for the master playlist "IPOD"
660 gchar* haystack = g_strconcat(
661 plMember.pTrack->artist,
662 plMember.pTrack->title,
663 plMember.pTrack->album,
664 plMember.pTrack->comment,
665 plMember.pTrack->genre,
666 NULL);
667
668 if( findInUtf8Case(haystack, search.data())) {
669 add = true;
670 }
671 g_free(haystack);
672 } else {
673 add = true;
674 }
675
676 if (add) {
677 m_sortedPlaylist.append(plMember);
678 }
679
680 }
681 qSort(m_sortedPlaylist.begin(), m_sortedPlaylist.end(), columnLessThan);
682
683 endInsertRows();
684 }
685}
686
687//static
688bool IPodPlaylistModel::findInUtf8Case(gchar* heystack, gchar* needles) {
689 bool ret = true;
690 if (heystack == NULL) {
691 return false;
692 }
693 if (needles == NULL) {
694 return true;
695 }
696
697 gchar* heystack_casefold = g_utf8_casefold(heystack, -1);
698 gchar* needles_casefold = g_utf8_casefold(needles, -1);
699 gchar** needle = g_strsplit_set(needles_casefold, " ", 0);
700
701 qDebug() << "find" << heystack_casefold;
702
703 // all needles must be found, implicit AND
704 for (int i = 0; needle[i]; i++) {
705 if (needle[i][0] != 0) {
706 if (!g_strrstr(heystack_casefold, needle[i])) {
707 ret = false;
708 break;
709 }
710 }
711 }
712 g_free(heystack_casefold);
713 g_free(needles_casefold);
714 g_strfreev(needle);
715 return ret;
716}
717
718TrackPointer IPodPlaylistModel::getTrack(const QModelIndex& index) const {
719
720 Itdb_Track* pTrack = getPTrackFromModelIndex(index);
721 if (!pTrack) {
722 return TrackPointer();
723 }
724
725 QString location = itdb_get_mountpoint(m_pPlaylist->itdb);
726 QString ipod_path = pTrack->ipod_path;
727 ipod_path.replace(QString(":"), QString("/"));
728 location += ipod_path;
729
730 qDebug() << location;
731
732 TrackDAO& track_dao = m_pTrackCollection->getTrackDAO();
733 int track_id = track_dao.getTrackId(location);
734 if (track_id < 0) {
735 // Add Track to library
736 track_id = track_dao.addTrack(location, true);
737 }
738
739 TrackPointer pTrackP;
740
741 if (track_id < 0) {
742 // Add Track to library failed
743 // Create own TrackInfoObject
744 pTrackP = TrackPointer(new TrackInfoObject(location), &QObject::deleteLater);
745 }
746 else {
747 pTrackP = track_dao.getTrack(track_id);
748 }
749
750 // Overwrite metadata from Ipod
751 // Note: This will be written to the mixxx library as well
752 // This is OK here because the location ist still pointing to the iPod device
753 pTrackP->setArtist(QString::fromUtf8(pTrack->artist));
754 pTrackP->setTitle(QString::fromUtf8(pTrack->title));
755 pTrackP->setAlbum(QString::fromUtf8(pTrack->album));
756 pTrackP->setYear(QString::number(pTrack->year));
757 pTrackP->setGenre(QString::fromUtf8(pTrack->genre));
758 double bpm = (double)pTrack->BPM;
759 pTrackP->setBpm(bpm);
760 pTrackP->setComment(QString::fromUtf8(pTrack->comment));
761
762 // If the track has a BPM, then give it a static beatgrid.
763 if (bpm) {
764 BeatsPointer pBeats = BeatFactory::makeBeatGrid(pTrackP.data(), bpm, 0.0f);
765 pTrackP->setBeats(pBeats);
766 }
767
768 return pTrackP;
769}
770// Gets the on-disk location of the track at the given location.
771QString IPodPlaylistModel::getTrackLocation(const QModelIndex& index) const {
772
773 Itdb_Track* pTrack = getPTrackFromModelIndex(index);
774 if (!pTrack) {
775 return QString();
776 }
777
778 QString location = itdb_get_mountpoint(m_pPlaylist->itdb);
779 QString ipod_path = pTrack->ipod_path;
780 ipod_path.replace(QString(":"), QString("/"));
781 location += ipod_path;
782
783 return location;
784}
785
786// Gets a significant hint of the track at the given QModelIndex
787// This is used to restore the selection after WTrackTableView::doSortByColumn
788int IPodPlaylistModel::getTrackId(const QModelIndex& index) const {
789 // in our case the position in the playlist is as significant hint
790 int row = index.row();
791 if (row < m_sortedPlaylist.size()) {
792 return m_sortedPlaylist.at(row).pos;
793 }
794 else {
795 return 0;
796 }
797}
798
799const QLinkedList<int> IPodPlaylistModel::getTrackRows(int trackId) const {
800 // In this case we get the position as trackId, returned from getTrackId above.
801 QLinkedList<int> ret;
802 for (int i = 0; i < m_sortedPlaylist.size(); ++i) {
803 if (m_sortedPlaylist.at(i).pos == trackId ){
804 ret.push_back(i);
805 break;
806 }
807 }
808 return ret;
809}
810
811void IPodPlaylistModel::search(const QString& searchText) {
812 if (sDebug)
813 qDebug() << this << "search" << searchText;
814
815 if (m_currentSearch != searchText) {
816 m_currentSearch = searchText;
817 if (itdb_playlist_is_mpl(m_pPlaylist)) {
818 setPlaylist(m_pPlaylist);
819 }
820 }
821}
822
823const QString IPodPlaylistModel::currentSearch() {
824 return m_currentSearch;
825}
826
827bool IPodPlaylistModel::isColumnInternal(int column) {
828 Q_UNUSED(column);
829 return false;
830}
831
832QMimeData* IPodPlaylistModel::mimeData(const QModelIndexList &indexes) const {
833 QMimeData *mimeData = new QMimeData();
834 QList<QUrl> urls;
835
836 //Ok, so the list of indexes we're given contains separates indexes for
837 //each column, so even if only one row is selected, we'll have like 7 indexes.
838 //We need to only count each row once:
839 QList<int> rows;
840
841 foreach (QModelIndex index, indexes) {
842 if (index.isValid()) {
843 if (!rows.contains(index.row())) {
844 rows.push_back(index.row());
845 QUrl url = QUrl::fromLocalFile(getTrackLocation(index));
846 if (!url.isValid())
847 qDebug() << "ERROR invalid url\n";
848 else
849 urls.append(url);
850 }
851 }
852 }
853 mimeData->setUrls(urls);
854 return mimeData;
855}
856 /** if no header state exists, we may hide some columns so that the user can reactivate them **/
857bool IPodPlaylistModel::isColumnHiddenByDefault(int column) {
858 Q_UNUSED(column);
859 return false;
860}
861
862void IPodPlaylistModel::removeTrack(const QModelIndex& index) {
863 Q_UNUSED(index);
864}
865
866void IPodPlaylistModel::removeTracks(const QModelIndexList& indices) {
867 Q_UNUSED(indices);
868}
869
870bool IPodPlaylistModel::addTrack(const QModelIndex& index, QString location) {
871 Q_UNUSED(index);
872 Q_UNUSED(location);
873 return false;
874}
875
876void IPodPlaylistModel::moveTrack(const QModelIndex& sourceIndex, const QModelIndex& destIndex) {
877 Q_UNUSED(destIndex);
878 Q_UNUSED(sourceIndex);
879}
880
881QItemDelegate* IPodPlaylistModel::delegateForColumn(const int i) {
882 Q_UNUSED(i);
883 return NULL;
884}
885
886Itdb_Track* IPodPlaylistModel::getPTrackFromModelIndex(const QModelIndex& index) const {
887 if ( !index.isValid()
888 || m_pPlaylist == NULL
889 ) {
890 return NULL;
891 }
892
893 int row = index.row();
894 int column = index.column();
895
896 if (row >= m_sortedPlaylist.size() || column >= m_headerList.size()) {
897 // index is outside the valid range
898 return NULL;
899 }
900 return m_sortedPlaylist.at(row).pTrack;
901}
0902
=== added file 'mixxx/src/library/ipod/ipodplaylistmodel.h'
--- mixxx/src/library/ipod/ipodplaylistmodel.h 1970-01-01 00:00:00 +0000
+++ mixxx/src/library/ipod/ipodplaylistmodel.h 2012-11-18 17:09:22 +0000
@@ -0,0 +1,146 @@
1#ifndef IPODPLAYLISTMODEL_H
2#define IPODPLAYLISTMODEL_H
3
4#include <QtCore>
5#include <QHash>
6#include <QtGui>
7#include <QtSql>
8
9#include "library/trackmodel.h"
10#include "library/trackcollection.h"
11#include "library/dao/trackdao.h"
12
13extern "C"
14{
15#include <gpod/itdb.h>
16}
17
18// BaseSqlTableModel is a custom-written SQL-backed table which aggressively
19// caches the contents of the table and supports lightweight updates.
20class IPodPlaylistModel : public QAbstractTableModel , public virtual TrackModel
21{
22 Q_OBJECT
23 public:
24
25 struct playlist_member {
26 IPodPlaylistModel* pClass;
27 int pos;
28 Itdb_Track* pTrack;
29 };
30
31 IPodPlaylistModel(QObject* pParent, TrackCollection* pTrackCollection);
32 virtual ~IPodPlaylistModel();
33
34 ////////////////////////////////////////////////////////////////////////////
35 // Methods implemented from QAbstractItemModel
36 ////////////////////////////////////////////////////////////////////////////
37
38 virtual TrackPointer getTrack(const QModelIndex& index) const;
39 virtual QString getTrackLocation(const QModelIndex& index) const;
40 virtual int getTrackId(const QModelIndex& index) const;
41 virtual const QLinkedList<int> getTrackRows(int trackId) const;
42 virtual void search(const QString& searchText);
43 virtual const QString currentSearch();
44 virtual bool isColumnInternal(int column);
45 virtual bool isColumnHiddenByDefault(int column);
46 virtual void removeTrack(const QModelIndex& index);
47 virtual void removeTracks(const QModelIndexList& indices);
48 virtual bool addTrack(const QModelIndex& index, QString location);
49 virtual void moveTrack(const QModelIndex& sourceIndex, const QModelIndex& destIndex);
50
51 virtual Qt::ItemFlags flags(const QModelIndex &index) const;
52 QMimeData* mimeData(const QModelIndexList &indexes) const;
53
54 QItemDelegate* delegateForColumn(const int i);
55 TrackModel::CapabilitiesFlags getCapabilities() const;
56
57 virtual void sort(int column, Qt::SortOrder order);
58 virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
59 virtual bool setData(const QModelIndex& index, const QVariant& value, int role=Qt::EditRole);
60 virtual int columnCount(const QModelIndex& parent=QModelIndex()) const;
61 virtual int rowCount(const QModelIndex& parent=QModelIndex()) const;
62 virtual QVariant headerData(int section, Qt::Orientation orientation,
63 int role=Qt::DisplayRole) const;
64
65 void setPlaylist(Itdb_Playlist* pPlaylist);
66
67 ////////////////////////////////////////////////////////////////////////////
68 // Other public methods
69 ////////////////////////////////////////////////////////////////////////////
70
71 virtual const QString currentSearch() const;
72 virtual void setSort(int column, Qt::SortOrder order);
73 virtual int fieldIndex(const QString& fieldName) const;
74
75 protected:
76 /** Use this if you want a model that is read-only. */
77 virtual Qt::ItemFlags readOnlyFlags(const QModelIndex &index) const;
78 /** Use this if you want a model that can be changed */
79 virtual Qt::ItemFlags readWriteFlags(const QModelIndex &index) const;
80
81 // Set the columns used for searching. Names must correspond to the column
82 // names in the table provided to setTable. Must be called after setTable is
83 // called.
84 virtual void setSearchColumns(const QStringList& searchColumns);
85 // virtual QString orderByClause() const;
86 virtual void initHeaderData();
87 virtual void initDefaultSearchColumns();
88
89 private slots:
90 void trackChanged(int trackId);
91
92 private:
93 inline TrackPointer lookupCachedTrack(int trackId) const;
94 inline QVariant getTrackValueForColumn(TrackPointer pTrack, int column) const;
95 inline QVariant getTrackValueForColumn(int trackId, int column,
96 TrackPointer pTrack=TrackPointer()) const;
97 inline void setTrackValueForColumn(TrackPointer pTrack, int column, QVariant value);
98 QVariant getBaseValue(const QModelIndex& index, int role = Qt::DisplayRole) const;
99
100 static bool columnLessThan(const playlist_member &s1, const playlist_member &s2);
101
102
103 virtual int compareColumnValues(int iColumnNumber, Qt::SortOrder eSortOrder, QVariant val1, QVariant val2);
104 virtual int findSortInsertionPoint(int trackId, TrackPointer pTrack,
105 const QVector<QPair<int, QHash<int, QVariant> > >& rowInfo);
106
107 Itdb_Track* getPTrackFromModelIndex(const QModelIndex& index) const;
108
109 static bool findInUtf8Case(gchar* heystack, gchar* needles);
110
111 QString m_tableName;
112 QStringList m_columnNames;
113 QString m_columnNamesJoined;
114 QHash<QString, int> m_columnIndex;
115 QSet<QString> m_tableColumns;
116 QString m_tableColumnsJoined;
117 QSet<int> m_tableColumnIndices;
118
119 QStringList m_searchColumns;
120 QVector<int> m_searchColumnIndices;
121 QString m_idColumn;
122
123 int m_iSortColumn;
124 Qt::SortOrder m_eSortOrder;
125
126 bool m_bIndexBuilt;
127 QSqlRecord m_queryRecord;
128 QHash<int, QVector<QVariant> > m_recordCache;
129 QVector<QPair<int, QHash<int, QVariant> > > m_rowInfo;
130 QHash<int, QLinkedList<int> > m_trackIdToRows;
131 QSet<int> m_trackOverrides;
132
133 QString m_currentSearch;
134 QString m_currentSearchFilter;
135
136 QList<QPair<QString, size_t> > m_headerList;
137
138 QList<IPodPlaylistModel::playlist_member> m_sortedPlaylist;
139
140 TrackCollection* m_pTrackCollection;
141 TrackDAO& m_trackDAO;
142
143 Itdb_Playlist* m_pPlaylist;
144};
145
146#endif /* IPODPLAYLISTMODEL_H */
0147
=== modified file 'mixxx/src/library/library.cpp'
--- mixxx/src/library/library.cpp 2012-10-04 16:19:07 +0000
+++ mixxx/src/library/library.cpp 2012-11-18 17:09:22 +0000
@@ -14,6 +14,9 @@
14#include "library/rhythmbox/rhythmboxfeature.h"14#include "library/rhythmbox/rhythmboxfeature.h"
15#include "library/recording/recordingfeature.h"15#include "library/recording/recordingfeature.h"
16#include "library/itunes/itunesfeature.h"16#include "library/itunes/itunesfeature.h"
17#ifdef __IPOD__
18#include "library/ipod/ipodfeature.h"
19#endif // __IPOD__
17#include "library/mixxxlibraryfeature.h"20#include "library/mixxxlibraryfeature.h"
18#include "library/autodjfeature.h"21#include "library/autodjfeature.h"
19#include "library/playlistfeature.h"22#include "library/playlistfeature.h"
@@ -75,6 +78,12 @@
75 pConfig->getValueString(ConfigKey("[Library]","ShowITunesLibrary"),"1").toInt()) {78 pConfig->getValueString(ConfigKey("[Library]","ShowITunesLibrary"),"1").toInt()) {
76 addFeature(new ITunesFeature(this, m_pTrackCollection));79 addFeature(new ITunesFeature(this, m_pTrackCollection));
77 }80 }
81#ifdef __IPOD__
82 if (IPodFeature::isSupported() &&
83 pConfig->getValueString(ConfigKey("[Library]","ShowIpod"),"1").toInt()) {
84 addFeature(new IPodFeature(this, m_pTrackCollection));
85 }
86#endif // __IPOD__
78 if (TraktorFeature::isSupported() &&87 if (TraktorFeature::isSupported() &&
79 pConfig->getValueString(ConfigKey("[Library]","ShowTraktorLibrary"),"1").toInt()) {88 pConfig->getValueString(ConfigKey("[Library]","ShowTraktorLibrary"),"1").toInt()) {
80 addFeature(new TraktorFeature(this, m_pTrackCollection));89 addFeature(new TraktorFeature(this, m_pTrackCollection));
8190
=== modified file 'mixxx/src/widget/wlibrarysidebar.cpp'
--- mixxx/src/widget/wlibrarysidebar.cpp 2012-07-25 17:20:41 +0000
+++ mixxx/src/widget/wlibrarysidebar.cpp 2012-11-18 17:09:22 +0000
@@ -59,7 +59,8 @@
59 if (event->mimeData()->hasUrls()) {59 if (event->mimeData()->hasUrls()) {
60 QList<QUrl> urls(event->mimeData()->urls());60 QList<QUrl> urls(event->mimeData()->urls());
61 //Drag and drop within this widget61 //Drag and drop within this widget
62 if (event->source() == this && event->possibleActions() & Qt::MoveAction) {62 if ( (event->source() == this)
63 && (event->possibleActions() & Qt::MoveAction)) {
63 //Do nothing.64 //Do nothing.
64 event->ignore();65 event->ignore();
65 } else {66 } else {
@@ -77,13 +78,15 @@
77 }78 }
78 }79 }
79 }80 }
80 if (accepted)81 if (accepted) {
81 event->acceptProposedAction();82 event->acceptProposedAction();
82 else83 } else {
83 event->ignore();84 event->ignore();
85 }
84 }86 }
85 } else87 } else {
86 event->ignore();88 event->ignore();
89 }
87}90}
8891
89void WLibrarySidebar::timerEvent(QTimerEvent *event) {92void WLibrarySidebar::timerEvent(QTimerEvent *event) {
@@ -106,8 +109,10 @@
106 if (event->mimeData()->hasUrls()) {109 if (event->mimeData()->hasUrls()) {
107 QList<QUrl> urls(event->mimeData()->urls());110 QList<QUrl> urls(event->mimeData()->urls());
108 //Drag and drop within this widget111 //Drag and drop within this widget
109 if (event->source() == this && event->possibleActions() & Qt::MoveAction) {112 if ( (event->source() == this)
110 event->ignore();113 && (event->possibleActions() & Qt::MoveAction)) {
114 //Do nothing.
115 event->ignore();
111 } else {116 } else {
112 //Reset the selected items (if you had anything highlighted, it clears it)117 //Reset the selected items (if you had anything highlighted, it clears it)
113 //this->selectionModel()->clear();118 //this->selectionModel()->clear();
114119
=== modified file 'mixxx/src/widget/wtracktableview.cpp'
--- mixxx/src/widget/wtracktableview.cpp 2012-11-16 04:38:16 +0000
+++ mixxx/src/widget/wtracktableview.cpp 2012-11-18 17:09:22 +0000
@@ -959,11 +959,11 @@
959959
960void WTrackTableView::slotSendToAutoDJ() {960void WTrackTableView::slotSendToAutoDJ() {
961 // append to auto DJ961 // append to auto DJ
962 sendToAutoDJ(false); // bTop = false962 sendToAutoDJ(false); // bTop = false
963}963}
964964
965void WTrackTableView::slotSendToAutoDJTop() {965void WTrackTableView::slotSendToAutoDJTop() {
966 sendToAutoDJ(true); // bTop = true966 sendToAutoDJ(true); // bTop = true
967}967}
968968
969void WTrackTableView::sendToAutoDJ(bool bTop) {969void WTrackTableView::sendToAutoDJ(bool bTop) {