diff --git a/src/apps/mediaplayer/Controller.cpp b/src/apps/mediaplayer/Controller.cpp index 7182bf8a8f..7fbe1f3327 100644 --- a/src/apps/mediaplayer/Controller.cpp +++ b/src/apps/mediaplayer/Controller.cpp @@ -23,11 +23,10 @@ #include #include #include -#include -#include -#include -#include + #include +#include +#include #include #include @@ -87,6 +86,7 @@ Controller::Controller() , fStopped(true) , fVolume(1.0) + , fRef() , fMediaFile(0) // TODO: remove, InfoWin depends on it but should be fixed ,fAudioTrack(0) @@ -201,6 +201,11 @@ Controller::SetTo(const entry_ref &ref) BAutolock al(fVideoSupplierLock); BAutolock vl(fAudioSupplierLock); + if (fRef == ref) + return B_OK; + + fRef = ref; + fAudioTrackList->MakeEmpty(); fVideoTrackList->MakeEmpty(); if (fMediaFile) @@ -466,6 +471,18 @@ Controller::Pause() } +void +Controller::TogglePlaying() +{ + BAutolock _(fDataLock); + + if (IsPaused() || IsStopped()) + Play(); + else + Pause(); +} + + bool Controller::IsPaused() const { diff --git a/src/apps/mediaplayer/Controller.h b/src/apps/mediaplayer/Controller.h index 03aaecf8c1..bbcb3f605f 100644 --- a/src/apps/mediaplayer/Controller.h +++ b/src/apps/mediaplayer/Controller.h @@ -21,6 +21,7 @@ #ifndef __CONTROLLER_H #define __CONTROLLER_H +#include #include #include #include @@ -76,6 +77,7 @@ public: void Stop(); void Play(); void Pause(); + void TogglePlaying(); bool IsPaused() const; bool IsStopped() const; @@ -158,6 +160,7 @@ private: volatile bool fStopped; float fVolume; + entry_ref fRef; BMediaFile * fMediaFile; BMediaTrack * fAudioTrack; BMediaTrack * fVideoTrack; diff --git a/src/apps/mediaplayer/ControllerView.cpp b/src/apps/mediaplayer/ControllerView.cpp index c492fd596f..fa4cdf2eb9 100644 --- a/src/apps/mediaplayer/ControllerView.cpp +++ b/src/apps/mediaplayer/ControllerView.cpp @@ -20,6 +20,7 @@ */ #include "ControllerView.h" +#include #include #include #include @@ -91,18 +92,13 @@ ControllerView::EnabledButtons() void ControllerView::TogglePlaying() { - printf("ControllerView::TogglePlaying()\n"); - if (fController->IsPaused() || fController->IsStopped()) - fController->Play(); - else - fController->Pause(); + fController->TogglePlaying(); } void ControllerView::Stop() { - printf("ControllerView::Stop()\n"); fController->Stop(); } @@ -126,7 +122,7 @@ ControllerView::Forward() void ControllerView::SkipBackward() { - printf("ControllerView::SkipBackward()\n"); + BAutolock _(fPlaylist); fPlaylist->SetCurrentRefIndex(fPlaylist->CurrentRefIndex() - 1); } @@ -134,7 +130,7 @@ ControllerView::SkipBackward() void ControllerView::SkipForward() { - printf("ControllerView::SkipForward()\n"); + BAutolock _(fPlaylist); fPlaylist->SetCurrentRefIndex(fPlaylist->CurrentRefIndex() + 1); } @@ -142,7 +138,6 @@ ControllerView::SkipForward() void ControllerView::VolumeChanged(float value) { - printf("ControllerView::VolumeChanged(%.4f)\n", value); fController->SetVolume(value); } @@ -157,7 +152,6 @@ ControllerView::ToggleMute() void ControllerView::PositionChanged(float value) { - printf("ControllerView::PositionChanged(%.2f)\n", value); // 0.0 ... 1.0 fController->SetPosition(value); } @@ -169,6 +163,8 @@ ControllerView::PositionChanged(float value) void ControllerView::CheckSkippable() { + BAutolock _(fPlaylist); + bool canSkipNext, canSkipPrevious; fPlaylist->GetSkipInfo(&canSkipPrevious, &canSkipNext); SetSkippable(canSkipPrevious, canSkipNext); diff --git a/src/apps/mediaplayer/Jamfile b/src/apps/mediaplayer/Jamfile index e472ddc185..6757b0ba0a 100644 --- a/src/apps/mediaplayer/Jamfile +++ b/src/apps/mediaplayer/Jamfile @@ -32,6 +32,7 @@ Application MediaPlayer : MainApp.cpp MainWin.cpp Playlist.cpp + PlaylistListView.cpp PlaylistObserver.cpp PlaylistWindow.cpp SoundOutput.cpp diff --git a/src/apps/mediaplayer/ListViews.cpp b/src/apps/mediaplayer/ListViews.cpp index 513e69aedd..882da4245c 100644 --- a/src/apps/mediaplayer/ListViews.cpp +++ b/src/apps/mediaplayer/ListViews.cpp @@ -320,21 +320,20 @@ DragSortableListView::MessageReceived(BMessage* message) { if (AcceptDragMessage(message)) { DragSortableListView *list = NULL; - if ( message->FindPointer( "list", (void **)&list ) == B_OK - && list == this ) { + if (message->FindPointer("list", (void **)&list) == B_OK + && list == this) { int32 count = CountItems(); - if ( fDropIndex < 0 || fDropIndex > count ) + if (fDropIndex < 0 || fDropIndex > count) fDropIndex = count; - BList items; + BList indices; int32 index; - for ( int32 i = 0; message->FindInt32( "index", i, &index ) == B_OK; i++ ) - if ( BListItem* item = ItemAt(index) ) - items.AddItem( (void*)item ); - if ( items.CountItems() > 0 ) { - if ( modifiers() & B_SHIFT_KEY ) - CopyItems( items, fDropIndex ); + for (int32 i = 0; message->FindInt32("index", i, &index) == B_OK; i++) + indices.AddItem((void*)index); + if (indices.CountItems() > 0) { + if (modifiers() & B_SHIFT_KEY) + CopyItems(indices, fDropIndex); else - MoveItems( items, fDropIndex ); + MoveItems(indices, fDropIndex); } fDropIndex = -1; } @@ -371,9 +370,9 @@ DragSortableListView::MessageReceived(BMessage* message) } break; } -// case B_MODIFIERS_CHANGED: -// ModifiersChanged(); -// break; + case B_MODIFIERS_CHANGED: + ModifiersChanged(); + break; case B_MOUSE_WHEEL_CHANGED: { BListView::MessageReceived( message ); BPoint point; @@ -625,42 +624,39 @@ DragSortableListView::ScrollTo(int32 index) // MoveItems void -DragSortableListView::MoveItems( BList& items, int32 index ) +DragSortableListView::MoveItems(BList& indices, int32 index) { DeselectAll(); // we remove the items while we look at them, the insertion index is decreased // when the items index is lower, so that we insert at the right spot after // removal BList removedItems; - int32 count = items.CountItems(); - for ( int32 i = 0; i < count; i++ ) - { - BListItem* item = (BListItem*)items.ItemAt( i ); - int32 removeIndex = IndexOf( item ); - if ( RemoveItem( item ) && removedItems.AddItem( (void*)item ) ) - { - if ( removeIndex < index ) + int32 count = indices.CountItems(); + for (int32 i = 0; i < count; i++) { + int32 removeIndex = (int32)indices.ItemAtFast(i) - i; + BListItem* item = RemoveItem(removeIndex); + if (item && removedItems.AddItem((void*)item)) { + if (removeIndex < index) index--; } // else ??? -> blow up } - for ( int32 i = 0; BListItem* item = (BListItem*)removedItems.ItemAt( i ); i++ ) - { - if ( AddItem( item, index ) ) - { + count = removedItems.CountItems(); + for (int32 i = 0; i < count; i++) { + BListItem* item = (BListItem*)removedItems.ItemAtFast(i); + if (AddItem(item, index)) { // after we're done, the newly inserted items will be selected - Select( index, true ); + Select(index, true); // next items will be inserted after this one index++; - } - else + } else delete item; } } // CopyItems void -DragSortableListView::CopyItems( BList& items, int32 index ) +DragSortableListView::CopyItems(BList& indices, int32 toIndex) { DeselectAll(); // by inserting the items after we copied all items first, we avoid @@ -668,37 +664,34 @@ DragSortableListView::CopyItems( BList& items, int32 index ) // in other words, don't touch the list before we know which items // need to be cloned BList clonedItems; - int32 count = items.CountItems(); - for ( int32 i = 0; i < count; i++ ) - { - BListItem* item = CloneItem( IndexOf( (BListItem*)items.ItemAt( i ) ) ); - if ( item && !clonedItems.AddItem( (void*)item ) ) + int32 count = indices.CountItems(); + for (int32 i = 0; i < count; i++) { + int32 index = (int32)indices.ItemAtFast(i); + BListItem* item = CloneItem(index); + if (item && !clonedItems.AddItem((void*)item)) delete item; } - for ( int32 i = 0; BListItem* item = (BListItem*)clonedItems.ItemAt( i ); i++ ) - { - if ( AddItem( item, index ) ) - { + count = clonedItems.CountItems(); + for (int32 i = 0; i < count; i++) { + BListItem* item = (BListItem*)clonedItems.ItemAtFast(i); + if (AddItem(item, toIndex)) { // after we're done, the newly inserted items will be selected - Select( index, true ); + Select(toIndex, true); // next items will be inserted after this one - index++; - } - else + toIndex++; + } else delete item; } } // RemoveItemList void -DragSortableListView::RemoveItemList( BList& items ) +DragSortableListView::RemoveItemList(BList& indices) { - int32 count = items.CountItems(); - for ( int32 i = 0; i < count; i++ ) - { - BListItem* item = (BListItem*)items.ItemAt( i ); - if ( RemoveItem( item ) ) - delete item; + int32 count = indices.CountItems(); + for (int32 i = 0; i < count; i++) { + int32 index = (int32)indices.ItemAtFast(i) - i; + delete RemoveItem(index); } } @@ -706,13 +699,19 @@ DragSortableListView::RemoveItemList( BList& items ) void DragSortableListView::RemoveSelected() { -// if (fFocusedIndex >= 0) -// return; + BList indices; + for (int32 i = 0; true; i++) { + int32 index = CurrentSelection(i); + if (index < 0) + break; + if (!indices.AddItem((void*)index)) + break; + } - BList items; - for ( int32 i = 0; BListItem* item = ItemAt( CurrentSelection( i ) ); i++ ) - items.AddItem( (void*)item ); - RemoveItemList( items ); + DeselectAll(); + + if (indices.CountItems() > 0) + RemoveItemList(indices); } // CountSelectedItems @@ -720,7 +719,7 @@ int32 DragSortableListView::CountSelectedItems() const { int32 count = 0; - while ( CurrentSelection( count ) >= 0 ) + while (CurrentSelection(count) >= 0) count++; return count; } diff --git a/src/apps/mediaplayer/ListViews.h b/src/apps/mediaplayer/ListViews.h index b78bc84b18..5a9994852f 100644 --- a/src/apps/mediaplayer/ListViews.h +++ b/src/apps/mediaplayer/ListViews.h @@ -89,8 +89,8 @@ class DragSortableListView : public BListView { bool MouseWheelChanged(float x, float y); - virtual void MoveItems(BList& items, int32 toIndex); - virtual void CopyItems(BList& items, int32 toIndex); + virtual void MoveItems(BList& indices, int32 toIndex); + virtual void CopyItems(BList& indices, int32 toIndex); virtual void RemoveItemList(BList& indices); void RemoveSelected(); // uses RemoveItemList() int32 CountSelectedItems() const; diff --git a/src/apps/mediaplayer/MainWin.cpp b/src/apps/mediaplayer/MainWin.cpp index e6b5fe2a11..20be25950c 100644 --- a/src/apps/mediaplayer/MainWin.cpp +++ b/src/apps/mediaplayer/MainWin.cpp @@ -20,25 +20,27 @@ */ #include "MainWin.h" -#include -#include +#include +#include +#include + +#include +#include +#include +#include #include #include #include -#include -#include -#include -#include #include #include +#include #include -#include -#include +#include #include "ControllerObserver.h" +#include "MainApp.h" #include "PlaylistObserver.h" #include "PlaylistWindow.h" -#include "MainApp.h" #define NAME "MediaPlayer" #define MIN_WIDTH 250 @@ -317,7 +319,6 @@ MainWin::MessageReceived(BMessage *msg) // PlaylistObserver messages case MSG_PLAYLIST_REF_ADDED: { -printf("MSG_PLAYLIST_REF_ADDED\n"); entry_ref ref; int32 index; if (msg->FindRef("refs", &ref) == B_OK @@ -327,7 +328,6 @@ printf("MSG_PLAYLIST_REF_ADDED\n"); break; } case MSG_PLAYLIST_REF_REMOVED: { -printf("MSG_PLAYLIST_REF_REMOVED\n"); int32 index; if (msg->FindInt32("index", &index) == B_OK) { _RemovePlaylistItem(index); @@ -335,12 +335,12 @@ printf("MSG_PLAYLIST_REF_REMOVED\n"); break; } case MSG_PLAYLIST_CURRENT_REF_CHANGED: { -printf("MSG_PLAYLIST_CURRENT_REF_CHANGED\n"); + BAutolock _(fPlaylist); + int32 index; if (msg->FindInt32("index", &index) < B_OK || index != fPlaylist->CurrentRefIndex()) break; -printf(" index: %ld\n", index); entry_ref ref; if (fPlaylist->GetRefAt(index, &ref) == B_OK) { printf("open ref: %s\n", ref.name); @@ -352,7 +352,6 @@ printf(" index: %ld\n", index); // ControllerObserver messages case MSG_CONTROLLER_FILE_FINISHED: -printf("MSG_CONTROLLER_FILE_FINISHED\n"); fPlaylist->SetCurrentRefIndex(fPlaylist->CurrentRefIndex() + 1); break; case MSG_CONTROLLER_FILE_CHANGED: @@ -686,7 +685,7 @@ MainWin::ShowPlaylistWindow() { if (!fPlaylistWindow) { fPlaylistWindow = new PlaylistWindow(BRect(150, 150, 400, 500), - fPlaylist); + fPlaylist, fController); fPlaylistWindow->Show(); return; } @@ -731,34 +730,13 @@ MainWin::VideoFormatChange(int width, int height, float width_scale, float heigh void MainWin::_RefsReceived(BMessage *msg) { - printf("MainWin::_RefsReceived\n"); - // the playlist ist replaced by dropped files // or the dropped files are appended to the end // of the existing playlist if is pressed - bool add = modifiers() & B_SHIFT_KEY; + int32 appendIndex = modifiers() & B_SHIFT_KEY ? + fPlaylist->CountItems() : -1; - if (!add) - fPlaylist->MakeEmpty(); - - bool startPlaying = fPlaylist->CountItems() == 0; - - Playlist temporaryPlaylist; - Playlist* playlist = add ? &temporaryPlaylist : fPlaylist; - - entry_ref ref; - for (int i = 0; B_OK == msg->FindRef("refs", i, &ref); i++) - _AppendToPlaylist(ref, playlist); - - playlist->Sort(); - - if (add) - fPlaylist->AdoptPlaylist(temporaryPlaylist); - - if (startPlaying) { - // open first file - fPlaylist->SetCurrentRefIndex(0); - } + fPlaylist->AppendRefs(msg, appendIndex); } @@ -1414,9 +1392,10 @@ MainWin::_UpdateControlsEnabledStatus() void MainWin::_UpdatePlaylistMenu() { - fPlaylistMenu->RemoveItems(0, fPlaylistMenu->CountItems(), true); + if (!fPlaylist->Lock()) + return; - // TODO: lock fPlaylist + fPlaylistMenu->RemoveItems(0, fPlaylistMenu->CountItems(), true); int32 count = fPlaylist->CountItems(); for (int32 i = 0; i < count; i++) { @@ -1428,6 +1407,8 @@ MainWin::_UpdatePlaylistMenu() fPlaylistMenu->SetTargetForItems(this); _MarkPlaylistItem(fPlaylist->CurrentRefIndex()); + + fPlaylist->Unlock(); } @@ -1462,25 +1443,3 @@ MainWin::_MarkPlaylistItem(int32 index) } -void -MainWin::_AppendToPlaylist(const entry_ref& ref, Playlist* playlist) -{ - // recursively append the ref (dive into folders) - BEntry entry(&ref, true); - if (entry.InitCheck() < B_OK) - return; - if (!entry.Exists()) - return; - if (entry.IsDirectory()) { - BDirectory dir(&entry); - if (dir.InitCheck() < B_OK) - return; - entry.Unset(); - entry_ref subRef; - while (dir.GetNextRef(&subRef) == B_OK) - _AppendToPlaylist(subRef, playlist); - } else if (entry.IsFile()) { - playlist->AddRef(ref); - } -} - diff --git a/src/apps/mediaplayer/MainWin.h b/src/apps/mediaplayer/MainWin.h index f404f2dde0..5929689a90 100644 --- a/src/apps/mediaplayer/MainWin.h +++ b/src/apps/mediaplayer/MainWin.h @@ -91,8 +91,6 @@ private: int32 index); void _RemovePlaylistItem(int32 index); void _MarkPlaylistItem(int32 index); - void _AppendToPlaylist(const entry_ref& ref, - Playlist* playlist); BMenuBar* fMenuBar; BView* fBackground; diff --git a/src/apps/mediaplayer/Playlist.cpp b/src/apps/mediaplayer/Playlist.cpp index 68c6bcd191..1552beb588 100644 --- a/src/apps/mediaplayer/Playlist.cpp +++ b/src/apps/mediaplayer/Playlist.cpp @@ -25,6 +25,9 @@ #include #include +#include +#include +#include #include using std::nothrow; @@ -43,7 +46,8 @@ void Playlist::Listener::CurrentRefChanged(int32 newIndex) {} Playlist::Playlist() - : fRefs() + : BLocker("playlist lock") + , fRefs() , fCurrentIndex(-1) { } @@ -104,6 +108,10 @@ Playlist::AddRef(const entry_ref &ref, int32 index) return false; } _NotifyRefAdded(ref, index); + + if (index <= fCurrentIndex) + SetCurrentRefIndex(fCurrentIndex + 1); + return true; } @@ -129,6 +137,8 @@ Playlist::AdoptPlaylist(Playlist& other, int32 index) entry_ref* ref = (entry_ref*)fRefs.ItemAtFast(i); _NotifyRefAdded(*ref, i); } + if (index <= fCurrentIndex) + SetCurrentRefIndex(fCurrentIndex + count); // empty the other list, so that the entry_refs are no ours other.fRefs.MakeEmpty(); return true; @@ -138,7 +148,7 @@ Playlist::AdoptPlaylist(Playlist& other, int32 index) entry_ref -Playlist::RemoveRef(int32 index) +Playlist::RemoveRef(int32 index, bool careAboutCurrentIndex) { entry_ref _ref; entry_ref* ref = (entry_ref*)fRefs.RemoveItem(index); @@ -147,21 +157,29 @@ Playlist::RemoveRef(int32 index) _NotifyRefRemoved(index); _ref = *ref; delete ref; + + if (careAboutCurrentIndex) { + if (index == fCurrentIndex) + SetCurrentRefIndex(-1); + else if (index < fCurrentIndex) + SetCurrentRefIndex(fCurrentIndex - 1); + } + return _ref; } -//int32 -//Playlist::IndexOf(const entry_ref& _ref) const -//{ -// int32 count = CountItems(); -// for (int32 i = 0; i < count; i++) { -// entry_ref* ref = (entry_ref*)fRefs.ItemAtFast(i); -// if (*ref == _ref) -// return i; -// } -// return -1; -//} +int32 +Playlist::IndexOf(const entry_ref& _ref) const +{ + int32 count = CountItems(); + for (int32 i = 0; i < count; i++) { + entry_ref* ref = (entry_ref*)fRefs.ItemAtFast(i); + if (*ref == _ref) + return i; + } + return -1; +} status_t @@ -223,6 +241,7 @@ Playlist::GetSkipInfo(bool* canSkipPrevious, bool* canSkipNext) const bool Playlist::AddListener(Listener* listener) { + BAutolock _(this); if (listener && !fListeners.HasItem(listener)) return fListeners.AddItem(listener); return false; @@ -232,6 +251,7 @@ Playlist::AddListener(Listener* listener) void Playlist::RemoveListener(Listener* listener) { + BAutolock _(this); fListeners.RemoveItem(listener); } @@ -239,6 +259,64 @@ Playlist::RemoveListener(Listener* listener) // #pragma mark - +void +Playlist::AppendRefs(BMessage* refsReceivedMessage, int32 appendIndex) +{ + // the playlist ist replaced by the refs in the message + // or the refs are appended at the appendIndex + // in the existing playlist + bool add = appendIndex >= 0; + + if (!add) + MakeEmpty(); + + bool startPlaying = CountItems() == 0; + + Playlist temporaryPlaylist; + Playlist* playlist = add ? &temporaryPlaylist : this; + + entry_ref ref; + for (int i = 0; refsReceivedMessage->FindRef("refs", i, &ref) == B_OK; i++) + AppendToPlaylistRecursive(ref, playlist); + + playlist->Sort(); + + if (add) + AdoptPlaylist(temporaryPlaylist, appendIndex); + + if (startPlaying) { + // open first file + SetCurrentRefIndex(0); + } +} + + +/*static*/ void +Playlist::AppendToPlaylistRecursive(const entry_ref& ref, Playlist* playlist) +{ + // recursively append the ref (dive into folders) + BEntry entry(&ref, true); + if (entry.InitCheck() < B_OK) + return; + if (!entry.Exists()) + return; + if (entry.IsDirectory()) { + BDirectory dir(&entry); + if (dir.InitCheck() < B_OK) + return; + entry.Unset(); + entry_ref subRef; + while (dir.GetNextRef(&subRef) == B_OK) + AppendToPlaylistRecursive(subRef, playlist); + } else if (entry.IsFile()) { + playlist->AddRef(ref); + } +} + + +// #pragma mark - + + int Playlist::playlist_cmp(const void *p1, const void *p2) { diff --git a/src/apps/mediaplayer/Playlist.h b/src/apps/mediaplayer/Playlist.h index aff634b637..5ca176629b 100644 --- a/src/apps/mediaplayer/Playlist.h +++ b/src/apps/mediaplayer/Playlist.h @@ -23,8 +23,9 @@ #include #include +#include -class Playlist { +class Playlist : public BLocker { public: class Listener { public: @@ -40,51 +41,58 @@ public: }; public: - Playlist(); - ~Playlist(); - - // list functionality - void MakeEmpty(); - int32 CountItems() const; - - void Sort(); + Playlist(); + ~Playlist(); + + // list functionality + void MakeEmpty(); + int32 CountItems() const; + + void Sort(); + + bool AddRef(const entry_ref& ref); + bool AddRef(const entry_ref& ref, int32 index); + entry_ref RemoveRef(int32 index, + bool careAboutCurrentIndex = true); + + bool AdoptPlaylist(Playlist& other); + bool AdoptPlaylist(Playlist& other, int32 index); + + int32 IndexOf(const entry_ref& ref) const; + status_t GetRefAt(int32 index, entry_ref* ref) const; + // bool HasRef(const entry_ref& ref) const; + + // navigating current ref + void SetCurrentRefIndex(int32 index); + int32 CurrentRefIndex() const; + + void GetSkipInfo(bool* canSkipPrevious, + bool* canSkipNext) const; - bool AddRef(const entry_ref& ref); - bool AddRef(const entry_ref& ref, int32 index); - entry_ref RemoveRef(int32 index); + // listener support + bool AddListener(Listener* listener); + void RemoveListener(Listener* listener); - bool AdoptPlaylist(Playlist& other); - bool AdoptPlaylist(Playlist& other, int32 index); - -// int32 IndexOf(const entry_ref& ref) const; - status_t GetRefAt(int32 index, entry_ref* ref) const; -// bool HasRef(const entry_ref& ref) const; - - // navigating current ref - void SetCurrentRefIndex(int32 index); - int32 CurrentRefIndex() const; - - void GetSkipInfo(bool* canSkipPrevious, - bool* canSkipNext) const; - - // listener support - bool AddListener(Listener* listener); - void RemoveListener(Listener* listener); + // support functions + void AppendRefs(BMessage* refsReceivedMessage, + int32 appendIndex = -1); + static void AppendToPlaylistRecursive(const entry_ref& ref, + Playlist* playlist); private: - static int playlist_cmp(const void* p1, const void* p2); + static int playlist_cmp(const void* p1, const void* p2); - void _NotifyRefAdded(const entry_ref& ref, - int32 index) const; - void _NotifyRefRemoved(int32 index) const; - void _NotifyRefsSorted() const; - void _NotifyCurrentRefChanged(int32 newIndex) const; + void _NotifyRefAdded(const entry_ref& ref, + int32 index) const; + void _NotifyRefRemoved(int32 index) const; + void _NotifyRefsSorted() const; + void _NotifyCurrentRefChanged(int32 newIndex) const; private: - BList fRefs; - BList fListeners; + BList fRefs; + BList fListeners; - int32 fCurrentIndex; + int32 fCurrentIndex; }; #endif diff --git a/src/apps/mediaplayer/PlaylistListView.cpp b/src/apps/mediaplayer/PlaylistListView.cpp new file mode 100644 index 0000000000..4cbdd485f5 --- /dev/null +++ b/src/apps/mediaplayer/PlaylistListView.cpp @@ -0,0 +1,502 @@ +/* + * Copyright 2007, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ +#include "PlaylistListView.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include "Controller.h" +#include "ControllerObserver.h" +#include "ListViews.h" +#include "PlaybackState.h" +#include "Playlist.h" +#include "PlaylistObserver.h" + +using std::nothrow; + + +enum { + DISPLAY_NAME = 0, + DISPLAY_PATH = 1 +}; + + +static float +playback_mark_size(const font_height& fh) +{ + return ceilf(fh.ascent * 0.7); +} + + +static float +text_offset(const font_height& fh) +{ + return ceilf(fh.ascent * 0.8); +} + + +class PlaylistItem : public SimpleItem { + public: + PlaylistItem(const entry_ref& ref); + virtual ~PlaylistItem(); + + void Draw(BView* owner, BRect frame, + const font_height& fh, + bool tintedLine, uint32 mode, + bool active, + uint32 playbackState); + + private: + entry_ref fRef; + +}; + + +PlaylistItem::PlaylistItem(const entry_ref& ref) + : SimpleItem(ref.name), + fRef(ref) +{ +} + + +PlaylistItem::~PlaylistItem() +{ +} + + +void +PlaylistItem::Draw(BView* owner, BRect frame, const font_height& fh, + bool tintedLine, uint32 mode, bool active, uint32 playbackState) +{ + rgb_color color = (rgb_color){ 255, 255, 255, 255 }; + if (tintedLine) + color = tint_color(color, 1.04); + // background + if (IsSelected()) + color = tint_color(color, B_DARKEN_2_TINT); + owner->SetLowColor(color); + owner->FillRect(frame, B_SOLID_LOW); + // label + rgb_color black = (rgb_color){ 0, 0, 0, 255 }; + owner->SetHighColor(black); + const char* text = Text(); + switch (mode) { + case DISPLAY_NAME: + // TODO + break; + case DISPLAY_PATH: + // TODO + break; + default: + break; + } + + float playbackMarkSize = playback_mark_size(fh); + float textOffset = text_offset(fh); + + BString truncatedString(text); + owner->TruncateString(&truncatedString, B_TRUNCATE_MIDDLE, + frame.Width() - playbackMarkSize - textOffset); + owner->DrawString(truncatedString.String(), + BPoint(frame.left + playbackMarkSize + textOffset, + floorf(frame.top + frame.bottom + fh.ascent) / 2 - 1)); + + // playmark + if (active) { + rgb_color green = (rgb_color){ 0, 255, 0, 255 }; + if (playbackState != PLAYBACK_STATE_PLAYING) + green = tint_color(color, B_DARKEN_1_TINT); + + BRect r(0, 0, playbackMarkSize, playbackMarkSize); + r.OffsetTo(frame.left + 4, + ceilf((frame.top + frame.bottom) / 2) - 5); + + BPoint arrow[3]; + arrow[0] = r.LeftTop(); + arrow[1] = r.LeftBottom(); + arrow[2].x = r.right; + arrow[2].y = (r.top + r.bottom) / 2; +#ifdef __HAIKU__ + owner->SetPenSize(2); + owner->StrokePolygon(arrow, 3); + owner->SetPenSize(1); +#else + rgb_color lightGreen = tint_color(green, B_LIGHTEN_2_TINT); + rgb_color darkGreen = tint_color(green, B_DARKEN_2_TINT); + owner->BeginLineArray( 6 ); + // black outline + owner->AddLine(arrow[0], arrow[1], black); + owner->AddLine(BPoint(arrow[1].x + 1.0, arrow[1].y - 1.0), + arrow[2], black); + owner->AddLine(arrow[0], arrow[2], black); + // inset arrow + arrow[0].x += 1.0; + arrow[0].y += 2.0; + arrow[1].x += 1.0; + arrow[1].y -= 2.0; + arrow[2].x -= 2.0; + // highlights and shadow + owner->AddLine(arrow[1], arrow[2], darkGreen); + owner->AddLine(arrow[0], arrow[2], lightGreen); + owner->AddLine(arrow[0], arrow[1], lightGreen); + owner->EndLineArray(); + // fill green + arrow[0].x += 1.0; + arrow[0].y += 1.0; + arrow[1].x += 1.0; + arrow[1].y -= 1.0; + arrow[2].x -= 2.0; +#endif // __HAIKU__ + owner->SetLowColor(owner->HighColor()); + owner->SetHighColor(green); + owner->FillPolygon(arrow, 3); + } +} + + +// #pragma mark - + + +PlaylistListView::PlaylistListView(BRect frame, Playlist* playlist, + Controller* controller) + : SimpleListView(frame, "playlist listview", NULL) + + , fPlaylist(playlist) + , fPlaylistObserver(new PlaylistObserver(this)) + + , fController(controller) + , fControllerObserver(new ControllerObserver(this, + OBSERVE_PLAYBACK_STATE_CHANGES)) + + , fCurrentPlaylistIndex(-1) + , fPlaybackState(PLAYBACK_STATE_STOPPED) + + , fLastClickedItem(NULL) +{ + fPlaylist->AddListener(fPlaylistObserver); + fController->AddListener(fControllerObserver); + +#ifdef __HAIKU__ + SetFlags(Flags() & B_SUBPIXEL_PRECISE); +#endif +} + + +PlaylistListView::~PlaylistListView() +{ + fPlaylist->RemoveListener(fPlaylistObserver); + delete fPlaylistObserver; + fController->RemoveListener(fControllerObserver); + delete fControllerObserver; +} + + +void +PlaylistListView::AttachedToWindow() +{ + _FullSync(); + SimpleListView::AttachedToWindow(); + + GetFontHeight(&fFontHeight); + MakeFocus(true); +} + + +void +PlaylistListView::MessageReceived(BMessage* message) +{ + switch (message->what) { + // PlaylistObserver messages + case MSG_PLAYLIST_REF_ADDED: { + entry_ref ref; + int32 index; + if (message->FindRef("refs", &ref) == B_OK + && message->FindInt32("index", &index) == B_OK) + _AddItem(ref, index); + break; + } + case MSG_PLAYLIST_REF_REMOVED: { + int32 index; + if (message->FindInt32("index", &index) == B_OK) + _RemoveItem(index); + break; + } + case MSG_PLAYLIST_REFS_SORTED: + _FullSync(); + break; + case MSG_PLAYLIST_CURRENT_REF_CHANGED: { + int32 index; + if (message->FindInt32("index", &index) == B_OK) + _SetCurrentPlaylistIndex(index); + break; + } + + // ControllerObserver messages + case MSG_CONTROLLER_PLAYBACK_STATE_CHANGED: { + uint32 state; + if (message->FindInt32("state", (int32*)&state) == B_OK) + _SetPlaybackState(state); + break; + } + + case B_SIMPLE_DATA: + if (message->HasRef("refs")) + _RefsReceived(message, fDropIndex); + else if (message->HasPointer("list")) + SimpleListView::MessageReceived(message); + break; + case B_REFS_RECEIVED: + _RefsReceived(message, fDropIndex); + break; + + default: + SimpleListView::MessageReceived(message); + break; + } +} + + +void +PlaylistListView::MouseDown(BPoint where) +{ + if (!IsFocus()) + MakeFocus(true); + + int32 clicks; + if (Window()->CurrentMessage()->FindInt32("clicks", &clicks) < B_OK) + clicks = 1; + + bool handled = false; + + float playbackMarkSize = playback_mark_size(fFontHeight); + float textOffset = text_offset(fFontHeight); + + for (int32 i = 0; + PlaylistItem* item = dynamic_cast(ItemAt(i)); i++) { + BRect r = ItemFrame(i); + if (r.Contains(where)) { + if (clicks == 2) { + // only do something if user clicked the same item twice + if (fLastClickedItem == item) { + BAutolock _(fPlaylist); + fPlaylist->SetCurrentRefIndex(i); + handled = true; + } + } else { + // remember last clicked item + fLastClickedItem = item; + if (i == fCurrentPlaylistIndex) { + r.right = r.left + playbackMarkSize + textOffset; + if (r.Contains (where)) { + fController->TogglePlaying(); + handled = true; + } + } + } + break; + } + } + + if (!handled) + SimpleListView::MouseDown(where); +} + + +void +PlaylistListView::KeyDown(const char* bytes, int32 numBytes) +{ + if (numBytes < 1) + return; + + if ((bytes[0] == B_BACKSPACE) || (bytes[0] == B_DELETE)) + RemoveSelected(); + + DragSortableListView::KeyDown(bytes, numBytes); +} + + +void +PlaylistListView::MoveItems(BList& indices, int32 toIndex) +{ + if (!fPlaylist->Lock()) + return; + + entry_ref currentRef; + bool adjustCurrentRef = fPlaylist->GetRefAt(fPlaylist->CurrentRefIndex(), + ¤tRef) == B_OK; + + int32 count = indices.CountItems(); + entry_ref refs[count]; + for (int32 i = 0; i < count; i++) { + int32 index = (int32)indices.ItemAtFast(i) - i; + // "-i" to account for items already removed in the + // target list + if (index < 0) { + // asynchronous message is out of date + return; + } + refs[i] = fPlaylist->RemoveRef(index, false); + if (index < toIndex) + toIndex --; + } + + for (int32 i = 0; i < count; i++) { + fPlaylist->AddRef(refs[i], toIndex++); + } + + if (adjustCurrentRef) + fPlaylist->SetCurrentRefIndex(fPlaylist->IndexOf(currentRef)); + + fPlaylist->Unlock(); +} + + +void +PlaylistListView::CopyItems(BList& indices, int32 toIndex) +{ + if (!fPlaylist->Lock()) + return; + + int32 count = indices.CountItems(); + entry_ref refs[count]; + for (int32 i = 0; i < count; i++) { + int32 index = (int32)indices.ItemAtFast(i); + if (index < 0) { + // asynchronous message is out of date + return; + } + if (fPlaylist->GetRefAt(index, &refs[i]) < B_OK) + return; + } + + for (int32 i = 0; i < count; i++) { + fPlaylist->AddRef(refs[i], toIndex++); + } + + fPlaylist->Unlock(); +} + + +void +PlaylistListView::RemoveItemList(BList& indices) +{ + if (!fPlaylist->Lock()) + return; + + int32 count = indices.CountItems(); + int32 lastRemovedIndex = -1; + for (int32 i = 0; i < count; i++) { + lastRemovedIndex = (int32)indices.ItemAtFast(i) - i; + // "-i" to account for items already removed in the + // target list + fPlaylist->RemoveRef(lastRemovedIndex); + } + + // in case we removed the currently playing file + if (fPlaylist->CurrentRefIndex() == -1) + fPlaylist->SetCurrentRefIndex(lastRemovedIndex); + + fPlaylist->Unlock(); +} + + +void +PlaylistListView::DrawListItem(BView* owner, int32 index, BRect frame) const +{ + if (PlaylistItem* item = dynamic_cast(ItemAt(index))) { + item->Draw(owner, frame, fFontHeight, index % 2, + DISPLAY_NAME, index == fCurrentPlaylistIndex, fPlaybackState); + } +} + + +// #pragma mark - + + +void +PlaylistListView::_FullSync() +{ + if (!fPlaylist->Lock()) + return; + + // TODO: detach scrollbar, and this will be quick... +// BScrollBar* scrollBar = ScrollBar(B_VERTICAL); +// SetScrollBar(); +// + MakeEmpty(); + + int32 count = fPlaylist->CountItems(); + for (int32 i = 0; i < count; i++) { + entry_ref ref; + if (fPlaylist->GetRefAt(i, &ref) == B_OK) + _AddItem(ref, i); + } + + _SetCurrentPlaylistIndex(fPlaylist->CurrentRefIndex()); + _SetPlaybackState(fController->PlaybackState()); + + fPlaylist->Unlock(); +} + + +void +PlaylistListView::_AddItem(const entry_ref& ref, int32 index) +{ + PlaylistItem* item = new (nothrow) PlaylistItem(ref); + if (item) + AddItem(item, index); +} + + +void +PlaylistListView::_RemoveItem(int32 index) +{ + delete RemoveItem(index); +} + + +void +PlaylistListView::_SetCurrentPlaylistIndex(int32 index) +{ + if (fCurrentPlaylistIndex == index) + return; + + InvalidateItem(fCurrentPlaylistIndex); + fCurrentPlaylistIndex = index; + InvalidateItem(fCurrentPlaylistIndex); +} + + +void +PlaylistListView::_SetPlaybackState(uint32 state) +{ + if (fPlaybackState == state) + return; + + fPlaybackState = state; + InvalidateItem(fCurrentPlaylistIndex); +} + + +void +PlaylistListView::_RefsReceived(BMessage* message, int32 dropIndex) +{ + if (!fPlaylist->Lock()) + return; + + fPlaylist->AppendRefs(message, dropIndex); + + fPlaylist->Unlock(); +} + diff --git a/src/apps/mediaplayer/PlaylistListView.h b/src/apps/mediaplayer/PlaylistListView.h new file mode 100644 index 0000000000..97959d13a3 --- /dev/null +++ b/src/apps/mediaplayer/PlaylistListView.h @@ -0,0 +1,65 @@ +/* + * Copyright 2007, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ +#ifndef PLAYLIST_LIST_VIEW_H +#define PLAYLIST_LIST_VIEW_H + +#include "ListViews.h" + +class Controller; +class ControllerObserver; +class Playlist; +class PlaylistItem; +class PlaylistObserver; + +class PlaylistListView : public SimpleListView { + public: + PlaylistListView(BRect frame, + Playlist* playlist, + Controller* controller); + virtual ~PlaylistListView(); + + // BView interface + virtual void AttachedToWindow(); + virtual void MessageReceived(BMessage* message); + + virtual void MouseDown(BPoint where); + virtual void KeyDown(const char* bytes, int32 numBytes); + + // SimpleListView interface + virtual void MoveItems(BList& indices, int32 toIndex); + virtual void CopyItems(BList& indices, int32 toIndex); + virtual void RemoveItemList(BList& indices); + + virtual void DrawListItem(BView* owner, int32 index, + BRect frame) const; + + private: + void _FullSync(); + void _AddItem(const entry_ref& ref, int32 index); + void _RemoveItem(int32 index); + + void _SetCurrentPlaylistIndex(int32 index); + void _SetPlaybackState(uint32 state); + + void _RefsReceived(BMessage* message, + int32 dropIndex); + + Playlist* fPlaylist; + PlaylistObserver* fPlaylistObserver; + + Controller* fController; + ControllerObserver* fControllerObserver; + + int32 fCurrentPlaylistIndex; + uint32 fPlaybackState; + + font_height fFontHeight; + PlaylistItem* fLastClickedItem; +}; + +#endif // PLAYLIST_LIST_VIEW_H diff --git a/src/apps/mediaplayer/PlaylistWindow.cpp b/src/apps/mediaplayer/PlaylistWindow.cpp index c499b08b51..eb1088039f 100644 --- a/src/apps/mediaplayer/PlaylistWindow.cpp +++ b/src/apps/mediaplayer/PlaylistWindow.cpp @@ -10,161 +10,17 @@ #include #include -#include "ListViews.h" -#include "Playlist.h" -#include "PlaylistObserver.h" - -class PlaylistListView : public SimpleListView { - public: - PlaylistListView(BRect frame, - Playlist* playlist); - virtual ~PlaylistListView(); - - // BView interface - virtual void AttachedToWindow(); - virtual void MessageReceived(BMessage* message); - - // SimpleListView interface - virtual void MoveItems(BList& items, int32 toIndex); - virtual void CopyItems(BList& items, int32 toIndex); - virtual void RemoveItemList(BList& indices); - - private: - void _FullSync(); - void _AddItem(const entry_ref& ref, int32 index); - void _RemoveItem(int32 index); - - Playlist* fPlaylist; - PlaylistObserver* fPlaylistObserver; -}; +#include "PlaylistListView.h" -// #pragma mark - - - -PlaylistListView::PlaylistListView(BRect frame, Playlist* playlist) - : SimpleListView(frame, "playlist listview", NULL) - , fPlaylist(playlist) - , fPlaylistObserver(new PlaylistObserver(this)) -{ - fPlaylist->AddListener(fPlaylistObserver); -} - - -PlaylistListView::~PlaylistListView() -{ - fPlaylist->RemoveListener(fPlaylistObserver); - delete fPlaylistObserver; -} - - -void -PlaylistListView::AttachedToWindow() -{ - _FullSync(); - SimpleListView::AttachedToWindow(); -} - - -void -PlaylistListView::MessageReceived(BMessage* message) -{ - switch (message->what) { - case MSG_PLAYLIST_REF_ADDED: { - entry_ref ref; - int32 index; - if (message->FindRef("refs", &ref) == B_OK - && message->FindInt32("index", &index) == B_OK) - _AddItem(ref, index); - break; - } - case MSG_PLAYLIST_REF_REMOVED: { - int32 index; - if (message->FindInt32("index", &index) == B_OK) - _RemoveItem(index); - break; - } - case MSG_PLAYLIST_REFS_SORTED: - _FullSync(); - break; - default: - SimpleListView::MessageReceived(message); - break; - } -} - - -void -PlaylistListView::MoveItems(BList& items, int32 toIndex) -{ - // TODO: drag indices instead of items! (avoids IndexOf()) - int32 count = items.CountItems(); - for (int32 i = 0; i < count; i++) { - BListItem* item = (BListItem*)items.ItemAtFast(i); - int32 index = IndexOf(item); - entry_ref ref = fPlaylist->RemoveRef(index); - } -} - - -void -PlaylistListView::CopyItems(BList& items, int32 toIndex) -{ -} - - -void -PlaylistListView::RemoveItemList(BList& indices) -{ - int32 count = indices.CountItems(); - for (int32 i = 0; i < count; i++) { - int32 index = (int32)indices.ItemAtFast(i); - fPlaylist->RemoveRef(index); - } -} - - -void -PlaylistListView::_FullSync() -{ -// BScrollBar* scrollBar = ScrollBar(B_VERTICAL); -// SetScrollBar(); -// - MakeEmpty(); - - int32 count = fPlaylist->CountItems(); - for (int32 i = 0; i < count; i++) { - entry_ref ref; - if (fPlaylist->GetRefAt(i, &ref) == B_OK) - _AddItem(ref, i); - } -} - - -void -PlaylistListView::_AddItem(const entry_ref& ref, int32 index) -{ - SimpleItem* item = new SimpleItem(ref.name); - AddItem(item, index); -} - - -void -PlaylistListView::_RemoveItem(int32 index) -{ - delete RemoveItem(index); -} - - -// #pragma mark - - - -PlaylistWindow::PlaylistWindow(BRect frame, Playlist* playlist) +PlaylistWindow::PlaylistWindow(BRect frame, Playlist* playlist, + Controller* controller) : BWindow(frame, "Playlist", B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS) { frame = Bounds(); frame.right -= B_V_SCROLL_BAR_WIDTH; - PlaylistListView* listView = new PlaylistListView(frame, playlist); + PlaylistListView* listView = new PlaylistListView(frame, playlist, + controller); fTopView = new BScrollView("playlist scrollview", listView, B_FOLLOW_ALL, 0, false, true, B_NO_BORDER); @@ -188,3 +44,18 @@ PlaylistWindow::QuitRequested() return false; } + +void +PlaylistWindow::MessageReceived(BMessage* message) +{ + switch (message->what) { + case B_MODIFIERS_CHANGED: + if (LastMouseMovedView()) + PostMessage(message, LastMouseMovedView()); + break; + default: + BWindow::MessageReceived(message); + break; + } +} + diff --git a/src/apps/mediaplayer/PlaylistWindow.h b/src/apps/mediaplayer/PlaylistWindow.h index 99aa1a3b50..28c65c6427 100644 --- a/src/apps/mediaplayer/PlaylistWindow.h +++ b/src/apps/mediaplayer/PlaylistWindow.h @@ -8,17 +8,21 @@ #ifndef PLAYLIST_WINDOW_H #define PLAYLIST_WINDOW_H + #include +class Controller; class Playlist; class PlaylistWindow : public BWindow { public: PlaylistWindow(BRect frame, - Playlist* playlist); + Playlist* playlist, + Controller* controller); virtual ~PlaylistWindow(); virtual bool QuitRequested(); + virtual void MessageReceived(BMessage* message); private: BView* fTopView;