1 /*
2 * FileBrowser.cpp - implementation of the project-, preset- and
3 * sample-file-browser
4 *
5 * Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
6 *
7 * This file is part of LMMS - https://lmms.io
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public
20 * License along with this program (see COPYING); if not, write to the
21 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301 USA.
23 *
24 */
25
26
27 #include <QHBoxLayout>
28 #include <QKeyEvent>
29 #include <QLineEdit>
30 #include <QMenu>
31 #include <QPushButton>
32 #include <QMdiArea>
33 #include <QMdiSubWindow>
34 #include <QMessageBox>
35 #include <QShortcut>
36
37 #include "FileBrowser.h"
38 #include "BBTrackContainer.h"
39 #include "ConfigManager.h"
40 #include "embed.h"
41 #include "Engine.h"
42 #include "GuiApplication.h"
43 #include "gui_templates.h"
44 #include "ImportFilter.h"
45 #include "Instrument.h"
46 #include "InstrumentTrack.h"
47 #include "MainWindow.h"
48 #include "Mixer.h"
49 #include "PluginFactory.h"
50 #include "PresetPreviewPlayHandle.h"
51 #include "SamplePlayHandle.h"
52 #include "Song.h"
53 #include "StringPairDrag.h"
54 #include "TextFloat.h"
55
56
57
58 enum TreeWidgetItemTypes
59 {
60 TypeFileItem = QTreeWidgetItem::UserType,
61 TypeDirectoryItem
62 } ;
63
64
65
FileBrowser(const QString & directories,const QString & filter,const QString & title,const QPixmap & pm,QWidget * parent,bool dirs_as_items,bool recurse)66 FileBrowser::FileBrowser(const QString & directories, const QString & filter,
67 const QString & title, const QPixmap & pm,
68 QWidget * parent, bool dirs_as_items, bool recurse ) :
69 SideBarWidget( title, pm, parent ),
70 m_directories( directories ),
71 m_filter( filter ),
72 m_dirsAsItems( dirs_as_items ),
73 m_recurse( recurse )
74 {
75 setWindowTitle( tr( "Browser" ) );
76 m_l = new FileBrowserTreeWidget( contentParent() );
77 addContentWidget( m_l );
78
79 QWidget * ops = new QWidget( contentParent() );
80 ops->setFixedHeight( 24 );
81
82 QHBoxLayout * opl = new QHBoxLayout( ops );
83 opl->setMargin( 0 );
84 opl->setSpacing( 0 );
85
86 m_filterEdit = new QLineEdit( ops );
87 #if QT_VERSION >= 0x050000
88 m_filterEdit->setClearButtonEnabled( true );
89 #endif
90 connect( m_filterEdit, SIGNAL( textEdited( const QString & ) ),
91 this, SLOT( filterItems( const QString & ) ) );
92
93 QPushButton * reload_btn = new QPushButton(
94 embed::getIconPixmap( "reload" ),
95 QString::null, ops );
96 connect( reload_btn, SIGNAL( clicked() ), this, SLOT( reloadTree() ) );
97
98 opl->addWidget( m_filterEdit );
99 opl->addSpacing( 5 );
100 opl->addWidget( reload_btn );
101
102 addContentWidget( ops );
103
104 // Whenever the FileBrowser has focus, Ctrl+F should direct focus to its filter box.
105 QShortcut *filterFocusShortcut = new QShortcut( QKeySequence( QKeySequence::Find ), this, SLOT(giveFocusToFilter()) );
106 filterFocusShortcut->setContext(Qt::WidgetWithChildrenShortcut);
107
108 reloadTree();
109 show();
110 }
111
112
113
114
~FileBrowser()115 FileBrowser::~FileBrowser()
116 {
117 }
118
119
120
121
filterItems(const QString & filter,QTreeWidgetItem * item)122 bool FileBrowser::filterItems( const QString & filter, QTreeWidgetItem * item )
123 {
124 // call with item=NULL to filter the entire tree
125 bool anyMatched = false;
126
127 int numChildren = item ? item->childCount() : m_l->topLevelItemCount();
128 for( int i = 0; i < numChildren; ++i )
129 {
130 QTreeWidgetItem * it = item ? item->child( i ) : m_l->topLevelItem(i);
131
132 // is directory?
133 if( it->childCount() )
134 {
135 // matches filter?
136 if( it->text( 0 ).
137 contains( filter, Qt::CaseInsensitive ) )
138 {
139 // yes, then show everything below
140 it->setHidden( false );
141 filterItems( QString::null, it );
142 anyMatched = true;
143 }
144 else
145 {
146 // only show if item below matches filter
147 bool didMatch = filterItems( filter, it );
148 it->setHidden( !didMatch );
149 anyMatched = anyMatched || didMatch;
150 }
151 }
152 // a standard item (i.e. no file or directory item?)
153 else if( it->type() == QTreeWidgetItem::Type )
154 {
155 // hide if there's any filter
156 it->setHidden( !filter.isEmpty() );
157 }
158 else
159 {
160 // file matches filter?
161 bool didMatch = it->text( 0 ).
162 contains( filter, Qt::CaseInsensitive );
163 it->setHidden( !didMatch );
164 anyMatched = anyMatched || didMatch;
165 }
166 }
167
168 return anyMatched;
169 }
170
171
172
reloadTree(void)173 void FileBrowser::reloadTree( void )
174 {
175 const QString text = m_filterEdit->text();
176 m_filterEdit->clear();
177 m_l->clear();
178 QStringList paths = m_directories.split( '*' );
179 for( QStringList::iterator it = paths.begin(); it != paths.end(); ++it )
180 {
181 addItems( *it );
182 }
183 expandItems();
184 m_filterEdit->setText( text );
185 filterItems( text );
186 }
187
188
189
expandItems(QTreeWidgetItem * item)190 void FileBrowser::expandItems( QTreeWidgetItem * item )
191 {
192 int numChildren = item ? item->childCount() : m_l->topLevelItemCount();
193 for( int i = 0; i < numChildren; ++i )
194 {
195 QTreeWidgetItem * it = item ? item->child( i ) : m_l->topLevelItem(i);
196 if ( m_recurse )
197 {
198 it->setExpanded( true );
199 }
200 Directory *d = dynamic_cast<Directory *> ( it );
201 if( d )
202 {
203 d->update();
204 d->setExpanded( false );
205 }
206 if( m_recurse && it->childCount() )
207 {
208 expandItems(it);
209 }
210 }
211 }
212
213
214
giveFocusToFilter()215 void FileBrowser::giveFocusToFilter()
216 {
217 if (!m_filterEdit->hasFocus())
218 {
219 // give focus to filter text box and highlight its text for quick editing if not previously focused
220 m_filterEdit->setFocus();
221 m_filterEdit->selectAll();
222 }
223 }
224
225
226
addItems(const QString & path)227 void FileBrowser::addItems(const QString & path )
228 {
229 if( m_dirsAsItems )
230 {
231 m_l->addTopLevelItem( new Directory( path, QString::null, m_filter ) );
232 return;
233 }
234
235 // try to add all directories from file system alphabetically into the tree
236 QDir cdir( path );
237 QStringList files = cdir.entryList( QDir::Dirs, QDir::Name );
238 for( QStringList::const_iterator it = files.constBegin();
239 it != files.constEnd(); ++it )
240 {
241 QString cur_file = *it;
242 if( cur_file[0] != '.' )
243 {
244 bool orphan = true;
245 for( int i = 0; i < m_l->topLevelItemCount(); ++i )
246 {
247 Directory * d = dynamic_cast<Directory *>(
248 m_l->topLevelItem( i ) );
249 if( d == NULL || cur_file < d->text( 0 ) )
250 {
251 // insert before item, we're done
252 Directory *dd = new Directory( cur_file, path,
253 m_filter );
254 m_l->insertTopLevelItem( i,dd );
255 dd->update();
256 orphan = false;
257 break;
258 }
259 else if( cur_file == d->text( 0 ) )
260 {
261 // imagine we have subdirs named "TripleOscillator/xyz" in
262 // two directories from m_directories
263 // then only add one tree widget for both
264 // so we don't add a new Directory - we just
265 // add the path to the current directory
266 d->addDirectory( path );
267 d->update();
268 orphan = false;
269 break;
270 }
271 }
272 if( orphan )
273 {
274 // it has not yet been added yet, so it's (lexically)
275 // larger than all other dirs => append it at the bottom
276 Directory *d = new Directory( cur_file,
277 path, m_filter );
278 d->update();
279 m_l->addTopLevelItem( d );
280 }
281 }
282 }
283
284 files = cdir.entryList( QDir::Files, QDir::Name );
285 for( QStringList::const_iterator it = files.constBegin();
286 it != files.constEnd(); ++it )
287 {
288 QString cur_file = *it;
289 if( cur_file[0] != '.' )
290 {
291 // TODO: don't insert instead of removing, order changed
292 // remove existing file-items
293 QList<QTreeWidgetItem *> existing = m_l->findItems(
294 cur_file, Qt::MatchFixedString );
295 if( !existing.empty() )
296 {
297 delete existing.front();
298 }
299 (void) new FileItem( m_l, cur_file, path );
300 }
301 }
302 }
303
304
305
306
keyPressEvent(QKeyEvent * ke)307 void FileBrowser::keyPressEvent(QKeyEvent * ke )
308 {
309 if( ke->key() == Qt::Key_F5 )
310 {
311 reloadTree();
312 }
313 else
314 {
315 ke->ignore();
316 }
317 }
318
319
320
321
322
323
324
325
FileBrowserTreeWidget(QWidget * parent)326 FileBrowserTreeWidget::FileBrowserTreeWidget(QWidget * parent ) :
327 QTreeWidget( parent ),
328 m_mousePressed( false ),
329 m_pressPos(),
330 m_previewPlayHandle( NULL ),
331 m_pphMutex( QMutex::Recursive ),
332 m_contextMenuItem( NULL )
333 {
334 setColumnCount( 1 );
335 headerItem()->setHidden( true );
336 setSortingEnabled( false );
337
338 connect( this, SIGNAL( itemDoubleClicked( QTreeWidgetItem *, int ) ),
339 SLOT( activateListItem( QTreeWidgetItem *, int ) ) );
340 connect( this, SIGNAL( itemCollapsed( QTreeWidgetItem * ) ),
341 SLOT( updateDirectory( QTreeWidgetItem * ) ) );
342 connect( this, SIGNAL( itemExpanded( QTreeWidgetItem * ) ),
343 SLOT( updateDirectory( QTreeWidgetItem * ) ) );
344
345 }
346
347
348
349
~FileBrowserTreeWidget()350 FileBrowserTreeWidget::~FileBrowserTreeWidget()
351 {
352 }
353
354
355
356
contextMenuEvent(QContextMenuEvent * e)357 void FileBrowserTreeWidget::contextMenuEvent(QContextMenuEvent * e )
358 {
359 FileItem * f = dynamic_cast<FileItem *>( itemAt( e->pos() ) );
360 if( f != NULL && ( f->handling() == FileItem::LoadAsPreset ||
361 f->handling() == FileItem::LoadByPlugin ) )
362 {
363 m_contextMenuItem = f;
364 QMenu contextMenu( this );
365 contextMenu.addAction( tr( "Send to active instrument-track" ),
366 this,
367 SLOT( sendToActiveInstrumentTrack() ) );
368 contextMenu.addAction( tr( "Open in new instrument-track/"
369 "Song Editor" ),
370 this,
371 SLOT( openInNewInstrumentTrackSE() ) );
372 contextMenu.addAction( tr( "Open in new instrument-track/"
373 "B+B Editor" ),
374 this,
375 SLOT( openInNewInstrumentTrackBBE() ) );
376 contextMenu.exec( e->globalPos() );
377 m_contextMenuItem = NULL;
378 }
379 }
380
381
382
383
mousePressEvent(QMouseEvent * me)384 void FileBrowserTreeWidget::mousePressEvent(QMouseEvent * me )
385 {
386 QTreeWidget::mousePressEvent( me );
387 if( me->button() != Qt::LeftButton )
388 {
389 return;
390 }
391
392 QTreeWidgetItem * i = itemAt( me->pos() );
393 if ( i )
394 {
395 // TODO: Restrict to visible selection
396 // if ( _me->x() > header()->cellPos( header()->mapToActual( 0 ) )
397 // + treeStepSize() * ( i->depth() + ( rootIsDecorated() ?
398 // 1 : 0 ) ) + itemMargin() ||
399 // _me->x() < header()->cellPos(
400 // header()->mapToActual( 0 ) ) )
401 // {
402 m_pressPos = me->pos();
403 m_mousePressed = true;
404 // }
405 }
406
407 FileItem * f = dynamic_cast<FileItem *>( i );
408 if( f != NULL )
409 {
410 m_pphMutex.lock();
411 if( m_previewPlayHandle != NULL )
412 {
413 Engine::mixer()->removePlayHandle(
414 m_previewPlayHandle );
415 m_previewPlayHandle = NULL;
416 }
417
418 // in special case of sample-files we do not care about
419 // handling() rather than directly creating a SamplePlayHandle
420 if( f->type() == FileItem::SampleFile )
421 {
422 TextFloat * tf = TextFloat::displayMessage(
423 tr( "Loading sample" ),
424 tr( "Please wait, loading sample for "
425 "preview..." ),
426 embed::getIconPixmap( "sample_file",
427 24, 24 ), 0 );
428 qApp->processEvents(
429 QEventLoop::ExcludeUserInputEvents );
430 SamplePlayHandle * s = new SamplePlayHandle(
431 f->fullName() );
432 s->setDoneMayReturnTrue( false );
433 m_previewPlayHandle = s;
434 delete tf;
435 }
436 else if( ( f->extension ()== "xiz" || f->extension() == "sf2" || f->extension() == "gig" || f->extension() == "pat" ) &&
437 ! pluginFactory->pluginSupportingExtension(f->extension()).isNull() )
438 {
439 m_previewPlayHandle = new PresetPreviewPlayHandle( f->fullName(), f->handling() == FileItem::LoadByPlugin );
440 }
441 else if( f->type() != FileItem::VstPluginFile &&
442 ( f->handling() == FileItem::LoadAsPreset ||
443 f->handling() == FileItem::LoadByPlugin ) )
444 {
445 DataFile dataFile( f->fullName() );
446 if( !dataFile.validate( f->extension() ) )
447 {
448 QMessageBox::warning( 0, tr ( "Error" ),
449 f->fullName() + " " + tr( "does not appear to be a valid" ) + " " + f->extension() +
450 " " + tr( "file" ),
451 QMessageBox::Ok, QMessageBox::NoButton );
452 m_pphMutex.unlock();
453 return;
454 }
455 m_previewPlayHandle = new PresetPreviewPlayHandle( f->fullName(), f->handling() == FileItem::LoadByPlugin, &dataFile );
456 }
457 if( m_previewPlayHandle != NULL )
458 {
459 if( !Engine::mixer()->addPlayHandle(
460 m_previewPlayHandle ) )
461 {
462 m_previewPlayHandle = NULL;
463 }
464 }
465 m_pphMutex.unlock();
466 }
467 }
468
469
470
471
mouseMoveEvent(QMouseEvent * me)472 void FileBrowserTreeWidget::mouseMoveEvent( QMouseEvent * me )
473 {
474 if( m_mousePressed == true &&
475 ( m_pressPos - me->pos() ).manhattanLength() >
476 QApplication::startDragDistance() )
477 {
478 // make sure any playback is stopped
479 mouseReleaseEvent( NULL );
480
481 FileItem * f = dynamic_cast<FileItem *>( itemAt( m_pressPos ) );
482 if( f != NULL )
483 {
484 switch( f->type() )
485 {
486 case FileItem::PresetFile:
487 new StringPairDrag( f->handling() == FileItem::LoadAsPreset ?
488 "presetfile" : "pluginpresetfile",
489 f->fullName(),
490 embed::getIconPixmap( "preset_file" ), this );
491 break;
492
493 case FileItem::SampleFile:
494 new StringPairDrag( "samplefile", f->fullName(),
495 embed::getIconPixmap( "sample_file" ), this );
496 break;
497 case FileItem::SoundFontFile:
498 new StringPairDrag( "soundfontfile", f->fullName(),
499 embed::getIconPixmap( "soundfont_file" ), this );
500 break;
501 case FileItem::PatchFile:
502 new StringPairDrag( "patchfile", f->fullName(),
503 embed::getIconPixmap( "sample_file" ), this );
504 break;
505 case FileItem::VstPluginFile:
506 new StringPairDrag( "vstpluginfile", f->fullName(),
507 embed::getIconPixmap( "vst_plugin_file" ), this );
508 break;
509 case FileItem::MidiFile:
510 new StringPairDrag( "importedproject", f->fullName(),
511 embed::getIconPixmap( "midi_file" ), this );
512 break;
513 case FileItem::ProjectFile:
514 new StringPairDrag( "projectfile", f->fullName(),
515 embed::getIconPixmap( "project_file" ), this );
516 break;
517
518 default:
519 break;
520 }
521 }
522 }
523 }
524
525
526
527
mouseReleaseEvent(QMouseEvent * me)528 void FileBrowserTreeWidget::mouseReleaseEvent(QMouseEvent * me )
529 {
530 m_mousePressed = false;
531
532 m_pphMutex.lock();
533 if( m_previewPlayHandle != NULL )
534 {
535 // if there're samples shorter than 3 seconds, we don't
536 // stop them if the user releases mouse-button...
537 if( m_previewPlayHandle->type() == PlayHandle::TypeSamplePlayHandle )
538 {
539 SamplePlayHandle * s = dynamic_cast<SamplePlayHandle *>(
540 m_previewPlayHandle );
541 if( s && s->totalFrames() - s->framesDone() <=
542 static_cast<f_cnt_t>( Engine::mixer()->
543 processingSampleRate() * 3 ) )
544 {
545 s->setDoneMayReturnTrue( true );
546 m_previewPlayHandle = NULL;
547 m_pphMutex.unlock();
548 return;
549 }
550 }
551 Engine::mixer()->removePlayHandle( m_previewPlayHandle );
552 m_previewPlayHandle = NULL;
553 }
554 m_pphMutex.unlock();
555 }
556
557
558
559
560
handleFile(FileItem * f,InstrumentTrack * it)561 void FileBrowserTreeWidget::handleFile(FileItem * f, InstrumentTrack * it )
562 {
563 Engine::mixer()->requestChangeInModel();
564 switch( f->handling() )
565 {
566 case FileItem::LoadAsProject:
567 if( gui->mainWindow()->mayChangeProject(true) )
568 {
569 Engine::getSong()->loadProject( f->fullName() );
570 }
571 break;
572
573 case FileItem::LoadByPlugin:
574 {
575 const QString e = f->extension();
576 Instrument * i = it->instrument();
577 if( i == NULL ||
578 !i->descriptor()->supportsFileType( e ) )
579 {
580 i = it->loadInstrument(
581 pluginFactory->pluginSupportingExtension(e).name() );
582 }
583 i->loadFile( f->fullName() );
584 break;
585 }
586
587 case FileItem::LoadAsPreset:
588 {
589 DataFile dataFile( f->fullName() );
590 InstrumentTrack::removeMidiPortNode( dataFile );
591 it->setSimpleSerializing();
592 it->loadSettings( dataFile.content().toElement() );
593 break;
594 }
595
596 case FileItem::ImportAsProject:
597 ImportFilter::import( f->fullName(),
598 Engine::getSong() );
599 break;
600
601 case FileItem::NotSupported:
602 default:
603 break;
604
605 }
606 Engine::mixer()->doneChangeInModel();
607 }
608
609
610
611
activateListItem(QTreeWidgetItem * item,int column)612 void FileBrowserTreeWidget::activateListItem(QTreeWidgetItem * item,
613 int column )
614 {
615 FileItem * f = dynamic_cast<FileItem *>( item );
616 if( f == NULL )
617 {
618 return;
619 }
620
621 if( f->handling() == FileItem::LoadAsProject ||
622 f->handling() == FileItem::ImportAsProject )
623 {
624 handleFile( f, NULL );
625 }
626 else if( f->handling() != FileItem::NotSupported )
627 {
628 InstrumentTrack * it = dynamic_cast<InstrumentTrack *>(
629 Track::create( Track::InstrumentTrack,
630 Engine::getBBTrackContainer() ) );
631 handleFile( f, it );
632 }
633 }
634
635
636
637
openInNewInstrumentTrack(TrackContainer * tc)638 void FileBrowserTreeWidget::openInNewInstrumentTrack( TrackContainer* tc )
639 {
640 if( m_contextMenuItem->handling() == FileItem::LoadAsPreset ||
641 m_contextMenuItem->handling() == FileItem::LoadByPlugin )
642 {
643 InstrumentTrack * it = dynamic_cast<InstrumentTrack *>(
644 Track::create( Track::InstrumentTrack, tc ) );
645 handleFile( m_contextMenuItem, it );
646 }
647 }
648
649
650
651
openInNewInstrumentTrackBBE(void)652 void FileBrowserTreeWidget::openInNewInstrumentTrackBBE( void )
653 {
654 openInNewInstrumentTrack( Engine::getBBTrackContainer() );
655 }
656
657
658
659
openInNewInstrumentTrackSE(void)660 void FileBrowserTreeWidget::openInNewInstrumentTrackSE( void )
661 {
662 openInNewInstrumentTrack( Engine::getSong() );
663 }
664
665
666
667
sendToActiveInstrumentTrack(void)668 void FileBrowserTreeWidget::sendToActiveInstrumentTrack( void )
669 {
670 // get all windows opened in the workspace
671 QList<QMdiSubWindow*> pl =
672 gui->mainWindow()->workspace()->
673 subWindowList( QMdiArea::StackingOrder );
674 QListIterator<QMdiSubWindow *> w( pl );
675 w.toBack();
676 // now we travel through the window-list until we find an
677 // instrument-track
678 while( w.hasPrevious() )
679 {
680 InstrumentTrackWindow * itw =
681 dynamic_cast<InstrumentTrackWindow *>(
682 w.previous()->widget() );
683 if( itw != NULL && itw->isHidden() == false )
684 {
685 handleFile( m_contextMenuItem, itw->model() );
686 break;
687 }
688 }
689 }
690
691
692
693
updateDirectory(QTreeWidgetItem * item)694 void FileBrowserTreeWidget::updateDirectory(QTreeWidgetItem * item )
695 {
696 Directory * dir = dynamic_cast<Directory *>( item );
697 if( dir != NULL )
698 {
699 dir->update();
700 }
701 }
702
703
704
705
706
707
708 QPixmap * Directory::s_folderPixmap = NULL;
709 QPixmap * Directory::s_folderOpenedPixmap = NULL;
710 QPixmap * Directory::s_folderLockedPixmap = NULL;
711
712
Directory(const QString & filename,const QString & path,const QString & filter)713 Directory::Directory(const QString & filename, const QString & path,
714 const QString & filter ) :
715 QTreeWidgetItem( QStringList( filename ), TypeDirectoryItem ),
716 m_directories( path ),
717 m_filter( filter ),
718 m_dirCount( 0 )
719 {
720 initPixmaps();
721
722 setChildIndicatorPolicy( QTreeWidgetItem::ShowIndicator );
723
724 if( !QDir( fullName() ).isReadable() )
725 {
726 setIcon( 0, *s_folderLockedPixmap );
727 }
728 else
729 {
730 setIcon( 0, *s_folderPixmap );
731 }
732 }
733
734
735
736
initPixmaps(void)737 void Directory::initPixmaps( void )
738 {
739 if( s_folderPixmap == NULL )
740 {
741 s_folderPixmap = new QPixmap(
742 embed::getIconPixmap( "folder" ) );
743 }
744
745 if( s_folderOpenedPixmap == NULL )
746 {
747 s_folderOpenedPixmap = new QPixmap(
748 embed::getIconPixmap( "folder_opened" ) );
749 }
750
751 if( s_folderLockedPixmap == NULL )
752 {
753 s_folderLockedPixmap = new QPixmap(
754 embed::getIconPixmap( "folder_locked" ) );
755 }
756 }
757
758
759
760
update(void)761 void Directory::update( void )
762 {
763 if( !isExpanded() )
764 {
765 setIcon( 0, *s_folderPixmap );
766 return;
767 }
768
769 setIcon( 0, *s_folderOpenedPixmap );
770 if( !childCount() )
771 {
772 m_dirCount = 0;
773 // for all paths leading here, add their items
774 for( QStringList::iterator it = m_directories.begin();
775 it != m_directories.end(); ++it )
776 {
777 int filesBeforeAdd = childCount() - m_dirCount;
778 if( addItems( fullName( *it ) ) &&
779 ( *it ).contains(
780 ConfigManager::inst()->dataDir() ) )
781 {
782 // factory file directory is added
783 // note: those are always added last
784 int filesNow = childCount() - m_dirCount;
785 if(filesNow > filesBeforeAdd) // any file appended?
786 {
787 QTreeWidgetItem * sep = new QTreeWidgetItem;
788 sep->setText( 0,
789 FileBrowserTreeWidget::tr(
790 "--- Factory files ---" ) );
791 sep->setIcon( 0, embed::getIconPixmap(
792 "factory_files" ) );
793 // add delimeter after last file before appending our files
794 insertChild( filesBeforeAdd + m_dirCount, sep );
795 }
796 }
797 }
798 }
799 }
800
801
802
803
addItems(const QString & path)804 bool Directory::addItems(const QString & path )
805 {
806 QDir thisDir( path );
807 if( !thisDir.isReadable() )
808 {
809 return false;
810 }
811
812 treeWidget()->setUpdatesEnabled( false );
813
814 bool added_something = false;
815
816 // try to add all directories from file system alphabetically into the tree
817 QStringList files = thisDir.entryList( QDir::Dirs, QDir::Name );
818 for( QStringList::const_iterator it = files.constBegin();
819 it != files.constEnd(); ++it )
820 {
821 QString cur_file = *it;
822 if( cur_file[0] != '.' )
823 {
824 bool orphan = true;
825 for( int i = 0; i < childCount(); ++i )
826 {
827 Directory * d = dynamic_cast<Directory *>(
828 child( i ) );
829 if( d == NULL || cur_file < d->text( 0 ) )
830 {
831 // insert before item, we're done
832 insertChild( i, new Directory( cur_file,
833 path, m_filter ) );
834 orphan = false;
835 m_dirCount++;
836 break;
837 }
838 else if( cur_file == d->text( 0 ) )
839 {
840 // imagine we have top-level subdirs named "TripleOscillator" in
841 // two directories from FileBrowser::m_directories
842 // and imagine both have a sub folder named "xyz"
843 // then only add one tree widget for both
844 // so we don't add a new Directory - we just
845 // add the path to the current directory
846 d->addDirectory( path );
847 orphan = false;
848 break;
849 }
850 }
851 if( orphan )
852 {
853 // it has not yet been added yet, so it's (lexically)
854 // larger than all other dirs => append it at the bottom
855 addChild( new Directory( cur_file, path,
856 m_filter ) );
857 m_dirCount++;
858 }
859
860 added_something = true;
861 }
862 }
863
864 QList<QTreeWidgetItem*> items;
865 files = thisDir.entryList( QDir::Files, QDir::Name );
866 for( QStringList::const_iterator it = files.constBegin();
867 it != files.constEnd(); ++it )
868 {
869 QString cur_file = *it;
870 if( cur_file[0] != '.' &&
871 thisDir.match( m_filter, cur_file.toLower() ) )
872 {
873 items << new FileItem( cur_file, path );
874 added_something = true;
875 }
876 }
877 addChildren( items );
878
879 treeWidget()->setUpdatesEnabled( true );
880
881 return added_something;
882 }
883
884
885
886
887 QPixmap * FileItem::s_projectFilePixmap = NULL;
888 QPixmap * FileItem::s_presetFilePixmap = NULL;
889 QPixmap * FileItem::s_sampleFilePixmap = NULL;
890 QPixmap * FileItem::s_soundfontFilePixmap = NULL;
891 QPixmap * FileItem::s_vstPluginFilePixmap = NULL;
892 QPixmap * FileItem::s_midiFilePixmap = NULL;
893 QPixmap * FileItem::s_unknownFilePixmap = NULL;
894
895
FileItem(QTreeWidget * parent,const QString & name,const QString & path)896 FileItem::FileItem(QTreeWidget * parent, const QString & name,
897 const QString & path ) :
898 QTreeWidgetItem( parent, QStringList( name) , TypeFileItem ),
899 m_path( path )
900 {
901 determineFileType();
902 initPixmaps();
903 }
904
905
906
907
FileItem(const QString & name,const QString & path)908 FileItem::FileItem(const QString & name, const QString & path ) :
909 QTreeWidgetItem( QStringList( name ), TypeFileItem ),
910 m_path( path )
911 {
912 determineFileType();
913 initPixmaps();
914 }
915
916
917
918
initPixmaps(void)919 void FileItem::initPixmaps( void )
920 {
921 if( s_projectFilePixmap == NULL )
922 {
923 s_projectFilePixmap = new QPixmap( embed::getIconPixmap(
924 "project_file", 16, 16 ) );
925 }
926
927 if( s_presetFilePixmap == NULL )
928 {
929 s_presetFilePixmap = new QPixmap( embed::getIconPixmap(
930 "preset_file", 16, 16 ) );
931 }
932
933 if( s_sampleFilePixmap == NULL )
934 {
935 s_sampleFilePixmap = new QPixmap( embed::getIconPixmap(
936 "sample_file", 16, 16 ) );
937 }
938
939 if ( s_soundfontFilePixmap == NULL )
940 {
941 s_soundfontFilePixmap = new QPixmap( embed::getIconPixmap(
942 "soundfont_file", 16, 16 ) );
943 }
944
945 if ( s_vstPluginFilePixmap == NULL )
946 {
947 s_vstPluginFilePixmap = new QPixmap( embed::getIconPixmap(
948 "vst_plugin_file", 16, 16 ) );
949 }
950
951 if( s_midiFilePixmap == NULL )
952 {
953 s_midiFilePixmap = new QPixmap( embed::getIconPixmap(
954 "midi_file", 16, 16 ) );
955 }
956
957 if( s_unknownFilePixmap == NULL )
958 {
959 s_unknownFilePixmap = new QPixmap( embed::getIconPixmap(
960 "unknown_file" ) );
961 }
962
963 switch( m_type )
964 {
965 case ProjectFile:
966 setIcon( 0, *s_projectFilePixmap );
967 break;
968 case PresetFile:
969 setIcon( 0, *s_presetFilePixmap );
970 break;
971 case SoundFontFile:
972 setIcon( 0, *s_soundfontFilePixmap );
973 break;
974 case VstPluginFile:
975 setIcon( 0, *s_vstPluginFilePixmap );
976 break;
977 case SampleFile:
978 case PatchFile: // TODO
979 setIcon( 0, *s_sampleFilePixmap );
980 break;
981 case MidiFile:
982 setIcon( 0, *s_midiFilePixmap );
983 break;
984 case UnknownFile:
985 default:
986 setIcon( 0, *s_unknownFilePixmap );
987 break;
988 }
989 }
990
991
992
993
determineFileType(void)994 void FileItem::determineFileType( void )
995 {
996 m_handling = NotSupported;
997
998 const QString ext = extension();
999 if( ext == "mmp" || ext == "mpt" || ext == "mmpz" )
1000 {
1001 m_type = ProjectFile;
1002 m_handling = LoadAsProject;
1003 }
1004 else if( ext == "xpf" || ext == "xml" )
1005 {
1006 m_type = PresetFile;
1007 m_handling = LoadAsPreset;
1008 }
1009 else if( ext == "xiz" && ! pluginFactory->pluginSupportingExtension(ext).isNull() )
1010 {
1011 m_type = PresetFile;
1012 m_handling = LoadByPlugin;
1013 }
1014 else if( ext == "sf2" )
1015 {
1016 m_type = SoundFontFile;
1017 }
1018 else if( ext == "pat" )
1019 {
1020 m_type = PatchFile;
1021 }
1022 else if( ext == "mid" )
1023 {
1024 m_type = MidiFile;
1025 m_handling = ImportAsProject;
1026 }
1027 else if( ext == "dll" )
1028 {
1029 m_type = VstPluginFile;
1030 m_handling = LoadByPlugin;
1031 }
1032 else
1033 {
1034 m_type = UnknownFile;
1035 }
1036
1037 if( m_handling == NotSupported &&
1038 !ext.isEmpty() && ! pluginFactory->pluginSupportingExtension(ext).isNull() )
1039 {
1040 m_handling = LoadByPlugin;
1041 // classify as sample if not classified by anything yet but can
1042 // be handled by a certain plugin
1043 if( m_type == UnknownFile )
1044 {
1045 m_type = SampleFile;
1046 }
1047 }
1048 }
1049
1050
1051
1052
extension(void)1053 QString FileItem::extension( void )
1054 {
1055 return extension( fullName() );
1056 }
1057
1058
1059
1060
extension(const QString & file)1061 QString FileItem::extension(const QString & file )
1062 {
1063 return QFileInfo( file ).suffix().toLower();
1064 }
1065
1066
1067
1068
1069