* Refactor the "Remove and Put into Trash" backend a bit to allow giving it an

index and not work on the current selection only.
* Reenable the code in MainWin that implements the shortcuts to trigger this
  feature during playback. Now it uses the Undo/Redo stack and profits from
  existing polish.
* Add missing fPlaylist locking in MainWin at several places. Should not have
  caused any realworld problems, though.
* Add Undo/Redo shortcuts to MainWin and forward to Playlist window.
* Make sure the Playlist window opens on the same workspace as the player
  window. This could be seen when launching MediaPlayer on one workspace, later
  moving the window and then opening the Playlist window, it would open on
  the original workspace. The new behavior additionally pulls the playlist
  when it's already open and you invoke the "Playlist" menu item.
* Added the beginnings of a refactoring to make the Playlist and everything
  else use a PlaylistItem class, instead of entry_refs directly. This stuff is
  not yet used, though, just compiles.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@30776 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus
2009-05-17 00:16:08 +00:00
parent e85f80eedf
commit dd09109647
11 changed files with 436 additions and 35 deletions
+2
View File
@@ -55,10 +55,12 @@ Application MediaPlayer :
# playlist # playlist
CopyPLItemsCommand.cpp CopyPLItemsCommand.cpp
EntryRefPlaylistItem.cpp
ImportPLItemsCommand.cpp ImportPLItemsCommand.cpp
ListViews.cpp ListViews.cpp
MovePLItemsCommand.cpp MovePLItemsCommand.cpp
Playlist.cpp Playlist.cpp
PlaylistItem.cpp
PlaylistListView.cpp PlaylistListView.cpp
PlaylistObserver.cpp PlaylistObserver.cpp
PlaylistWindow.cpp PlaylistWindow.cpp
+37 -9
View File
@@ -194,6 +194,11 @@ MainWin::MainWin()
Settings::Default()->AddListener(&fGlobalSettingsListener); Settings::Default()->AddListener(&fGlobalSettingsListener);
_AdoptGlobalSettings(); _AdoptGlobalSettings();
AddShortcut('z', B_COMMAND_KEY, new BMessage(B_UNDO));
AddShortcut('y', B_COMMAND_KEY, new BMessage(B_UNDO));
AddShortcut('z', B_COMMAND_KEY | B_SHIFT_KEY, new BMessage(B_REDO));
AddShortcut('y', B_COMMAND_KEY | B_SHIFT_KEY, new BMessage(B_REDO));
Show(); Show();
} }
@@ -358,6 +363,11 @@ MainWin::MessageReceived(BMessage *msg)
} }
break; break;
case B_UNDO:
case B_REDO:
fPlaylistWindow->PostMessage(msg);
break;
case M_MEDIA_SERVER_STARTED: case M_MEDIA_SERVER_STARTED:
printf("TODO: implement M_MEDIA_SERVER_STARTED\n"); printf("TODO: implement M_MEDIA_SERVER_STARTED\n");
// fController->... // fController->...
@@ -404,6 +414,8 @@ MainWin::MessageReceived(BMessage *msg)
// ControllerObserver messages // ControllerObserver messages
case MSG_CONTROLLER_FILE_FINISHED: case MSG_CONTROLLER_FILE_FINISHED:
{ {
BAutolock _(fPlaylist);
bool hadNext = fPlaylist->SetCurrentRefIndex( bool hadNext = fPlaylist->SetCurrentRefIndex(
fPlaylist->CurrentRefIndex() + 1); fPlaylist->CurrentRefIndex() + 1);
if (!hadNext) { if (!hadNext) {
@@ -653,6 +665,8 @@ MainWin::MessageReceived(BMessage *msg)
} }
*/ */
case M_SET_PLAYLIST_POSITION: { case M_SET_PLAYLIST_POSITION: {
BAutolock _(fPlaylist);
int32 index; int32 index;
if (msg->FindInt32("index", &index) == B_OK) if (msg->FindInt32("index", &index) == B_OK)
fPlaylist->SetCurrentRefIndex(index); fPlaylist->SetCurrentRefIndex(index);
@@ -723,6 +737,7 @@ MainWin::OpenFile(const entry_ref &ref)
status_t err = fController->SetTo(ref); status_t err = fController->SetTo(ref);
if (err != B_OK) { if (err != B_OK) {
BAutolock _(fPlaylist);
if (fPlaylist->CountItems() == 1) { if (fPlaylist->CountItems() == 1) {
// display error if this is the only file we're supposed to play // display error if this is the only file we're supposed to play
BString message; BString message;
@@ -778,10 +793,17 @@ void
MainWin::ShowPlaylistWindow() MainWin::ShowPlaylistWindow()
{ {
if (fPlaylistWindow->Lock()) { if (fPlaylistWindow->Lock()) {
// make sure the window shows on the same workspace as ourself
uint32 workspaces = Workspaces();
if (fPlaylistWindow->Workspaces() != workspaces)
fPlaylistWindow->SetWorkspaces(workspaces);
// show or activate
if (fPlaylistWindow->IsHidden()) if (fPlaylistWindow->IsHidden())
fPlaylistWindow->Show(); fPlaylistWindow->Show();
else else
fPlaylistWindow->Activate(); fPlaylistWindow->Activate();
fPlaylistWindow->Unlock(); fPlaylistWindow->Unlock();
} }
} }
@@ -823,6 +845,7 @@ MainWin::_RefsReceived(BMessage* msg)
// the playlist ist replaced by dropped files // the playlist ist replaced by dropped files
// or the dropped files are appended to the end // or the dropped files are appended to the end
// of the existing playlist if <shift> is pressed // of the existing playlist if <shift> is pressed
BAutolock _(fPlaylist);
int32 appendIndex = modifiers() & B_SHIFT_KEY ? int32 appendIndex = modifiers() & B_SHIFT_KEY ?
fPlaylist->CountItems() : -1; fPlaylist->CountItems() : -1;
msg->AddInt32("append_index", appendIndex); msg->AddInt32("append_index", appendIndex);
@@ -1367,15 +1390,19 @@ MainWin::_KeyDown(BMessage *msg)
case 0x48: // numeric keypad left arrow case 0x48: // numeric keypad left arrow
PostMessage(M_SKIP_PREV); PostMessage(M_SKIP_PREV);
return B_OK; return B_OK;
// TODO: Reenable this and use Undo/Redo stack...
// case 0x34: //delete button case 0x34: //delete button
// case 0x3e: //d for delete case 0x3e: //d for delete
// case 0x2b: //t for Trash case 0x2b: //t for Trash
// if (modifiers() & B_COMMAND_KEY) { if (modifiers() & B_COMMAND_KEY) {
// PostMessage(M_FILE_DELETE); BAutolock _(fPlaylist);
// return B_OK; BMessage removeMessage(M_PLAYLIST_REMOVE_AND_PUT_INTO_TRASH);
// } removeMessage.AddInt32("playlist index",
// break; fPlaylist->CurrentRefIndex());
fPlaylistWindow->PostMessage(&removeMessage);
return B_OK;
}
break;
} }
return B_ERROR; return B_ERROR;
@@ -1541,6 +1568,7 @@ MainWin::_UpdateControlsEnabledStatus()
if (fHasAudio) if (fHasAudio)
enabledButtons |= VOLUME_ENABLED; enabledButtons |= VOLUME_ENABLED;
BAutolock _(fPlaylist);
bool canSkipPrevious, canSkipNext; bool canSkipPrevious, canSkipNext;
fPlaylist->GetSkipInfo(&canSkipPrevious, &canSkipNext); fPlaylist->GetSkipInfo(&canSkipPrevious, &canSkipNext);
if (canSkipPrevious) if (canSkipPrevious)
@@ -0,0 +1,152 @@
/*
* Copyright 2009 Stephan Aßmus <[email protected]>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#include "EntryRefPlaylistItem.h"
#include <new>
#include <MediaFile.h>
EntryRefPlaylistItem::EntryRefPlaylistItem(const entry_ref& ref)
:
fRef(ref)
{
}
EntryRefPlaylistItem::~EntryRefPlaylistItem()
{
}
status_t
EntryRefPlaylistItem::SetName(const BString& name)
{
BEntry entry(&fRef);
status_t ret = entry.Rename(name.String(), false);
if (ret != B_OK)
return ret;
entry.GetRef(&fRef);
_NotifyListeners();
return B_OK;
}
status_t
EntryRefPlaylistItem::GetName(BString& name) const
{
name = fRef.name;
return B_OK;
}
status_t
EntryRefPlaylistItem::SetTitle(const BString& title)
{
return B_NOT_SUPPORTED;
}
status_t
EntryRefPlaylistItem::GetTitle(BString& title) const
{
return B_NOT_SUPPORTED;
}
status_t
EntryRefPlaylistItem::SetAuthor(const BString& author)
{
return B_NOT_SUPPORTED;
}
status_t
EntryRefPlaylistItem::GetAuthor(BString& author) const
{
return B_NOT_SUPPORTED;
}
status_t
EntryRefPlaylistItem::SetAlbum(const BString& album)
{
return B_NOT_SUPPORTED;
}
status_t
EntryRefPlaylistItem::GetAlbum(BString& album) const
{
return B_NOT_SUPPORTED;
}
status_t
EntryRefPlaylistItem::SetTrackNumber(int32 trackNumber)
{
return B_NOT_SUPPORTED;
}
status_t
EntryRefPlaylistItem::GetTrackNumber(int32& trackNumber) const
{
return B_NOT_SUPPORTED;
}
status_t
EntryRefPlaylistItem::SetBitRate(int32 bitRate)
{
return B_NOT_SUPPORTED;
}
status_t
EntryRefPlaylistItem::GetBitRate(int32& bitRate) const
{
return B_NOT_SUPPORTED;
}
status_t
EntryRefPlaylistItem::GetDuration(bigtime_t& duration) const
{
return B_NOT_SUPPORTED;
}
// #pragma mark -
status_t
EntryRefPlaylistItem::MoveIntoTrash()
{
return B_NOT_SUPPORTED;
}
status_t
EntryRefPlaylistItem::RestoreFromTrash()
{
return B_NOT_SUPPORTED;
}
// #pragma mark -
BMediaFile*
EntryRefPlaylistItem::CreateMediaFile() const
{
return new (std::nothrow) BMediaFile(&fRef);
}
@@ -0,0 +1,56 @@
/*
* Copyright 2009 Stephan Aßmus <[email protected]>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#ifndef ENTRY_REF_PLAYLIST_ITEM_H
#define ENTRY_REF_PLAYLIST_ITEM_H
#include "PlaylistItem.h"
#include <Entry.h>
class EntryRefPlaylistItem : public PlaylistItem {
public:
EntryRefPlaylistItem(const entry_ref& ref);
virtual ~EntryRefPlaylistItem();
// archiving
// virtual status_t Unarchive(const BMessage* archive);
// virtual status_t Archive(BMessage* into) const;
//
// virtual status_t Unflatten(BDataIO* stream);
// virtual status_t Flatten(BDataIO* stream) const;
// properties
virtual status_t SetName(const BString& name);
virtual status_t GetName(BString& name) const;
virtual status_t SetTitle(const BString& title);
virtual status_t GetTitle(BString& title) const;
virtual status_t SetAuthor(const BString& author);
virtual status_t GetAuthor(BString& author) const;
virtual status_t SetAlbum(const BString& album);
virtual status_t GetAlbum(BString& album) const;
virtual status_t SetTrackNumber(int32 trackNumber);
virtual status_t GetTrackNumber(int32& trackNumber) const;
virtual status_t SetBitRate(int32 bitRate);
virtual status_t GetBitRate(int32& bitRate) const;
virtual status_t GetDuration(bigtime_t& duration) const;
// methods
virtual status_t MoveIntoTrash();
virtual status_t RestoreFromTrash();
// playback
virtual BMediaFile* CreateMediaFile() const;
private:
entry_ref fRef;
};
#endif // ENTRY_REF_PLAYLIST_ITEM_H
@@ -40,11 +40,6 @@ using std::nothrow;
// TODO: using BList for objects is bad, replace it with a template // TODO: using BList for objects is bad, replace it with a template
// TODO: Remove this and use Tracker's Command.h once it is moved into the private headers
namespace BPrivate {
const uint32 kMoveToTrash = 'Ttrs';
}
Playlist::Listener::Listener() {} Playlist::Listener::Listener() {}
Playlist::Listener::~Listener() {} Playlist::Listener::~Listener() {}
void Playlist::Listener::RefAdded(const entry_ref& ref, int32 index) {} void Playlist::Listener::RefAdded(const entry_ref& ref, int32 index) {}
@@ -0,0 +1,63 @@
/*
* Copyright 2009 Stephan Aßmus <[email protected]>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#include "PlaylistItem.h"
PlaylistItem::Listener::Listener()
{
}
PlaylistItem::Listener::~Listener()
{
}
void PlaylistItem::Listener::ItemChanged(const PlaylistItem* item)
{
}
// #pragma mark -
PlaylistItem::PlaylistItem()
{
}
PlaylistItem::~PlaylistItem()
{
}
//! You must hold the Playlist lock.
bool
PlaylistItem::AddListener(Listener* listener)
{
if (listener && !fListeners.HasItem(listener))
return fListeners.AddItem(listener);
return false;
}
//! You must hold the Playlist lock.
void
PlaylistItem::RemoveListener(Listener* listener)
{
fListeners.RemoveItem(listener);
}
void
PlaylistItem::_NotifyListeners() const
{
BList listeners(fListeners);
int32 count = listeners.CountItems();
for (int32 i = 0; i < count; i++) {
Listener* listener = (Listener*)listeners.ItemAtFast(i);
listener->ItemChanged(this);
}
}
@@ -0,0 +1,75 @@
/*
* Copyright 2009 Stephan Aßmus <[email protected]>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#ifndef PLAYLIST_ITEM_H
#define PLAYLIST_ITEM_H
#include <List.h>
#include <String.h>
class BDataIO;
class BMediaFile;
class BMessage;
class PlaylistItem {
public:
class Listener {
public:
Listener();
virtual ~Listener();
virtual void ItemChanged(const PlaylistItem* item);
};
public:
PlaylistItem();
virtual ~PlaylistItem();
// archiving
// virtual status_t Unarchive(const BMessage* archive) = 0;
// virtual status_t Archive(BMessage* into) const = 0;
//
// virtual status_t Unflatten(BDataIO* stream) = 0;
// virtual status_t Flatten(BDataIO* stream) const = 0;
// properties
virtual status_t SetName(const BString& name) = 0;
virtual status_t GetName(BString& name) const = 0;
virtual status_t SetTitle(const BString& title) = 0;
virtual status_t GetTitle(BString& title) const = 0;
virtual status_t SetAuthor(const BString& author) = 0;
virtual status_t GetAuthor(BString& author) const = 0;
virtual status_t SetAlbum(const BString& album) = 0;
virtual status_t GetAlbum(BString& album) const = 0;
virtual status_t SetTrackNumber(int32 trackNumber) = 0;
virtual status_t GetTrackNumber(int32& trackNumber) const = 0;
virtual status_t SetBitRate(int32 bitRate) = 0;
virtual status_t GetBitRate(int32& bitRate) const = 0;
virtual status_t GetDuration(bigtime_t& duration) const = 0;
// methods
virtual status_t MoveIntoTrash() = 0;
virtual status_t RestoreFromTrash() = 0;
// playback
virtual BMediaFile* CreateMediaFile() const = 0;
// listener support
bool AddListener(Listener* listener);
void RemoveListener(Listener* listener);
protected:
void _NotifyListeners() const;
private:
BList fListeners;
};
#endif // PLAYLIST_ITEM_H
@@ -1,5 +1,5 @@
/* /*
* Copyright 2007-2008, Haiku. All rights reserved. * Copyright 2007-2009, Haiku. All rights reserved.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
* *
* Authors: * Authors:
@@ -56,10 +56,10 @@ text_offset(const font_height& fh)
} }
class PlaylistItem : public SimpleItem { class PlaylistListView::Item : public SimpleItem {
public: public:
PlaylistItem(const entry_ref& ref); Item(const entry_ref& ref);
virtual ~PlaylistItem(); virtual ~Item();
void Draw(BView* owner, BRect frame, void Draw(BView* owner, BRect frame,
const font_height& fh, const font_height& fh,
@@ -73,20 +73,20 @@ class PlaylistItem : public SimpleItem {
}; };
PlaylistItem::PlaylistItem(const entry_ref& ref) PlaylistListView::Item::Item(const entry_ref& ref)
: SimpleItem(ref.name), : SimpleItem(ref.name),
fRef(ref) fRef(ref)
{ {
} }
PlaylistItem::~PlaylistItem() PlaylistListView::Item::~Item()
{ {
} }
void void
PlaylistItem::Draw(BView* owner, BRect frame, const font_height& fh, PlaylistListView::Item::Draw(BView* owner, BRect frame, const font_height& fh,
bool tintedLine, uint32 mode, bool active, uint32 playbackState) bool tintedLine, uint32 mode, bool active, uint32 playbackState)
{ {
rgb_color color = (rgb_color){ 255, 255, 255, 255 }; rgb_color color = (rgb_color){ 255, 255, 255, 255 };
@@ -323,7 +323,7 @@ PlaylistListView::MouseDown(BPoint where)
float textOffset = text_offset(fFontHeight); float textOffset = text_offset(fFontHeight);
for (int32 i = 0; for (int32 i = 0;
PlaylistItem* item = dynamic_cast<PlaylistItem*>(ItemAt(i)); i++) { Item* item = dynamic_cast<Item*>(ItemAt(i)); i++) {
BRect r = ItemFrame(i); BRect r = ItemFrame(i);
if (r.Contains(where)) { if (r.Contains(where)) {
if (clicks == 2) { if (clicks == 2) {
@@ -385,15 +385,14 @@ PlaylistListView::CopyItems(const BList& indices, int32 toIndex)
void void
PlaylistListView::RemoveItemList(const BList& indices) PlaylistListView::RemoveItemList(const BList& indices)
{ {
fCommandStack->Perform(new (nothrow) RemovePLItemsCommand(fPlaylist, RemoveItemList(indices, false);
(int32*)indices.Items(), indices.CountItems()));
} }
void void
PlaylistListView::DrawListItem(BView* owner, int32 index, BRect frame) const PlaylistListView::DrawListItem(BView* owner, int32 index, BRect frame) const
{ {
if (PlaylistItem* item = dynamic_cast<PlaylistItem*>(ItemAt(index))) { if (Item* item = dynamic_cast<Item*>(ItemAt(index))) {
item->Draw(owner, frame, fFontHeight, index % 2, item->Draw(owner, frame, fFontHeight, index % 2,
DISPLAY_NAME, index == fCurrentPlaylistIndex, fPlaybackState); DISPLAY_NAME, index == fCurrentPlaylistIndex, fPlaybackState);
} }
@@ -448,8 +447,24 @@ PlaylistListView::RemoveSelectionToTrash()
{ {
BList indices; BList indices;
GetSelectedItems(indices); GetSelectedItems(indices);
RemoveItemList(indices, true);
}
void
PlaylistListView::RemoveToTrash(int32 index)
{
BList indices;
indices.AddItem((void*)index);
RemoveItemList(indices, true);
}
void
PlaylistListView::RemoveItemList(const BList& indices, bool intoTrash)
{
fCommandStack->Perform(new (nothrow) RemovePLItemsCommand(fPlaylist, fCommandStack->Perform(new (nothrow) RemovePLItemsCommand(fPlaylist,
(int32*)indices.Items(), indices.CountItems(), true)); (int32*)indices.Items(), indices.CountItems(), intoTrash));
} }
@@ -496,7 +511,7 @@ PlaylistListView::_FullSync()
void void
PlaylistListView::_AddItem(const entry_ref& ref, int32 index) PlaylistListView::_AddItem(const entry_ref& ref, int32 index)
{ {
PlaylistItem* item = new (nothrow) PlaylistItem(ref); Item* item = new (nothrow) Item(ref);
if (item) if (item)
AddItem(item, index); AddItem(item, index);
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright 2007, Haiku. All rights reserved. * Copyright 2007-2009, Haiku. All rights reserved.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
* *
* Authors: * Authors:
@@ -14,11 +14,10 @@ class CommandStack;
class Controller; class Controller;
class ControllerObserver; class ControllerObserver;
class Playlist; class Playlist;
class PlaylistItem;
class PlaylistObserver; class PlaylistObserver;
class PlaylistListView : public SimpleListView { class PlaylistListView : public SimpleListView {
public: public:
PlaylistListView(BRect frame, PlaylistListView(BRect frame,
Playlist* playlist, Playlist* playlist,
Controller* controller, Controller* controller,
@@ -46,8 +45,13 @@ class PlaylistListView : public SimpleListView {
void Randomize(); void Randomize();
void RemoveSelectionToTrash(); void RemoveSelectionToTrash();
void RemoveToTrash(int32 index);
void RemoveItemList(const BList& indices,
bool intoTrash);
private:
class Item;
private:
void _FullSync(); void _FullSync();
void _AddItem(const entry_ref& ref, int32 index); void _AddItem(const entry_ref& ref, int32 index);
void _RemoveItem(int32 index); void _RemoveItem(int32 index);
@@ -67,7 +71,7 @@ class PlaylistListView : public SimpleListView {
uint32 fPlaybackState; uint32 fPlaybackState;
font_height fFontHeight; font_height fFontHeight;
PlaylistItem* fLastClickedItem; Item* fLastClickedItem;
}; };
#endif // PLAYLIST_LIST_VIEW_H #endif // PLAYLIST_LIST_VIEW_H
@@ -55,8 +55,7 @@ enum {
M_PLAYLIST_EMPTY = 'emty', M_PLAYLIST_EMPTY = 'emty',
M_PLAYLIST_RANDOMIZE = 'rand', M_PLAYLIST_RANDOMIZE = 'rand',
M_PLAYLIST_REMOVE = 'rmov', M_PLAYLIST_REMOVE = 'rmov'
M_PLAYLIST_REMOVE_AND_PUT_INTO_TRASH = 'rmtr'
}; };
#define SPACE 5 #define SPACE 5
@@ -198,8 +197,16 @@ PlaylistWindow::MessageReceived(BMessage* message)
fListView->RemoveSelected(); fListView->RemoveSelected();
break; break;
case M_PLAYLIST_REMOVE_AND_PUT_INTO_TRASH: case M_PLAYLIST_REMOVE_AND_PUT_INTO_TRASH:
fListView->RemoveSelectionToTrash(); {
printf("M_PLAYLIST_REMOVE_AND_PUT_INTO_TRASH\n");
message->PrintToStream();
int32 index;
if (message->FindInt32("playlist index", &index) == B_OK)
fListView->RemoveToTrash(index);
else
fListView->RemoveSelectionToTrash();
break; break;
}
default: default:
BWindow::MessageReceived(message); BWindow::MessageReceived(message);
break; break;
@@ -27,6 +27,10 @@ class RWLocker;
class BButton; class BButton;
class BFilePanel; class BFilePanel;
enum {
M_PLAYLIST_REMOVE_AND_PUT_INTO_TRASH = 'rmtr'
};
class PlaylistWindow : public BWindow { class PlaylistWindow : public BWindow {
public: public:
PlaylistWindow(BRect frame, PlaylistWindow(BRect frame,