Merge lp:~mixxxdevelopers/mixxx/tree_item_browser into lp:~mixxxdevelopers/mixxx/trunk
- tree_item_browser
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 2714 | ||||
Proposed branch: | lp:~mixxxdevelopers/mixxx/tree_item_browser | ||||
Merge into: | lp:~mixxxdevelopers/mixxx/trunk | ||||
Diff against target: |
1262 lines (+452/-121) 36 files modified
mixxx/src/dlgprefplaylist.cpp (+4/-0) mixxx/src/dlgprefplaylistdlg.ui (+7/-0) mixxx/src/library/autodjfeature.cpp (+3/-0) mixxx/src/library/autodjfeature.h (+1/-0) mixxx/src/library/browse/browsefeature.cpp (+21/-10) mixxx/src/library/browse/browsefeature.h (+3/-1) mixxx/src/library/browse/browsetablemodel.cpp (+33/-25) mixxx/src/library/browse/browsetablemodel.h (+40/-34) mixxx/src/library/browse/browsethread.cpp (+76/-30) mixxx/src/library/browse/browsethread.h (+28/-6) mixxx/src/library/cratefeature.cpp (+46/-3) mixxx/src/library/cratefeature.h (+6/-1) mixxx/src/library/itunes/itunesfeature.cpp (+3/-1) mixxx/src/library/itunes/itunesfeature.h (+1/-0) mixxx/src/library/library.cpp (+3/-3) mixxx/src/library/libraryfeature.h (+11/-0) mixxx/src/library/mixxxlibraryfeature.cpp (+2/-0) mixxx/src/library/mixxxlibraryfeature.h (+1/-0) mixxx/src/library/parser.cpp (+3/-0) mixxx/src/library/parser.h (+1/-0) mixxx/src/library/parserm3u.cpp (+34/-0) mixxx/src/library/parserm3u.h (+3/-0) mixxx/src/library/parserpls.cpp (+31/-0) mixxx/src/library/parserpls.h (+2/-0) mixxx/src/library/playlistfeature.cpp (+46/-2) mixxx/src/library/playlistfeature.h (+6/-1) mixxx/src/library/preparefeature.cpp (+5/-0) mixxx/src/library/preparefeature.h (+1/-0) mixxx/src/library/promotracksfeature.cpp (+3/-0) mixxx/src/library/promotracksfeature.h (+1/-0) mixxx/src/library/rhythmbox/rhythmboxfeature.cpp (+3/-1) mixxx/src/library/rhythmbox/rhythmboxfeature.h (+1/-0) mixxx/src/library/sidebarmodel.cpp (+18/-3) mixxx/src/library/sidebarmodel.h (+1/-0) mixxx/src/library/traktor/traktorfeature.cpp (+3/-0) mixxx/src/library/traktor/traktorfeature.h (+1/-0) |
||||
To merge this branch: | bzr merge lp:~mixxxdevelopers/mixxx/tree_item_browser | ||||
Related bugs: |
|
||||
Related blueprints: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
RJ Skerry-Ryan | Approve | ||
Review via email:
|
Commit message
Description of the change
This a rewrite of the BrowseFeature and has been tested on OS X, Windows and Linux.
The BrowseFeature is now expandable showing drive letters and folders. All supported audio files are listed in the right track table view along with their metadata.
Thanks,
Tobias
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
RAFFI TEA (raffitea) wrote : | # |
- 2659. By Raffitea
-
Fixed some typos.
- 2660. By Raffitea
-
Implemented Playlist Export to M3U and PLS.
- 2661. By Raffitea
-
merging from trunk
- 2662. By Raffitea
-
Implemented relative M3U and PLS playlist export. Works fine on OS X with VLC but iTunes does not support relative paths.
- 2663. By Raffitea
-
Added an option to preferences to make relative playlist export optional.
- 2664. By Raffitea
-
Fixed a compiler warning on Windows. Also tested PLS and M3U export on Windows. Works as expected with Media Player and iTunes but also VLC, RealPlayer and Winamp.
- 2665. By Raffitea
-
Added a comment about QDir::relativeP
ath().
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
RAFFI TEA (raffitea) wrote : | # |
I've added playlist export to M3U and PLS since it is requested more than once in the forums. Relative paths for PLS and M3U is realized. By default relative paths are turned off. You can enable that feature in the preferences under "Library".
Please not that playlist import is already in trunk which supports relative paths, too.
Hi Tobias,
thanks for the good work, really like the improvements.
Currently testing http://
1st
Performance problem in "Browse" mode
The track listing is super slow, its like every track is displayed line after line. It takes about 2 seconds to display a folder with 50 mp3 tracks in there.
Every track got its own "Debug: []: ~TrackInfoObject()" line in the terminal output.
2nd
Could you please elaborate how to save a playlist in pls format? The "Export Playlist" file dialog saves as m3u per default.
> Currently testing http://
> _browser/
Ok, reply to myself since there is no edit function :-)
It was actually http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
RJ Skerry-Ryan (rryan) wrote : | # |
I believe the slow populating is because it pauses for 50ms between reading
every track. Tobias said this was to ease the load that browse-mode puts on
the computer to prevent skips while playing and browsing at the same time.
On Mon, Mar 14, 2011 at 11:46 AM, jus <email address hidden> wrote:
> > Currently testing
> http://
> > _browser/
> Ok, reply to myself since there is no edit function :-)
> It was actually
> http://
>
> --
>
> https:/
> You are requested to review the proposed merge of
> lp:~mixxxdevelopers/mixxx/tree_item_browser into lp:mixxx.
>
- 2668. By Raffitea
-
Improved Playlist export dialog.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
RAFFI TEA (raffitea) wrote : | # |
Addressed most of your problems in r2668.
You're right. The performance is quite slow. I did it on purpose because a faster browse feature will cause the waveform to stutter. Feel free to tweak the browser performance. You'll need to decrease the value of line 130 in browsethread.cpp.
I'll try to improve the performance soon. I've some ideas on how to realize that :-)
- 2669. By Raffitea
-
Drastically improved the population of the tracktable view in BrowseFeature. Hopefully does not freez the GUI while clicking through the folder structure.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
RAFFI TEA (raffitea) wrote : | # |
Ok. Performance should be much better now while not hitting the waveform to much :-)
- 2670. By Tobias <Tobias@Silverstar>
-
Merging manually from trunk
- 2671. By Tobias <Tobias@Silverstar>
-
Merging manually from trunk the third time.
- 2672. By Tobias <Tobias@Silverstar>
-
Merging from trunk to get library caching fixes
- 2673. By Raffitea
-
made BrowseTableModel singleton because the recording feature will use the BrowseTableModel to display contents of a special recording directory. This approach avoids two BrowseThreads. If I had made BrowseThread singleton there would be two BrowseTableModel objects and both would populate when the Thread is active. This is not performant
- 2674. By Raffitea
-
Merging from trunk to get PrepareFeature fixes
- 2675. By Raffitea
-
Merging from trunk
- 2676. By Raffitea
-
Merging from trunk
- 2677. By Raffitea
-
Removed the singleton character of BrowseTableModel. This was a really stupid idea....
- 2678. By Raffitea
-
Now made BrowseThread singleton. BrowseTableModel objects will only process received signals if they have asked the thread to do so.
- 2679. By Raffitea
-
Merging from trunk to get auto-expand feature
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
RJ Skerry-Ryan (rryan) wrote : | # |
More solid work, Tobias!
* The strings in the CrateFeature context menu should be "Export Crate" and "Import Crate" probably.
* If the user types an invalid filename, I would re-popup the box with whatever they typed so they can pick a new name. I typed "crate" and hit save and then had to right-click and hit export again and type "crate.pls".
* BrowseTableModel now has BlockingQueuedC
* Could you add a static member variable s_mutex alongside the m_instance (also, rename that to s_instance to call out that it's static?) variable in BrowseTableModel and use that instead of the local-variable static mutex in the getInstance/
- 2680. By Raffitea
-
Merging from trunk
- 2681. By Raffitea
-
Renamed context menu entry for crate export/import; If file extension is missing, append .m3u to the export file and continue to export in m3u; Replaced Qt::BlockingQue
uedConnection with QueuedConnection, which seems to work for rapid sidebar folder iterations with keyboard/MIDI; Added a global static member for BrowseThread: :getInstance( ).
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
RAFFI TEA (raffitea) wrote : | # |
Addressed all your concerns.
If QueuedConnection cause segfaults on rapid sidebar folder iterations, we can replace it with the BlockingQueuedC
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
RJ Skerry-Ryan (rryan) wrote : | # |
Cool, looks good. Merge away!
Preview Diff
1 | === modified file 'mixxx/src/dlgprefplaylist.cpp' |
2 | --- mixxx/src/dlgprefplaylist.cpp 2011-03-05 05:18:52 +0000 |
3 | +++ mixxx/src/dlgprefplaylist.cpp 2011-03-28 22:22:23 +0000 |
4 | @@ -152,6 +152,7 @@ |
5 | checkBoxPromoStats->setChecked((bool)config->getValueString(ConfigKey("[Promo]","StatTracking")).toInt()); |
6 | checkBox_library_scan->setChecked((bool)config->getValueString(ConfigKey("[Library]","RescanOnStartup")).toInt()); |
7 | checkbox_ID3_sync->setChecked((bool)config->getValueString(ConfigKey("[Library]","WriteAudioTags")).toInt()); |
8 | + checkBox_use_relative_path->setChecked((bool)config->getValueString(ConfigKey("[Library]","UseRelativePathOnExport")).toInt()); |
9 | |
10 | |
11 | } |
12 | @@ -237,6 +238,9 @@ |
13 | config->set(ConfigKey("[Library]","WriteAudioTags"), |
14 | ConfigValue((int)checkbox_ID3_sync->isChecked())); |
15 | |
16 | + config->set(ConfigKey("[Library]","UseRelativePathOnExport"), |
17 | + ConfigValue((int)checkBox_use_relative_path->isChecked())); |
18 | + |
19 | |
20 | config->Save(); |
21 | |
22 | |
23 | === modified file 'mixxx/src/dlgprefplaylistdlg.ui' |
24 | --- mixxx/src/dlgprefplaylistdlg.ui 2010-12-30 09:59:53 +0000 |
25 | +++ mixxx/src/dlgprefplaylistdlg.ui 2011-03-28 22:22:23 +0000 |
26 | @@ -168,6 +168,13 @@ |
27 | </property> |
28 | </widget> |
29 | </item> |
30 | + <item row="2" column="0"> |
31 | + <widget class="QCheckBox" name="checkBox_use_relative_path"> |
32 | + <property name="text"> |
33 | + <string>Use relative paths for playlist export if possible</string> |
34 | + </property> |
35 | + </widget> |
36 | + </item> |
37 | </layout> |
38 | </widget> |
39 | </item> |
40 | |
41 | === modified file 'mixxx/src/library/autodjfeature.cpp' |
42 | --- mixxx/src/library/autodjfeature.cpp 2011-03-27 01:36:30 +0000 |
43 | +++ mixxx/src/library/autodjfeature.cpp 2011-03-28 22:22:23 +0000 |
44 | @@ -120,3 +120,6 @@ |
45 | QUrl url) { |
46 | return false; |
47 | } |
48 | +void AutoDJFeature::onLazyChildExpandation(const QModelIndex &index){ |
49 | + //Nothing to do because the childmodel is not of lazy nature. |
50 | +} |
51 | |
52 | === modified file 'mixxx/src/library/autodjfeature.h' |
53 | --- mixxx/src/library/autodjfeature.h 2010-12-23 18:55:54 +0000 |
54 | +++ mixxx/src/library/autodjfeature.h 2011-03-28 22:22:23 +0000 |
55 | @@ -42,6 +42,7 @@ |
56 | void activateChild(const QModelIndex& index); |
57 | void onRightClick(const QPoint& globalPos); |
58 | void onRightClickChild(const QPoint& globalPos, QModelIndex index); |
59 | + void onLazyChildExpandation(const QModelIndex& index); |
60 | |
61 | private: |
62 | ConfigObject<ConfigValue>* m_pConfig; |
63 | |
64 | === modified file 'mixxx/src/library/browse/browsefeature.cpp' |
65 | --- mixxx/src/library/browse/browsefeature.cpp 2011-03-20 03:47:06 +0000 |
66 | +++ mixxx/src/library/browse/browsefeature.cpp 2011-03-28 22:22:23 +0000 |
67 | @@ -135,10 +135,30 @@ |
68 | void BrowseFeature::activate() { |
69 | emit(restoreSearch(m_currentSearch)); |
70 | } |
71 | - |
72 | +/* |
73 | + * Note: This is executed whenever you single click on an child item |
74 | + * Single clicks will not populate sub folders |
75 | + */ |
76 | void BrowseFeature::activateChild(const QModelIndex& index) { |
77 | TreeItem *item = static_cast<TreeItem*>(index.internalPointer()); |
78 | qDebug() << "BrowseFeature::activateChild " << item->data() << " " << item->dataPath(); |
79 | + m_browseModel.setPath(item->dataPath().toString()); |
80 | + emit(showTrackModel(&m_proxyModel)); |
81 | + |
82 | +} |
83 | + |
84 | +void BrowseFeature::onRightClick(const QPoint& globalPos) { |
85 | +} |
86 | + |
87 | +void BrowseFeature::onRightClickChild(const QPoint& globalPos, QModelIndex index) { |
88 | +} |
89 | +/* |
90 | + * This is called whenever you double click or use the triangle symbol to expand |
91 | + * the subtree. The method will read the subfolders. |
92 | + */ |
93 | +void BrowseFeature::onLazyChildExpandation(const QModelIndex &index){ |
94 | + TreeItem *item = static_cast<TreeItem*>(index.internalPointer()); |
95 | + qDebug() << "BrowseFeature::onLazyChildExpandation " << item->data() << " " << item->dataPath(); |
96 | |
97 | // If the item is a build-in node, e.g., 'QuickLink' return |
98 | if(item->dataPath().toString() == QUICK_LINK_NODE) |
99 | @@ -191,13 +211,4 @@ |
100 | //On Ubuntu 10.04, otherwise, this will draw an icon although the folder has no subfolders |
101 | if(!folders.isEmpty()) |
102 | m_childModel.insertRows(folders, 0, folders.size() , index); |
103 | - m_browseModel.setPath(item->dataPath().toString()); |
104 | - emit(showTrackModel(&m_proxyModel)); |
105 | - |
106 | -} |
107 | - |
108 | -void BrowseFeature::onRightClick(const QPoint& globalPos) { |
109 | -} |
110 | - |
111 | -void BrowseFeature::onRightClickChild(const QPoint& globalPos, QModelIndex index) { |
112 | } |
113 | |
114 | === modified file 'mixxx/src/library/browse/browsefeature.h' |
115 | --- mixxx/src/library/browse/browsefeature.h 2011-03-20 03:47:06 +0000 |
116 | +++ mixxx/src/library/browse/browsefeature.h 2011-03-28 22:22:23 +0000 |
117 | @@ -42,7 +42,9 @@ |
118 | void onRightClick(const QPoint& globalPos); |
119 | void onRightClickChild(const QPoint& globalPos, QModelIndex index); |
120 | |
121 | - signals: |
122 | + void onLazyChildExpandation(const QModelIndex& index); |
123 | + |
124 | + signals: |
125 | void setRootIndex(const QModelIndex&); |
126 | |
127 | private: |
128 | |
129 | === modified file 'mixxx/src/library/browse/browsetablemodel.cpp' |
130 | --- mixxx/src/library/browse/browsetablemodel.cpp 2011-03-20 03:47:06 +0000 |
131 | +++ mixxx/src/library/browse/browsetablemodel.cpp 2011-03-28 22:22:23 +0000 |
132 | @@ -38,16 +38,18 @@ |
133 | addSearchColumn(COLUMN_KEY); |
134 | addSearchColumn(COLUMN_COMMENT); |
135 | |
136 | - m_backgroundThread.start(QThread::LowestPriority); |
137 | + |
138 | |
139 | setHorizontalHeaderLabels(header_data); |
140 | //register the QList<T> as a metatype since we use QueuedConnection below |
141 | - qRegisterMetaType< QList<QStandardItem*> >("QList<QStandardItem*>"); |
142 | - |
143 | - QObject::connect(&m_backgroundThread, SIGNAL(clearModel()), |
144 | - this, SLOT(slotClear()), Qt::BlockingQueuedConnection); |
145 | - QObject::connect(&m_backgroundThread, SIGNAL(rowDataAppended(const QList<QStandardItem*>&)), |
146 | - this, SLOT(slotInsert(const QList<QStandardItem*>&)), Qt::BlockingQueuedConnection); |
147 | + qRegisterMetaType< QList< QList<QStandardItem*> > >("QList< QList<QStandardItem*> >"); |
148 | + qRegisterMetaType<BrowseTableModel*>("BrowseTableModel*"); |
149 | + |
150 | + QObject::connect(BrowseThread::getInstance(), SIGNAL(clearModel(BrowseTableModel*)), |
151 | + this, SLOT(slotClear(BrowseTableModel*)), Qt::QueuedConnection); |
152 | + |
153 | + QObject::connect(BrowseThread::getInstance(), SIGNAL(rowsAppended(const QList< QList<QStandardItem*> >&, BrowseTableModel*)), |
154 | + this, SLOT(slotInsert(const QList< QList<QStandardItem*> >&, BrowseTableModel*)), Qt::QueuedConnection); |
155 | |
156 | } |
157 | |
158 | @@ -64,7 +66,8 @@ |
159 | } |
160 | void BrowseTableModel::setPath(QString absPath) |
161 | { |
162 | - m_backgroundThread.setPath(absPath); |
163 | + BrowseThread::getInstance()->executePopulation(absPath, this); |
164 | + |
165 | } |
166 | |
167 | TrackPointer BrowseTableModel::getTrack(const QModelIndex& index) const |
168 | @@ -73,16 +76,6 @@ |
169 | return TrackPointer(tio, &QObject::deleteLater); |
170 | } |
171 | |
172 | -int BrowseTableModel::getTrackId(const QModelIndex& index) const { |
173 | - // We can't implement this as it stands. |
174 | - return -1; |
175 | -} |
176 | - |
177 | -int BrowseTableModel::getTrackRow(int trackId) const { |
178 | - // We can't implement this as it stands. |
179 | - return -1; |
180 | -} |
181 | - |
182 | QString BrowseTableModel::getTrackLocation(const QModelIndex& index) const |
183 | { |
184 | int row = index.row(); |
185 | @@ -91,7 +84,15 @@ |
186 | return data(index2).toString(); |
187 | |
188 | } |
189 | +int BrowseTableModel::getTrackId(const QModelIndex& index) const { |
190 | + // We can't implement this as it stands. |
191 | + return -1; |
192 | +} |
193 | |
194 | +int BrowseTableModel::getTrackRow(int trackId) const { |
195 | + // We can't implement this as it stands. |
196 | + return -1; |
197 | +} |
198 | void BrowseTableModel::search(const QString& searchText) |
199 | { |
200 | |
201 | @@ -159,14 +160,21 @@ |
202 | return mimeData; |
203 | } |
204 | |
205 | -void BrowseTableModel::slotClear() |
206 | +void BrowseTableModel::slotClear(BrowseTableModel* caller_object) |
207 | { |
208 | - removeRows(0, rowCount()); |
209 | + if(caller_object == this) |
210 | + removeRows(0, rowCount()); |
211 | } |
212 | |
213 | -void BrowseTableModel::slotInsert(const QList<QStandardItem*> &column_data) |
214 | -{ |
215 | - appendRow(column_data); |
216 | - //Does not work for some reason |
217 | - //setItem(row, column, item); |
218 | + |
219 | +void BrowseTableModel::slotInsert(const QList< QList<QStandardItem*> >& rows, BrowseTableModel* caller_object){ |
220 | + //There exists more than one BrowseTableModel in Mixxx |
221 | + //We only want to receive items here, this object has 'ordered' by the BrowserThread (singleton) |
222 | + if(caller_object == this){ |
223 | + //qDebug() << "BrowseTableModel::slotInsert"; |
224 | + for(int i=0; i < rows.size(); ++i){ |
225 | + appendRow(rows.at(i)); |
226 | + } |
227 | + } |
228 | + |
229 | } |
230 | |
231 | === modified file 'mixxx/src/library/browse/browsetablemodel.h' |
232 | --- mixxx/src/library/browse/browsetablemodel.h 2011-03-20 03:47:06 +0000 |
233 | +++ mixxx/src/library/browse/browsetablemodel.h 2011-03-28 22:22:23 +0000 |
234 | @@ -23,41 +23,47 @@ |
235 | const int COLUMN_TYPE = 11; |
236 | const int COLUMN_BITRATE = 12; |
237 | const int COLUMN_LOCATION = 13; |
238 | - |
239 | -class BrowseTableModel : public QStandardItemModel, public TrackModel { |
240 | +/* |
241 | + * The BrowseTable models displays tracks |
242 | + * of given directory on the HDD. |
243 | + * The class is realized as sigleton because |
244 | + * the 'recording' feature uses the BrowseTableModel, too. |
245 | + * This simply does not create two BrowseThreads |
246 | + */ |
247 | +class BrowseTableModel : public QStandardItemModel, public TrackModel |
248 | +{ |
249 | + |
250 | Q_OBJECT |
251 | - public: |
252 | - BrowseTableModel(QObject* parent); |
253 | - ~BrowseTableModel(); |
254 | - |
255 | - void setPath(QString absPath); |
256 | - //reimplemented from TrackModel class |
257 | - virtual TrackPointer getTrack(const QModelIndex& index) const; |
258 | - virtual QString getTrackLocation(const QModelIndex& index) const; |
259 | - virtual int getTrackId(const QModelIndex& index) const; |
260 | - virtual int getTrackRow(int trackId) const; |
261 | - |
262 | - virtual void search(const QString& searchText); |
263 | - virtual void removeTrack(const QModelIndex& index); |
264 | - virtual void removeTracks(const QModelIndexList& indices); |
265 | - virtual bool addTrack(const QModelIndex& index, QString location); |
266 | - virtual QMimeData* mimeData(const QModelIndexList &indexes) const; |
267 | - virtual const QString currentSearch(); |
268 | - virtual bool isColumnInternal(int); |
269 | - virtual void moveTrack(const QModelIndex&, const QModelIndex&); |
270 | - virtual QItemDelegate* delegateForColumn(const int); |
271 | - virtual bool isColumnHiddenByDefault(int column); |
272 | - virtual const QList<int>& searchColumns() const; |
273 | - |
274 | - public slots: |
275 | - void slotClear(); |
276 | - void slotInsert(const QList<QStandardItem*> &item); |
277 | - |
278 | - private: |
279 | - void addSearchColumn(int index); |
280 | - |
281 | - BrowseThread m_backgroundThread; |
282 | - QList<int> m_searchColumns; |
283 | + public: |
284 | + BrowseTableModel(QObject* parent); |
285 | + virtual ~BrowseTableModel(); |
286 | + void setPath(QString absPath); |
287 | + //reimplemented from TrackModel class |
288 | + virtual TrackPointer getTrack(const QModelIndex& index) const; |
289 | + virtual QString getTrackLocation(const QModelIndex& index) const; |
290 | + virtual int getTrackId(const QModelIndex& index) const; |
291 | + virtual int getTrackRow(int trackId) const; |
292 | + |
293 | + virtual void search(const QString& searchText); |
294 | + virtual void removeTrack(const QModelIndex& index); |
295 | + virtual void removeTracks(const QModelIndexList& indices); |
296 | + virtual bool addTrack(const QModelIndex& index, QString location); |
297 | + virtual QMimeData* mimeData(const QModelIndexList &indexes) const; |
298 | + virtual const QString currentSearch(); |
299 | + virtual bool isColumnInternal(int); |
300 | + virtual void moveTrack(const QModelIndex&, const QModelIndex&); |
301 | + virtual QItemDelegate* delegateForColumn(const int); |
302 | + virtual bool isColumnHiddenByDefault(int column); |
303 | + virtual const QList<int>& searchColumns() const; |
304 | + private: |
305 | + |
306 | + |
307 | + void addSearchColumn(int index); |
308 | + QList<int> m_searchColumns; |
309 | + |
310 | + public slots: |
311 | + void slotClear(BrowseTableModel*); |
312 | + void slotInsert(const QList< QList<QStandardItem*> >&, BrowseTableModel*); |
313 | }; |
314 | |
315 | #endif |
316 | |
317 | === modified file 'mixxx/src/library/browse/browsethread.cpp' |
318 | --- mixxx/src/library/browse/browsethread.cpp 2011-03-20 03:47:06 +0000 |
319 | +++ mixxx/src/library/browse/browsethread.cpp 2011-03-28 22:22:23 +0000 |
320 | @@ -1,3 +1,7 @@ |
321 | +/* |
322 | + * browsethread.cpp (C) 2011 Tobias Rafreider |
323 | + */ |
324 | + |
325 | #include <QStringList> |
326 | #include <QDirIterator> |
327 | #include <QtCore> |
328 | @@ -7,10 +11,25 @@ |
329 | #include "soundsourceproxy.h" |
330 | #include "mixxxutils.cpp" |
331 | |
332 | -BrowseThread::BrowseThread(QObject *parent) |
333 | - : QThread(parent), |
334 | - m_bStopThread(false) { |
335 | - //QObject::moveToThread(this); |
336 | +BrowseThread* BrowseThread::m_instance = 0; |
337 | +static QMutex s_Mutex; |
338 | + |
339 | +/* |
340 | + * This class is a singleton and represents a thread |
341 | + * that is used to read ID3 metadata |
342 | + * from a particular folder. |
343 | + * |
344 | + * The BroseTableModel uses this class. |
345 | + * Note: Don't call getInstance() from places |
346 | + * other than the GUI thread. BrowseThreads emit |
347 | + * signals to BrowseModel objects. It does not |
348 | + * make sense to use this class in non-GUI threads |
349 | + */ |
350 | +BrowseThread::BrowseThread(QObject *parent): QThread(parent) |
351 | +{ |
352 | + m_bStopThread = false; |
353 | + //start Thread |
354 | + start(QThread::LowestPriority); |
355 | } |
356 | |
357 | BrowseThread::~BrowseThread() { |
358 | @@ -23,9 +42,31 @@ |
359 | wait(); |
360 | qDebug() << "Browser background thread terminated!"; |
361 | } |
362 | - |
363 | -void BrowseThread::setPath(QString& path) { |
364 | +BrowseThread* BrowseThread::getInstance(){ |
365 | + if (!m_instance) |
366 | + { |
367 | + s_Mutex.lock();; |
368 | + |
369 | + if (!m_instance) |
370 | + m_instance = new BrowseThread(); |
371 | + |
372 | + s_Mutex.unlock(); |
373 | + } |
374 | + return m_instance; |
375 | +} |
376 | +void BrowseThread::destroyInstance() |
377 | +{ |
378 | + s_Mutex.lock(); |
379 | + if(m_instance){ |
380 | + delete m_instance; |
381 | + m_instance = 0; |
382 | + } |
383 | + s_Mutex.unlock(); |
384 | +} |
385 | + |
386 | +void BrowseThread::executePopulation(QString& path, BrowseTableModel* client) { |
387 | m_path = path; |
388 | + m_model_observer = client; |
389 | m_locationUpdated.wakeAll(); |
390 | } |
391 | |
392 | @@ -56,8 +97,9 @@ |
393 | * This is a blocking operation |
394 | * see signal/slot connection in BrowseTableModel |
395 | */ |
396 | - emit(clearModel()); |
397 | + emit(clearModel(m_model_observer)); |
398 | |
399 | + QList< QList<QStandardItem*> > rows; |
400 | |
401 | int row = 0; |
402 | //Iterate over the files |
403 | @@ -74,59 +116,63 @@ |
404 | |
405 | QString filepath = fileIt.next(); |
406 | TrackInfoObject tio(filepath); |
407 | - QList<QStandardItem*> column_data; |
408 | + QList<QStandardItem*> row_data; |
409 | |
410 | QStandardItem* item = new QStandardItem(tio.getFilename()); |
411 | - column_data.insert(COLUMN_FILENAME, item); |
412 | + row_data.insert(COLUMN_FILENAME, item); |
413 | |
414 | item = new QStandardItem(tio.getArtist()); |
415 | - column_data.insert(COLUMN_ARTIST, item); |
416 | + row_data.insert(COLUMN_ARTIST, item); |
417 | |
418 | item = new QStandardItem(tio.getTitle()); |
419 | - column_data.insert(COLUMN_TITLE, item); |
420 | + row_data.insert(COLUMN_TITLE, item); |
421 | |
422 | item = new QStandardItem(tio.getAlbum()); |
423 | - column_data.insert(COLUMN_ALBUM, item); |
424 | + row_data.insert(COLUMN_ALBUM, item); |
425 | |
426 | item = new QStandardItem(tio.getTrackNumber()); |
427 | - column_data.insert(COLUMN_TRACK_NUMBER, item); |
428 | + row_data.insert(COLUMN_TRACK_NUMBER, item); |
429 | |
430 | item = new QStandardItem(tio.getYear()); |
431 | - column_data.insert(COLUMN_YEAR, item); |
432 | + row_data.insert(COLUMN_YEAR, item); |
433 | |
434 | item = new QStandardItem(tio.getGenre()); |
435 | - column_data.insert(COLUMN_GENRE, item); |
436 | + row_data.insert(COLUMN_GENRE, item); |
437 | |
438 | item = new QStandardItem(tio.getComment()); |
439 | - column_data.insert(COLUMN_COMMENT, item); |
440 | + row_data.insert(COLUMN_COMMENT, item); |
441 | |
442 | QString duration = MixxxUtils::secondsToMinutes(qVariantValue<int>(tio.getDuration())); |
443 | item = new QStandardItem(duration); |
444 | - column_data.insert(COLUMN_DURATION, item); |
445 | + row_data.insert(COLUMN_DURATION, item); |
446 | |
447 | item = new QStandardItem(tio.getBpmStr()); |
448 | - column_data.insert(COLUMN_BPM, item); |
449 | + row_data.insert(COLUMN_BPM, item); |
450 | |
451 | item = new QStandardItem(tio.getKey()); |
452 | - column_data.insert(COLUMN_KEY, item); |
453 | + row_data.insert(COLUMN_KEY, item); |
454 | |
455 | item = new QStandardItem(tio.getType()); |
456 | - column_data.insert(COLUMN_TYPE, item); |
457 | + row_data.insert(COLUMN_TYPE, item); |
458 | |
459 | item = new QStandardItem(tio.getBitrateStr()); |
460 | - column_data.insert(COLUMN_BITRATE, item); |
461 | + row_data.insert(COLUMN_BITRATE, item); |
462 | |
463 | item = new QStandardItem(filepath); |
464 | - column_data.insert(COLUMN_LOCATION, item); |
465 | - |
466 | - // this is a blocking operation |
467 | - // see signal/slot connection in BrowseTableModel |
468 | - emit(rowDataAppended(column_data)); |
469 | - |
470 | - //QCoreApplication::processEvents(); |
471 | + row_data.insert(COLUMN_LOCATION, item); |
472 | + |
473 | + rows.append(row_data); |
474 | ++row; |
475 | + //If 10 tracks have been analyzed, send it to GUI |
476 | + //Will limit GUI freezing |
477 | + if(row % 10 == 0){ |
478 | + //this is a blocking operation |
479 | + emit(rowsAppended(rows, m_model_observer)); |
480 | |
481 | - //Sleep for 50ms which prevents us from GUI freezes |
482 | - msleep(50); |
483 | + rows.clear(); |
484 | + } |
485 | + //Sleep additionally for 10ms which prevents us from GUI freezes |
486 | + msleep(20); |
487 | } |
488 | + emit(rowsAppended(rows, m_model_observer)); |
489 | } |
490 | |
491 | === modified file 'mixxx/src/library/browse/browsethread.h' |
492 | --- mixxx/src/library/browse/browsethread.h 2011-03-20 03:47:06 +0000 |
493 | +++ mixxx/src/library/browse/browsethread.h 2011-03-28 22:22:23 +0000 |
494 | @@ -1,3 +1,7 @@ |
495 | +/* |
496 | + * browsethread.h (C) 2011 Tobias Rafreider |
497 | + */ |
498 | + |
499 | #ifndef BROWSETHREAD_H |
500 | #define BROWSETHREAD_H |
501 | |
502 | @@ -7,20 +11,35 @@ |
503 | #include <QStandardItem> |
504 | #include <QList> |
505 | |
506 | +#include "library/browse/browsetablemodel.h" |
507 | + |
508 | +/* |
509 | + * This class is a singleton and represents a thread |
510 | + * that is used to read ID3 metadata |
511 | + * from a particular folder. |
512 | + * |
513 | + * The BroseTableModel uses this class. |
514 | + * Note: Don't call getInstance() from places |
515 | + * other than the GUI thread. |
516 | + */ |
517 | +class BrowseTableModel; |
518 | + |
519 | class BrowseThread : public QThread { |
520 | Q_OBJECT |
521 | public: |
522 | - BrowseThread(QObject *parent = 0); |
523 | - ~BrowseThread(); |
524 | - |
525 | - void setPath(QString& path); |
526 | + void executePopulation(QString& path, BrowseTableModel* client); |
527 | void run(); |
528 | + static BrowseThread* getInstance(); |
529 | + static void destroyInstance(); |
530 | |
531 | signals: |
532 | - void rowDataAppended(const QList<QStandardItem*>&); |
533 | - void clearModel(); |
534 | + void rowsAppended(const QList< QList<QStandardItem*> >&, BrowseTableModel*); |
535 | + void clearModel(BrowseTableModel*); |
536 | |
537 | private: |
538 | + BrowseThread(QObject *parent = 0); |
539 | + virtual ~BrowseThread(); |
540 | + |
541 | void populateModel(); |
542 | |
543 | QMutex m_mutex; |
544 | @@ -28,6 +47,9 @@ |
545 | QList<int> m_searchColumns; |
546 | QString m_path; |
547 | bool m_bStopThread; |
548 | + |
549 | + static BrowseThread* m_instance; |
550 | + BrowseTableModel* m_model_observer; |
551 | }; |
552 | |
553 | #endif // BROWSETHREAD_H |
554 | |
555 | === modified file 'mixxx/src/library/cratefeature.cpp' |
556 | --- mixxx/src/library/cratefeature.cpp 2011-03-27 01:36:30 +0000 |
557 | +++ mixxx/src/library/cratefeature.cpp 2011-03-28 22:22:23 +0000 |
558 | @@ -20,9 +20,10 @@ |
559 | #include "soundsourceproxy.h" |
560 | |
561 | CrateFeature::CrateFeature(QObject* parent, |
562 | - TrackCollection* pTrackCollection) |
563 | + TrackCollection* pTrackCollection, ConfigObject<ConfigValue>* pConfig) |
564 | : m_pTrackCollection(pTrackCollection), |
565 | m_crateListTableModel(this, pTrackCollection->getDatabase()), |
566 | + m_pConfig(pConfig), |
567 | m_crateTableModel(this, pTrackCollection) { |
568 | m_pCreateCrateAction = new QAction(tr("New Crate"),this); |
569 | connect(m_pCreateCrateAction, SIGNAL(triggered()), |
570 | @@ -40,9 +41,12 @@ |
571 | connect(m_pLockCrateAction, SIGNAL(triggered()), |
572 | this, SLOT(slotToggleCrateLock())); |
573 | |
574 | - m_pImportPlaylistAction = new QAction(tr("Import Playlist"),this); |
575 | + m_pImportPlaylistAction = new QAction(tr("Import Crate"),this); |
576 | connect(m_pImportPlaylistAction, SIGNAL(triggered()), |
577 | this, SLOT(slotImportPlaylist())); |
578 | + m_pExportPlaylistAction = new QAction(tr("Export Crate"), this); |
579 | + connect(m_pExportPlaylistAction, SIGNAL(triggered()), |
580 | + this, SLOT(slotExportPlaylist())); |
581 | |
582 | m_crateListTableModel.setTable("crates"); |
583 | m_crateListTableModel.setSort(m_crateListTableModel.fieldIndex("name"), |
584 | @@ -175,6 +179,7 @@ |
585 | menu.addAction(m_pLockCrateAction); |
586 | menu.addSeparator(); |
587 | menu.addAction(m_pImportPlaylistAction); |
588 | + menu.addAction(m_pExportPlaylistAction); |
589 | menu.exec(globalPos); |
590 | } |
591 | |
592 | @@ -409,4 +414,42 @@ |
593 | if(playlist_parser) |
594 | delete playlist_parser; |
595 | } |
596 | - |
597 | +void CrateFeature::onLazyChildExpandation(const QModelIndex &index){ |
598 | + //Nothing to do because the childmodel is not of lazy nature. |
599 | +} |
600 | +void CrateFeature::slotExportPlaylist(){ |
601 | + qDebug() << "Export playlist" << m_lastRightClickedIndex.data(); |
602 | + QString file_location = QFileDialog::getSaveFileName(NULL, |
603 | + tr("Export Playlist"), |
604 | + QDesktopServices::storageLocation(QDesktopServices::MusicLocation), |
605 | + tr("M3U Playlist (*.m3u);;PLS Playlist (*.pls)")); |
606 | + //Exit method if user cancelled the open dialog. |
607 | + if(file_location.isNull() || file_location.isEmpty()) return; |
608 | + //create and populate a list of files of the playlist |
609 | + QList<QString> playlist_items; |
610 | + int rows = m_crateTableModel.rowCount(); |
611 | + for(int i = 0; i < rows; ++i){ |
612 | + QModelIndex index = m_crateTableModel.index(i,0); |
613 | + playlist_items << m_crateTableModel.getTrackLocation(index); |
614 | + } |
615 | + //check config if relative paths are desired |
616 | + bool useRelativePath = (bool)m_pConfig->getValueString(ConfigKey("[Library]","UseRelativePathOnExport")).toInt(); |
617 | + |
618 | + if(file_location.endsWith(".m3u", Qt::CaseInsensitive)) |
619 | + { |
620 | + ParserM3u::writeM3UFile(file_location, playlist_items, useRelativePath); |
621 | + } |
622 | + else if(file_location.endsWith(".pls", Qt::CaseInsensitive)) |
623 | + { |
624 | + ParserPls::writePLSFile(file_location,playlist_items, useRelativePath); |
625 | + } |
626 | + else |
627 | + { |
628 | + //default export to M3U if file extension is missing |
629 | + |
630 | + qDebug() << "Crate export: No file extension specified. Appending .m3u " |
631 | + << "and exporting to M3U."; |
632 | + file_location.append(".m3u"); |
633 | + ParserM3u::writeM3UFile(file_location, playlist_items, useRelativePath); |
634 | + } |
635 | +} |
636 | |
637 | === modified file 'mixxx/src/library/cratefeature.h' |
638 | --- mixxx/src/library/cratefeature.h 2011-02-21 07:01:11 +0000 |
639 | +++ mixxx/src/library/cratefeature.h 2011-03-28 22:22:23 +0000 |
640 | @@ -9,13 +9,14 @@ |
641 | #include "library/cratetablemodel.h" |
642 | |
643 | #include "treeitemmodel.h" |
644 | +#include "configobject.h" |
645 | |
646 | class TrackCollection; |
647 | |
648 | class CrateFeature : public LibraryFeature { |
649 | Q_OBJECT |
650 | public: |
651 | - CrateFeature(QObject* parent, TrackCollection* pTrackCollection); |
652 | + CrateFeature(QObject* parent, TrackCollection* pTrackCollection, ConfigObject<ConfigValue>* pConfig); |
653 | virtual ~CrateFeature(); |
654 | |
655 | QVariant title(); |
656 | @@ -39,12 +40,14 @@ |
657 | void activateChild(const QModelIndex& index); |
658 | void onRightClick(const QPoint& globalPos); |
659 | void onRightClickChild(const QPoint& globalPos, QModelIndex index); |
660 | + void onLazyChildExpandation(const QModelIndex& index); |
661 | |
662 | void slotCreateCrate(); |
663 | void slotDeleteCrate(); |
664 | void slotRenameCrate(); |
665 | void slotToggleCrateLock(); |
666 | void slotImportPlaylist(); |
667 | + void slotExportPlaylist(); |
668 | |
669 | private: |
670 | void constructChildModel(); |
671 | @@ -56,10 +59,12 @@ |
672 | QAction *m_pRenameCrateAction; |
673 | QAction *m_pLockCrateAction; |
674 | QAction *m_pImportPlaylistAction; |
675 | + QAction *m_pExportPlaylistAction; |
676 | QSqlTableModel m_crateListTableModel; |
677 | CrateTableModel m_crateTableModel; |
678 | QModelIndex m_lastRightClickedIndex; |
679 | TreeItemModel m_childModel; |
680 | + ConfigObject<ConfigValue>* m_pConfig; |
681 | }; |
682 | |
683 | #endif /* CRATEFEATURE_H */ |
684 | |
685 | === modified file 'mixxx/src/library/itunes/itunesfeature.cpp' |
686 | --- mixxx/src/library/itunes/itunesfeature.cpp 2011-03-24 06:34:54 +0000 |
687 | +++ mixxx/src/library/itunes/itunesfeature.cpp 2011-03-28 22:22:23 +0000 |
688 | @@ -587,4 +587,6 @@ |
689 | emit(featureLoadingFinished(this)); |
690 | activate(); |
691 | } |
692 | - |
693 | +void ITunesFeature::onLazyChildExpandation(const QModelIndex &index){ |
694 | + //Nothing to do because the childmodel is not of lazy nature. |
695 | +} |
696 | |
697 | === modified file 'mixxx/src/library/itunes/itunesfeature.h' |
698 | --- mixxx/src/library/itunes/itunesfeature.h 2011-02-21 23:53:01 +0000 |
699 | +++ mixxx/src/library/itunes/itunesfeature.h 2011-03-28 22:22:23 +0000 |
700 | @@ -43,6 +43,7 @@ |
701 | void activateChild(const QModelIndex& index); |
702 | void onRightClick(const QPoint& globalPos); |
703 | void onRightClickChild(const QPoint& globalPos, QModelIndex index); |
704 | + void onLazyChildExpandation(const QModelIndex& index); |
705 | void onTrackCollectionLoaded(); |
706 | |
707 | private: |
708 | |
709 | === modified file 'mixxx/src/library/library.cpp' |
710 | --- mixxx/src/library/library.cpp 2011-03-27 20:05:54 +0000 |
711 | +++ mixxx/src/library/library.cpp 2011-03-28 22:22:23 +0000 |
712 | @@ -52,9 +52,9 @@ |
713 | } |
714 | |
715 | addFeature(new AutoDJFeature(this, pConfig, m_pTrackCollection)); |
716 | - m_pPlaylistFeature = new PlaylistFeature(this, m_pTrackCollection); |
717 | + m_pPlaylistFeature = new PlaylistFeature(this, m_pTrackCollection, pConfig); |
718 | addFeature(m_pPlaylistFeature); |
719 | - m_pCrateFeature = new CrateFeature(this, m_pTrackCollection); |
720 | + m_pCrateFeature = new CrateFeature(this, m_pTrackCollection, pConfig); |
721 | addFeature(m_pCrateFeature); |
722 | addFeature(new BrowseFeature(this, pConfig, m_pTrackCollection)); |
723 | addFeature(new PrepareFeature(this, pConfig, m_pTrackCollection)); |
724 | @@ -122,7 +122,7 @@ |
725 | m_pSidebarModel, SLOT(clicked(const QModelIndex&))); |
726 | // Lazy model: Let triange symbol increment the model |
727 | connect(pSidebarWidget, SIGNAL(expanded(const QModelIndex&)), |
728 | - m_pSidebarModel, SLOT(clicked(const QModelIndex&))); |
729 | + m_pSidebarModel, SLOT(doubleClicked(const QModelIndex&))); |
730 | |
731 | connect(pSidebarWidget, SIGNAL(rightClicked(const QPoint&, const QModelIndex&)), |
732 | m_pSidebarModel, SLOT(rightClicked(const QPoint&, const QModelIndex&))); |
733 | |
734 | === modified file 'mixxx/src/library/libraryfeature.h' |
735 | --- mixxx/src/library/libraryfeature.h 2011-02-20 11:34:47 +0000 |
736 | +++ mixxx/src/library/libraryfeature.h 2011-03-28 22:22:23 +0000 |
737 | @@ -42,10 +42,19 @@ |
738 | virtual TreeItemModel* getChildModel() = 0; |
739 | |
740 | public slots: |
741 | + /** called when you single click on the root item **/ |
742 | virtual void activate() = 0; |
743 | + /** called when you single click on a child item, e.g., a concrete playlist or crate **/ |
744 | virtual void activateChild(const QModelIndex& index) = 0; |
745 | + /** called when you right click on the root item **/ |
746 | virtual void onRightClick(const QPoint& globalPos) = 0; |
747 | + /** called when you right click on a child item, e.g., a concrete playlist or crate **/ |
748 | virtual void onRightClickChild(const QPoint& globalPos, QModelIndex index) = 0; |
749 | + /* |
750 | + * Only implement this, if using incremental or lazy childmodels, see BrowseFeature. |
751 | + * This method is executed whenever you **double** click child items |
752 | + */ |
753 | + virtual void onLazyChildExpandation(const QModelIndex& index) = 0; |
754 | signals: |
755 | void featureUpdated(); |
756 | void showTrackModel(QAbstractItemModel* model); |
757 | @@ -53,7 +62,9 @@ |
758 | void loadTrack(TrackPointer pTrack); |
759 | void loadTrackToPlayer(TrackPointer pTrack, QString group); |
760 | void restoreSearch(const QString&); |
761 | + /** emit this signal before you parse a large music collection, e.g., iTunes, Traktor. **/ |
762 | void featureIsLoading(LibraryFeature*); |
763 | + /** emit this signal if the foreign music collection has been imported/parsed. **/ |
764 | void featureLoadingFinished(LibraryFeature*s); |
765 | |
766 | |
767 | |
768 | === modified file 'mixxx/src/library/mixxxlibraryfeature.cpp' |
769 | --- mixxx/src/library/mixxxlibraryfeature.cpp 2011-02-19 13:29:31 +0000 |
770 | +++ mixxx/src/library/mixxxlibraryfeature.cpp 2011-03-28 22:22:23 +0000 |
771 | @@ -85,4 +85,6 @@ |
772 | bool MixxxLibraryFeature::dragMoveAcceptChild(const QModelIndex& index, |
773 | QUrl url) { |
774 | return false; |
775 | +}void MixxxLibraryFeature::onLazyChildExpandation(const QModelIndex &index){ |
776 | +//Nothing to do because the childmodel is not of lazy nature. |
777 | } |
778 | |
779 | === modified file 'mixxx/src/library/mixxxlibraryfeature.h' |
780 | --- mixxx/src/library/mixxxlibraryfeature.h 2010-12-23 18:55:54 +0000 |
781 | +++ mixxx/src/library/mixxxlibraryfeature.h 2011-03-28 22:22:23 +0000 |
782 | @@ -35,6 +35,7 @@ |
783 | void activateChild(const QModelIndex& index); |
784 | void onRightClick(const QPoint& globalPos); |
785 | void onRightClickChild(const QPoint& globalPos, QModelIndex index); |
786 | + void onLazyChildExpandation(const QModelIndex& index); |
787 | void refreshLibraryModels(); |
788 | private: |
789 | LibraryTableModel* m_pLibraryTableModel; |
790 | |
791 | === modified file 'mixxx/src/library/parser.cpp' |
792 | --- mixxx/src/library/parser.cpp 2010-12-30 20:44:34 +0000 |
793 | +++ mixxx/src/library/parser.cpp 2011-03-28 22:22:23 +0000 |
794 | @@ -5,12 +5,14 @@ |
795 | // |
796 | // |
797 | // Author: Ingo Kossyk <kossyki@cs.tu-berlin.de>, (C) 2004 |
798 | +// Author: Tobias Rafreider trafreider@mixxx.org, (C) 2011 |
799 | // |
800 | // Copyright: See COPYING file that comes with this distribution |
801 | // |
802 | // |
803 | |
804 | #include <QtDebug> |
805 | +#include <QStringList> |
806 | #include "parser.h" |
807 | |
808 | /** |
809 | @@ -74,3 +76,4 @@ |
810 | file.close(); |
811 | return false; |
812 | } |
813 | + |
814 | |
815 | === modified file 'mixxx/src/library/parser.h' |
816 | --- mixxx/src/library/parser.h 2010-12-30 20:44:34 +0000 |
817 | +++ mixxx/src/library/parser.h 2011-03-28 22:22:23 +0000 |
818 | @@ -5,6 +5,7 @@ |
819 | // |
820 | // |
821 | // Author: Ingo Kossyk <kossyki@cs.tu-berlin.de>, (C) 2004 |
822 | +// Author: Tobias Rafreider trafreider@mixxx.org, (C) 2011 |
823 | // |
824 | // Copyright: See COPYING file that comes with this distribution |
825 | // |
826 | |
827 | === modified file 'mixxx/src/library/parserm3u.cpp' |
828 | --- mixxx/src/library/parserm3u.cpp 2011-01-01 14:33:45 +0000 |
829 | +++ mixxx/src/library/parserm3u.cpp 2011-03-28 22:22:23 +0000 |
830 | @@ -5,12 +5,15 @@ |
831 | // |
832 | // |
833 | // Author: Ingo Kossyk <kossyki@cs.tu-berlin.de>, (C) 2004 |
834 | +// Author: Tobias Rafreider trafreider@mixxx.org, (C) 2011 |
835 | // |
836 | // Copyright: See COPYING file that comes with this distribution |
837 | // |
838 | // |
839 | #include <QTextStream> |
840 | #include <QDebug> |
841 | +#include <QDir> |
842 | +#include <QMessageBox> |
843 | #include "parserm3u.h" |
844 | #include <QUrl> |
845 | |
846 | @@ -120,3 +123,34 @@ |
847 | return 0; |
848 | |
849 | } |
850 | +bool ParserM3u::writeM3UFile(const QString &file_str, QList<QString> &items, bool useRelativePath) |
851 | +{ |
852 | + QFile file(file_str); |
853 | + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)){ |
854 | + QMessageBox::warning(NULL,tr("Playlist Export Failed"), |
855 | + tr("Could not create file")+" "+file_str); |
856 | + return false; |
857 | + } |
858 | + //Base folder of file |
859 | + QString base = file_str.section('/', 0, -2); |
860 | + QDir base_dir(base); |
861 | + |
862 | + qDebug() << "Basepath: " << base; |
863 | + QTextStream out(&file); |
864 | + out << "#EXTM3U\n"; |
865 | + for(int i =0; i < items.size(); ++i){ |
866 | + out << "#EXTINF\n"; |
867 | + |
868 | + //Write relative path if possible |
869 | + if(useRelativePath){ |
870 | + //QDir::relativePath() will return the absolutePath if it cannot compute the |
871 | + //relative Path |
872 | + out << base_dir.relativeFilePath(items.at(i)) << "\n"; |
873 | + } |
874 | + else |
875 | + out << items.at(i) << "\n"; |
876 | + } |
877 | + |
878 | + return true; |
879 | + |
880 | +} |
881 | |
882 | === modified file 'mixxx/src/library/parserm3u.h' |
883 | --- mixxx/src/library/parserm3u.h 2010-12-30 20:44:34 +0000 |
884 | +++ mixxx/src/library/parserm3u.h 2011-03-28 22:22:23 +0000 |
885 | @@ -5,6 +5,7 @@ |
886 | // |
887 | // |
888 | // Author: Ingo Kossyk <kossyki@cs.tu-berlin.de>, (C) 2004 |
889 | +// Author: Tobias Rafreider trafreider@mixxx.org, (C) 2011 |
890 | // |
891 | // Copyright: See COPYING file that comes with this distribution |
892 | // |
893 | @@ -24,6 +25,8 @@ |
894 | ~ParserM3u(); |
895 | /**Overwriting function parse in class Parser**/ |
896 | QList<QString> parse(QString); |
897 | + //Playlist Export |
898 | + static bool writeM3UFile(const QString &file, QList<QString> &items, bool useRelativePath); |
899 | |
900 | private: |
901 | /**Reads a line from the file and returns filepath if a valid file**/ |
902 | |
903 | === modified file 'mixxx/src/library/parserpls.cpp' |
904 | --- mixxx/src/library/parserpls.cpp 2011-01-01 14:33:45 +0000 |
905 | +++ mixxx/src/library/parserpls.cpp 2011-03-28 22:22:23 +0000 |
906 | @@ -5,6 +5,7 @@ |
907 | // |
908 | // |
909 | // Author: Ingo Kossyk <kossyki@cs.tu-berlin.de>, (C) 2004 |
910 | +// Author: Tobias Rafreider trafreider@mixxx.org, (C) 2011 |
911 | // |
912 | // Copyright: See COPYING file that comes with this distribution |
913 | // |
914 | @@ -13,6 +14,8 @@ |
915 | #include "parserpls.h" |
916 | #include <QDebug> |
917 | #include <QTextStream> |
918 | +#include <QMessageBox> |
919 | +#include <QDir> |
920 | #include <QFile> |
921 | #include <QUrl> |
922 | |
923 | @@ -141,3 +144,31 @@ |
924 | return 0; |
925 | |
926 | } |
927 | +bool ParserPls::writePLSFile(QString &file_str, QList<QString> &items, bool useRelativePath) |
928 | +{ |
929 | + QFile file(file_str); |
930 | + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)){ |
931 | + QMessageBox::warning(NULL,tr("Playlist Export Failed"), |
932 | + tr("Could not create file")+" "+file_str); |
933 | + return false; |
934 | + } |
935 | + //Base folder of file |
936 | + QString base = file_str.section('/', 0, -2); |
937 | + QDir base_dir(base); |
938 | + |
939 | + QTextStream out(&file); |
940 | + out << "[playlist]\n"; |
941 | + out << "NumberOfEntries=" << items.size() << "\n"; |
942 | + for(int i =0; i < items.size(); ++i){ |
943 | + //Write relative path if possible |
944 | + if(useRelativePath){ |
945 | + //QDir::relativePath() will return the absolutePath if it cannot compute the |
946 | + //relative Path |
947 | + out << "File" << i << "=" << base_dir.relativeFilePath(items.at(i)) << "\n"; |
948 | + } |
949 | + else |
950 | + out << "File" << i << "=" << items.at(i) << "\n"; |
951 | + } |
952 | + |
953 | + return true; |
954 | +} |
955 | |
956 | === modified file 'mixxx/src/library/parserpls.h' |
957 | --- mixxx/src/library/parserpls.h 2010-12-30 20:44:34 +0000 |
958 | +++ mixxx/src/library/parserpls.h 2011-03-28 22:22:23 +0000 |
959 | @@ -26,6 +26,8 @@ |
960 | ~ParserPls(); |
961 | /**Can be called to parse a pls file**/ |
962 | QList<QString> parse(QString); |
963 | + //Playlist Export |
964 | + static bool writePLSFile(QString &file, QList<QString> &items, bool useRelativePath); |
965 | |
966 | private: |
967 | /**Returns the Number of entries in the pls file**/ |
968 | |
969 | === modified file 'mixxx/src/library/playlistfeature.cpp' |
970 | --- mixxx/src/library/playlistfeature.cpp 2011-03-27 01:36:30 +0000 |
971 | +++ mixxx/src/library/playlistfeature.cpp 2011-03-28 22:22:23 +0000 |
972 | @@ -19,11 +19,12 @@ |
973 | #include "treeitem.h" |
974 | #include "soundsourceproxy.h" |
975 | |
976 | -PlaylistFeature::PlaylistFeature(QObject* parent, TrackCollection* pTrackCollection) |
977 | +PlaylistFeature::PlaylistFeature(QObject* parent, TrackCollection* pTrackCollection, ConfigObject<ConfigValue>* pConfig) |
978 | : LibraryFeature(parent), |
979 | // m_pTrackCollection(pTrackCollection), |
980 | m_playlistDao(pTrackCollection->getPlaylistDAO()), |
981 | m_trackDao(pTrackCollection->getTrackDAO()), |
982 | + m_pConfig(pConfig), |
983 | m_playlistTableModel(this, pTrackCollection->getDatabase()) { |
984 | m_pPlaylistTableModel = new PlaylistTableModel(this, pTrackCollection); |
985 | |
986 | @@ -50,6 +51,9 @@ |
987 | m_pImportPlaylistAction = new QAction(tr("Import Playlist"),this); |
988 | connect(m_pImportPlaylistAction, SIGNAL(triggered()), |
989 | this, SLOT(slotImportPlaylist())); |
990 | + m_pExportPlaylistAction = new QAction(tr("Export Playlist"), this); |
991 | + connect(m_pExportPlaylistAction, SIGNAL(triggered()), |
992 | + this, SLOT(slotExportPlaylist())); |
993 | |
994 | // Setup the sidebar playlist model |
995 | m_playlistTableModel.setTable("Playlists"); |
996 | @@ -140,6 +144,7 @@ |
997 | menu.addAction(m_pLockPlaylistAction); |
998 | menu.addSeparator(); |
999 | menu.addAction(m_pImportPlaylistAction); |
1000 | + menu.addAction(m_pExportPlaylistAction); |
1001 | menu.exec(globalPos); |
1002 | } |
1003 | |
1004 | @@ -426,7 +431,46 @@ |
1005 | //delete the parser object |
1006 | if(playlist_parser) delete playlist_parser; |
1007 | } |
1008 | - |
1009 | +void PlaylistFeature::onLazyChildExpandation(const QModelIndex &index){ |
1010 | + //Nothing to do because the childmodel is not of lazy nature. |
1011 | +} |
1012 | +void PlaylistFeature::slotExportPlaylist(){ |
1013 | + qDebug() << "Export playlist" << m_lastRightClickedIndex.data(); |
1014 | + QString file_location = QFileDialog::getSaveFileName(NULL, |
1015 | + tr("Export Playlist"), |
1016 | + QDesktopServices::storageLocation(QDesktopServices::MusicLocation), |
1017 | + tr("M3U Playlist (*.m3u);;PLS Playlist (*.pls)")); |
1018 | + //Exit method if user cancelled the open dialog. |
1019 | + if(file_location.isNull() || file_location.isEmpty()) return; |
1020 | + //create and populate a list of files of the playlist |
1021 | + QList<QString> playlist_items; |
1022 | + int rows = m_pPlaylistTableModel->rowCount(); |
1023 | + for(int i = 0; i < rows; ++i){ |
1024 | + QModelIndex index = m_pPlaylistTableModel->index(i,0); |
1025 | + playlist_items << m_pPlaylistTableModel->getTrackLocation(index); |
1026 | + } |
1027 | + //check config if relative paths are desired |
1028 | + bool useRelativePath = (bool)m_pConfig->getValueString(ConfigKey("[Library]","UseRelativePathOnExport")).toInt(); |
1029 | + |
1030 | + if(file_location.endsWith(".m3u", Qt::CaseInsensitive)) |
1031 | + { |
1032 | + ParserM3u::writeM3UFile(file_location, playlist_items, useRelativePath); |
1033 | + } |
1034 | + else if(file_location.endsWith(".pls", Qt::CaseInsensitive)) |
1035 | + { |
1036 | + ParserPls::writePLSFile(file_location,playlist_items, useRelativePath); |
1037 | + } |
1038 | + else |
1039 | + { |
1040 | + //default export to M3U if file extension is missing |
1041 | + |
1042 | + qDebug() << "Playlist export: No file extension specified. Appending .m3u " |
1043 | + << "and exporting to M3U."; |
1044 | + file_location.append(".m3u"); |
1045 | + ParserM3u::writeM3UFile(file_location, playlist_items, useRelativePath); |
1046 | + } |
1047 | + |
1048 | +} |
1049 | void PlaylistFeature::slotAddToAutoDJ() { |
1050 | //qDebug() << "slotAddToAutoDJ() row:" << m_lastRightClickedIndex.data(); |
1051 | |
1052 | |
1053 | === modified file 'mixxx/src/library/playlistfeature.h' |
1054 | --- mixxx/src/library/playlistfeature.h 2011-03-26 07:47:01 +0000 |
1055 | +++ mixxx/src/library/playlistfeature.h 2011-03-28 22:22:23 +0000 |
1056 | @@ -12,6 +12,7 @@ |
1057 | #include "library/dao/playlistdao.h" |
1058 | #include "library/dao/trackdao.h" |
1059 | #include "treeitemmodel.h" |
1060 | +#include "configobject.h" |
1061 | |
1062 | |
1063 | class PlaylistTableModel; |
1064 | @@ -20,7 +21,7 @@ |
1065 | class PlaylistFeature : public LibraryFeature { |
1066 | Q_OBJECT |
1067 | public: |
1068 | - PlaylistFeature(QObject* parent, TrackCollection* pTrackCollection); |
1069 | + PlaylistFeature(QObject* parent, TrackCollection* pTrackCollection, ConfigObject<ConfigValue>* pConfig); |
1070 | virtual ~PlaylistFeature(); |
1071 | |
1072 | QVariant title(); |
1073 | @@ -44,6 +45,7 @@ |
1074 | void activateChild(const QModelIndex& index); |
1075 | void onRightClick(const QPoint& globalPos); |
1076 | void onRightClickChild(const QPoint& globalPos, QModelIndex index); |
1077 | + void onLazyChildExpandation(const QModelIndex& index); |
1078 | |
1079 | void slotCreatePlaylist(); |
1080 | void slotDeletePlaylist(); |
1081 | @@ -51,6 +53,7 @@ |
1082 | void slotRenamePlaylist(); |
1083 | void slotTogglePlaylistLock(); |
1084 | void slotImportPlaylist(); |
1085 | + void slotExportPlaylist(); |
1086 | |
1087 | |
1088 | private: |
1089 | @@ -66,9 +69,11 @@ |
1090 | QAction *m_pRenamePlaylistAction; |
1091 | QAction *m_pLockPlaylistAction; |
1092 | QAction *m_pImportPlaylistAction; |
1093 | + QAction *m_pExportPlaylistAction; |
1094 | QSqlTableModel m_playlistTableModel; |
1095 | QModelIndex m_lastRightClickedIndex; |
1096 | TreeItemModel m_childModel; |
1097 | + ConfigObject<ConfigValue>* m_pConfig; |
1098 | }; |
1099 | |
1100 | #endif /* PLAYLISTFEATURE_H */ |
1101 | |
1102 | === modified file 'mixxx/src/library/preparefeature.cpp' |
1103 | --- mixxx/src/library/preparefeature.cpp 2011-03-26 07:45:47 +0000 |
1104 | +++ mixxx/src/library/preparefeature.cpp 2011-03-28 22:22:23 +0000 |
1105 | @@ -104,6 +104,10 @@ |
1106 | return false; |
1107 | } |
1108 | |
1109 | +void PrepareFeature::onLazyChildExpandation(const QModelIndex &index){ |
1110 | + //Nothing to do because the childmodel is not of lazy nature. |
1111 | +} |
1112 | + |
1113 | void PrepareFeature::analyzeTracks(QList<int> trackIds) { |
1114 | if (m_pAnalyserQueue == NULL) { |
1115 | //Save the old BPM detection prefs setting (on or off) |
1116 | @@ -155,4 +159,5 @@ |
1117 | //Restore old BPM detection setting for preferences... |
1118 | m_pConfig->set(ConfigKey("[BPM]","BPMDetectionEnabled"), ConfigValue(m_iOldBpmEnabled)); |
1119 | } |
1120 | + |
1121 | } |
1122 | |
1123 | === modified file 'mixxx/src/library/preparefeature.h' |
1124 | --- mixxx/src/library/preparefeature.h 2011-03-26 07:45:47 +0000 |
1125 | +++ mixxx/src/library/preparefeature.h 2011-03-28 22:22:23 +0000 |
1126 | @@ -46,6 +46,7 @@ |
1127 | void activateChild(const QModelIndex& index); |
1128 | void onRightClick(const QPoint& globalPos); |
1129 | void onRightClickChild(const QPoint& globalPos, QModelIndex index); |
1130 | + void onLazyChildExpandation(const QModelIndex& index); |
1131 | |
1132 | private slots: |
1133 | void analyzeTracks(QList<int> trackIds); |
1134 | |
1135 | === modified file 'mixxx/src/library/promotracksfeature.cpp' |
1136 | --- mixxx/src/library/promotracksfeature.cpp 2011-01-09 17:53:56 +0000 |
1137 | +++ mixxx/src/library/promotracksfeature.cpp 2011-03-28 22:22:23 +0000 |
1138 | @@ -205,3 +205,6 @@ |
1139 | QUrl url) { |
1140 | return false; |
1141 | } |
1142 | +void PromoTracksFeature::onLazyChildExpandation(const QModelIndex &index){ |
1143 | + //Nothing to do because the childmodel is not of lazy nature. |
1144 | +} |
1145 | |
1146 | === modified file 'mixxx/src/library/promotracksfeature.h' |
1147 | --- mixxx/src/library/promotracksfeature.h 2010-12-23 18:55:54 +0000 |
1148 | +++ mixxx/src/library/promotracksfeature.h 2011-03-28 22:22:23 +0000 |
1149 | @@ -66,6 +66,7 @@ |
1150 | void activateChild(const QModelIndex& index); |
1151 | void onRightClick(const QPoint& globalPos); |
1152 | void onRightClickChild(const QPoint& globalPos, QModelIndex index); |
1153 | + void onLazyChildExpandation(const QModelIndex& index); |
1154 | |
1155 | private: |
1156 | ConfigObject<ConfigValue>* m_pConfig; |
1157 | |
1158 | === modified file 'mixxx/src/library/rhythmbox/rhythmboxfeature.cpp' |
1159 | --- mixxx/src/library/rhythmbox/rhythmboxfeature.cpp 2011-02-21 23:53:01 +0000 |
1160 | +++ mixxx/src/library/rhythmbox/rhythmboxfeature.cpp 2011-03-28 22:22:23 +0000 |
1161 | @@ -419,5 +419,7 @@ |
1162 | emit(featureLoadingFinished(this)); |
1163 | activate(); |
1164 | } |
1165 | - |
1166 | +void RhythmboxFeature::onLazyChildExpandation(const QModelIndex &index){ |
1167 | + //Nothing to do because the childmodel is not of lazy nature. |
1168 | +} |
1169 | |
1170 | |
1171 | === modified file 'mixxx/src/library/rhythmbox/rhythmboxfeature.h' |
1172 | --- mixxx/src/library/rhythmbox/rhythmboxfeature.h 2011-02-21 23:53:01 +0000 |
1173 | +++ mixxx/src/library/rhythmbox/rhythmboxfeature.h 2011-03-28 22:22:23 +0000 |
1174 | @@ -66,6 +66,7 @@ |
1175 | void activateChild(const QModelIndex& index); |
1176 | void onRightClick(const QPoint& globalPos); |
1177 | void onRightClickChild(const QPoint& globalPos, QModelIndex index); |
1178 | + void onLazyChildExpandation(const QModelIndex& index); |
1179 | void onTrackCollectionLoaded(); |
1180 | |
1181 | }; |
1182 | |
1183 | === modified file 'mixxx/src/library/sidebarmodel.cpp' |
1184 | --- mixxx/src/library/sidebarmodel.cpp 2011-03-27 20:05:54 +0000 |
1185 | +++ mixxx/src/library/sidebarmodel.cpp 2011-03-28 22:22:23 +0000 |
1186 | @@ -153,16 +153,18 @@ |
1187 | if (parent.isValid()) { |
1188 | if (parent.internalPointer() == this) { |
1189 | return QAbstractItemModel::hasChildren(parent); |
1190 | - } else { |
1191 | + } |
1192 | + else |
1193 | + { |
1194 | TreeItem* tree_item = (TreeItem*)parent.internalPointer(); |
1195 | if (tree_item) { |
1196 | LibraryFeature* feature = tree_item->getFeature(); |
1197 | return feature->getChildModel()->hasChildren(parent); |
1198 | } |
1199 | } |
1200 | - } else { |
1201 | - return QAbstractItemModel::hasChildren(parent); |
1202 | } |
1203 | + |
1204 | + return QAbstractItemModel::hasChildren(parent); |
1205 | } |
1206 | |
1207 | QVariant SidebarModel::data(const QModelIndex& index, int role) const { |
1208 | @@ -211,6 +213,19 @@ |
1209 | } |
1210 | } |
1211 | } |
1212 | +void SidebarModel::doubleClicked(const QModelIndex& index) { |
1213 | + if (index.isValid()) { |
1214 | + if (index.internalPointer() == this) { |
1215 | + return; |
1216 | + } else { |
1217 | + TreeItem* tree_item = (TreeItem*)index.internalPointer(); |
1218 | + if (tree_item) { |
1219 | + LibraryFeature* feature = tree_item->getFeature(); |
1220 | + feature->onLazyChildExpandation(index); |
1221 | + } |
1222 | + } |
1223 | + } |
1224 | +} |
1225 | |
1226 | void SidebarModel::rightClicked(const QPoint& globalPos, const QModelIndex& index) { |
1227 | //qDebug() << "SidebarModel::rightClicked() index=" << index; |
1228 | |
1229 | === modified file 'mixxx/src/library/sidebarmodel.h' |
1230 | --- mixxx/src/library/sidebarmodel.h 2011-03-27 20:05:54 +0000 |
1231 | +++ mixxx/src/library/sidebarmodel.h 2011-03-28 22:22:23 +0000 |
1232 | @@ -36,6 +36,7 @@ |
1233 | |
1234 | public slots: |
1235 | void clicked(const QModelIndex& index); |
1236 | + void doubleClicked(const QModelIndex& index); |
1237 | void rightClicked(const QPoint& globalPos, const QModelIndex& index); |
1238 | void refreshData(); |
1239 | void selectFeature(LibraryFeature* pFeature); |
1240 | |
1241 | === modified file 'mixxx/src/library/traktor/traktorfeature.cpp' |
1242 | --- mixxx/src/library/traktor/traktorfeature.cpp 2011-03-24 06:34:54 +0000 |
1243 | +++ mixxx/src/library/traktor/traktorfeature.cpp 2011-03-28 22:22:23 +0000 |
1244 | @@ -615,3 +615,6 @@ |
1245 | emit(featureLoadingFinished(this)); |
1246 | activate(); |
1247 | } |
1248 | +void TraktorFeature::onLazyChildExpandation(const QModelIndex &index){ |
1249 | + //Nothing to do because the childmodel is not of lazy nature. |
1250 | +} |
1251 | |
1252 | === modified file 'mixxx/src/library/traktor/traktorfeature.h' |
1253 | --- mixxx/src/library/traktor/traktorfeature.h 2011-02-21 23:53:01 +0000 |
1254 | +++ mixxx/src/library/traktor/traktorfeature.h 2011-03-28 22:22:23 +0000 |
1255 | @@ -42,6 +42,7 @@ |
1256 | void activateChild(const QModelIndex& index); |
1257 | void onRightClick(const QPoint& globalPos); |
1258 | void onRightClickChild(const QPoint& globalPos, QModelIndex index); |
1259 | + void onLazyChildExpandation(const QModelIndex& index); |
1260 | void refreshLibraryModels(); |
1261 | void onTrackCollectionLoaded(); |
1262 | private: |
I have tweaked the new Browser a bit. The sidebar now distinguishes between single and double clicks, see comment of commit r2658.