* added notification support to Playlist and Controller

* added AbstractLOAdapter by Ingo Weinhold which makes
  notifications asynchronous
* removed "Player" interface/concept, replaced it by
  the notification mechanism (window loads new file
  when "current" ref index changes in Playlist)
* removed some cross classes dependencies
* the wind buttons are not displayed anymore for now
* lots of changes to the Controller
  - the decoder/player threads are kept running for the
    entire lifetime of the Controller object (in essence, makes
    it possible to playback seamless)
  - abstracted BMediaTrack usage into "Video-" and "AudioSupplier"
    objects (the BMediaTrack pointers are only still there, because
    I have not gotten around to fix the messy/hacky InfoWindow)
  - reaching the end of the stream will trigger a notification,
    so that the next file from the playlist is played
  - fSoundOutput is managed by the Controller
  - tried to make seeking seem more controlled (slider doesn't
    jump back to previous position)
  - playback position is correctly updated in GUI
  - volume is maintained independend of SoundOutput so
    that it can be transfered from one to the next output
  - performance time is maintained correctly (?) even if
    no audio stream is present
* work in progress Playlist window (drag sorting does not work yet!)
* rearranged menus a bit
* rearranged overlay code in the VideoView, but it cannot work
  like it is currently designed, since the buffers need to be
  switched all at once, which the video decoding thread
  is not doing yet
* dragging files into the main window with shift held down
  appends to the existing playlist
* dropping folders adds files recursively
* pressing space toggles playback (instead of changing some
  settings of the GUI)
* fixed some more minor issues or unimplemented stuff in the UI


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21276 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus 2007-05-29 22:29:50 +00:00
parent 406228de63
commit 5fa5e5fea7
37 changed files with 4024 additions and 635 deletions

View File

@ -0,0 +1,60 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <bonefish@cs.tu-berlin.de>
*/
#include "AbstractLOAdapter.h"
#include <Handler.h>
#include <Looper.h>
#include <Messenger.h>
// constructor
AbstractLOAdapter::AbstractLOAdapter(BHandler* handler)
: fHandler(handler),
fMessenger(NULL)
{
}
// constructor
AbstractLOAdapter::AbstractLOAdapter(const BMessenger& messenger)
: fHandler(NULL),
fMessenger(new BMessenger(messenger))
{
}
// destructor
AbstractLOAdapter::~AbstractLOAdapter()
{
delete fMessenger;
}
// DeliverMessage
void
AbstractLOAdapter::DeliverMessage(BMessage* message)
{
if (fHandler) {
if (BLooper* looper = fHandler->Looper())
looper->PostMessage(message, fHandler);
} else if (fMessenger)
fMessenger->SendMessage(message);
}
// DeliverMessage
void
AbstractLOAdapter::DeliverMessage(BMessage& message)
{
DeliverMessage(&message);
}
// DeliverMessage
void
AbstractLOAdapter::DeliverMessage(uint32 command)
{
BMessage message(command);
DeliverMessage(&message);
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <bonefish@cs.tu-berlin.de>
*/
// This class provides some basic functionality for derivation of a
// listener -> observer adapter.
// The derived class should implement constructors similar to the
// ones of this class and pass the respective parameter.
// Each of the listener hook functions should construct a message
// and let it be delivered by DeliverMessage().
#ifndef ABSTRACT_LO_ADAPTER_H
#define ABSTRACT_LO_ADAPTER_H
#include <SupportDefs.h>
class BHandler;
class BLooper;
class BMessage;
class BMessenger;
class AbstractLOAdapter {
public:
AbstractLOAdapter(BHandler* handler);
AbstractLOAdapter(const BMessenger& messenger);
virtual ~AbstractLOAdapter();
void DeliverMessage(BMessage* message);
void DeliverMessage(BMessage& message);
void DeliverMessage(uint32 command);
private:
BHandler* fHandler;
BMessenger* fMessenger;
};
#endif // ABSTRACT_LO_ADAPTER_H

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,7 @@
* Controller.h - Media Player for the Haiku Operating System
*
* Copyright (C) 2006 Marcus Overhagen <marcus@overhagen.de>
* Copyright (C) 2007 Stephan Aßmus <superstippi@gmx.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -22,22 +23,47 @@
#include <MediaDefs.h>
#include <MediaNode.h>
#include <List.h>
#include <Locker.h>
#include <String.h>
class AudioSupplier;
class BBitmap;
class BMediaFile;
class BMediaTrack;
class SoundOutput;
class VideoSupplier;
class VideoView;
class ControllerView;
class Controller
{
class Controller {
public:
class Listener {
public:
Listener();
virtual ~Listener();
virtual void FileFinished();
virtual void FileChanged();
virtual void VideoTrackChanged(int32 index);
virtual void AudioTrackChanged(int32 index);
virtual void VideoStatsChanged();
virtual void AudioStatsChanged();
virtual void PlaybackStateChanged(uint32 state);
virtual void PositionChanged(float position);
virtual void VolumeChanged(float volume);
};
Controller();
virtual ~Controller();
bool Lock();
status_t LockWithTimeout(bigtime_t timeout);
void Unlock();
status_t SetTo(const entry_ref &ref);
void GetSize(int *width, int *height);
@ -47,51 +73,67 @@ public:
status_t SelectAudioTrack(int n);
status_t SelectVideoTrack(int n);
bigtime_t Duration();
bigtime_t Position();
status_t Seek(bigtime_t pos);
void Stop();
void Play();
void Pause();
bool IsPaused();
bool IsStopped();
bool IsPaused() const;
bool IsStopped() const;
uint32 PlaybackState() const;
bigtime_t Duration();
bigtime_t Position();
void SetVolume(float value);
float Volume() const;
void SetPosition(float value);
// video view
void SetVideoView(VideoView *view);
void SetControllerView(ControllerView *view);
bool IsOverlayActive();
void LockBitmap();
bool LockBitmap();
void UnlockBitmap();
BBitmap * Bitmap();
void VolumeUp();
void VolumeDown();
void SetVolume(float value);
void SetPosition(float value);
void UpdateVolume(float value);
void UpdatePosition(float value);
// notification support
bool AddListener(Listener* listener);
void RemoveListener(Listener* listener);
private:
static int32 audio_decode_thread(void *self);
static int32 video_decode_thread(void *self);
static int32 audio_play_thread(void *self);
static int32 video_play_thread(void *self);
void _AudioDecodeThread();
void _AudioPlayThread();
void _VideoDecodeThread();
void _VideoPlayThread();
void _StartThreads();
void _StopThreads();
void _EndOfStreamReached(bool isVideo = false);
void _UpdatePosition(bigtime_t position,
bool isVideoPosition = false,
bool force = false);
static int32 _VideoDecodeThreadEntry(void *self);
static int32 _VideoPlayThreadEntry(void *self);
static int32 _AudioDecodeThreadEntry(void *self);
static int32 _AudioPlayThreadEntry(void *self);
void AudioDecodeThread();
void VideoDecodeThread();
void AudioPlayThread();
void VideoPlayThread();
void StartThreads();
void StopThreads();
private:
void _NotifyFileChanged();
void _NotifyFileFinished();
void _NotifyVideoTrackChanged(int32 index);
void _NotifyAudioTrackChanged(int32 index);
void _NotifyVideoStatsChanged();
void _NotifyAudioStatsChanged();
void _NotifyPlaybackStateChanged();
void _NotifyPositionChanged(float position);
void _NotifyVolumeChanged(float volume);
friend class InfoWin;
enum {
@ -106,24 +148,29 @@ private:
size_t sizeMax;
bigtime_t startTime;
bool formatChanged;
bool endOfStream;
media_format mediaFormat;
};
VideoView * fVideoView;
ControllerView * fControllerView;
BString fName;
volatile bool fPaused;
volatile bool fStopped;
float fVolume;
BMediaFile * fMediaFile;
BMediaTrack * fAudioTrack;
BMediaTrack * fVideoTrack;
BLocker fAudioTrackLock;
BLocker fVideoTrackLock;
BMediaTrack * fAudioTrack;
BMediaTrack * fVideoTrack;
mutable BLocker fDataLock;
VideoSupplier* fVideoSupplier;
AudioSupplier* fAudioSupplier;
BLocker fVideoSupplierLock;
BLocker fAudioSupplierLock;
BList * fAudioTrackList;
BList * fVideoTrackList;
bigtime_t fPosition;
media_format fAudioFormat;
media_format fVideoFormat;
@ -141,6 +188,7 @@ private:
volatile bool fSeekAudio;
volatile bool fSeekVideo;
volatile bigtime_t fSeekPosition;
bigtime_t fPosition;
bigtime_t fDuration;
int32 fAudioBufferCount;
@ -157,9 +205,13 @@ private:
bigtime_t fTimeSourceSysTime;
bigtime_t fTimeSourcePerfTime;
bool fAutoplay;
volatile bool fPauseAtEndOfStream;
volatile bool fSeekToStartAfterPause;
BBitmap * fCurrentBitmap;
BLocker fBitmapLock;
BList fListeners;
};

View File

@ -0,0 +1,139 @@
/*
* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "ControllerObserver.h"
#include <Message.h>
ControllerObserver::ControllerObserver(BHandler* target, uint32 observeFlags)
: Controller::Listener()
, AbstractLOAdapter(target)
, fObserveFlags(observeFlags)
{
}
ControllerObserver::~ControllerObserver()
{
}
void
ControllerObserver::FileFinished()
{
if (!(fObserveFlags & OBSERVE_FILE_CHANGES))
return;
BMessage message(MSG_CONTROLLER_FILE_FINISHED);
DeliverMessage(message);
}
void
ControllerObserver::FileChanged()
{
if (!(fObserveFlags & OBSERVE_FILE_CHANGES))
return;
BMessage message(MSG_CONTROLLER_FILE_CHANGED);
DeliverMessage(message);
}
void
ControllerObserver::VideoTrackChanged(int32 index)
{
if (!(fObserveFlags & OBSERVE_TRACK_CHANGES))
return;
BMessage message(MSG_CONTROLLER_VIDEO_TRACK_CHANGED);
message.AddInt32("index", index);
DeliverMessage(message);
}
void
ControllerObserver::AudioTrackChanged(int32 index)
{
if (!(fObserveFlags & OBSERVE_TRACK_CHANGES))
return;
BMessage message(MSG_CONTROLLER_AUDIO_TRACK_CHANGED);
message.AddInt32("index", index);
DeliverMessage(message);
}
void
ControllerObserver::VideoStatsChanged()
{
if (!(fObserveFlags & OBSERVE_STAT_CHANGES))
return;
BMessage message(MSG_CONTROLLER_VIDEO_STATS_CHANGED);
DeliverMessage(message);
}
void
ControllerObserver::AudioStatsChanged()
{
if (!(fObserveFlags & OBSERVE_STAT_CHANGES))
return;
BMessage message(MSG_CONTROLLER_AUDIO_STATS_CHANGED);
DeliverMessage(message);
}
void
ControllerObserver::PlaybackStateChanged(uint32 state)
{
if (!(fObserveFlags & OBSERVE_PLAYBACK_STATE_CHANGES))
return;
BMessage message(MSG_CONTROLLER_PLAYBACK_STATE_CHANGED);
message.AddInt32("state", state);
DeliverMessage(message);
}
void
ControllerObserver::PositionChanged(float position)
{
if (!(fObserveFlags & OBSERVE_POSITION_CHANGES))
return;
BMessage message(MSG_CONTROLLER_POSITION_CHANGED);
message.AddFloat("position", position);
DeliverMessage(message);
}
void
ControllerObserver::VolumeChanged(float volume)
{
if (!(fObserveFlags & OBSERVE_VOLUME_CHANGES))
return;
BMessage message(MSG_CONTROLLER_VOLUME_CHANGED);
message.AddFloat("volume", volume);
DeliverMessage(message);
}

View File

@ -0,0 +1,65 @@
/*
* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef CONTROLLER_OBSERVER_H
#define CONTROLLER_OBSERVER_H
#include "AbstractLOAdapter.h"
#include "Controller.h"
enum {
MSG_CONTROLLER_FILE_FINISHED = 'cnff',
MSG_CONTROLLER_FILE_CHANGED = 'cnfc',
MSG_CONTROLLER_VIDEO_TRACK_CHANGED = 'cnvt',
MSG_CONTROLLER_AUDIO_TRACK_CHANGED = 'cnat',
MSG_CONTROLLER_VIDEO_STATS_CHANGED = 'cnvs',
MSG_CONTROLLER_AUDIO_STATS_CHANGED = 'cnas',
MSG_CONTROLLER_PLAYBACK_STATE_CHANGED = 'cnps',
MSG_CONTROLLER_POSITION_CHANGED = 'cnpc',
MSG_CONTROLLER_VOLUME_CHANGED = 'cnvc'
};
enum {
OBSERVE_FILE_CHANGES = 0x0001,
OBSERVE_TRACK_CHANGES = 0x0002,
OBSERVE_STAT_CHANGES = 0x0004,
OBSERVE_PLAYBACK_STATE_CHANGES = 0x0008,
OBSERVE_POSITION_CHANGES = 0x0010,
OBSERVE_VOLUME_CHANGES = 0x0020,
OBSERVE_ALL_CHANGES = 0xffff
};
class ControllerObserver : public Controller::Listener,
public AbstractLOAdapter {
public:
ControllerObserver(BHandler* target,
uint32 observeFlags = OBSERVE_ALL_CHANGES);
virtual ~ControllerObserver();
// Controller::Listener interface
virtual void FileFinished();
virtual void FileChanged();
virtual void VideoTrackChanged(int32 index);
virtual void AudioTrackChanged(int32 index);
virtual void VideoStatsChanged();
virtual void AudioStatsChanged();
virtual void PlaybackStateChanged(uint32 state);
virtual void PositionChanged(float position);
virtual void VolumeChanged(float volume);
private:
uint32 fObserveFlags;
};
#endif // CONTROLLER_OBSERVER_H

View File

@ -2,6 +2,7 @@
* Controller.cpp - Media Player for the Haiku Operating System
*
* Copyright (C) 2006 Marcus Overhagen <marcus@overhagen.de>
* Copyright (C) 2007 Stephan Aßmus <superstippi@gmx.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -17,26 +18,32 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "ControllerView.h"
#include <Message.h>
#include <stdio.h>
#include <string.h>
#include "ControllerView.h"
#include "Controller.h"
#include "Playlist.h"
#include "Player.h"
#include "PlaylistObserver.h"
ControllerView::ControllerView(BRect frame, Controller *ctrl, Playlist *pl, Player *p)
ControllerView::ControllerView(BRect frame, Controller* controller,
Playlist* playlist)
: TransportControlGroup(frame)
, fController(ctrl)
, fPlaylist(pl)
, fPlayer(p)
, fController(controller)
, fPlaylist(playlist)
, fPlaylistObserver(new PlaylistObserver(this))
{
fPlaylist->AddListener(fPlaylistObserver);
}
ControllerView::~ControllerView()
{
fPlaylist->RemoveListener(fPlaylistObserver);
delete fPlaylistObserver;
}
@ -58,6 +65,11 @@ void
ControllerView::MessageReceived(BMessage *msg)
{
switch (msg->what) {
case MSG_PLAYLIST_REF_ADDED:
case MSG_PLAYLIST_REF_REMOVED:
CheckSkippable();
break;
default:
TransportControlGroup::MessageReceived(msg);
}
@ -69,6 +81,7 @@ ControllerView::MessageReceived(BMessage *msg)
uint32
ControllerView::EnabledButtons()
{
// TODO: superflous
return 0xffffffff;
}
@ -112,11 +125,7 @@ void
ControllerView::SkipBackward()
{
printf("ControllerView::SkipBackward()\n");
entry_ref ref;
if (fPlaylist->PrevRef(&ref) == B_OK) {
printf("prev ref: %s\n", ref.name);
fPlayer->OpenFile(ref);
}
fPlaylist->SetCurrentRefIndex(fPlaylist->CurrentRefIndex() - 1);
}
@ -124,11 +133,7 @@ void
ControllerView::SkipForward()
{
printf("ControllerView::SkipForward()\n");
entry_ref ref;
if (fPlaylist->NextRef(&ref) == B_OK) {
printf("next ref: %s\n", ref.name);
fPlayer->OpenFile(ref);
}
fPlaylist->SetCurrentRefIndex(fPlaylist->CurrentRefIndex() + 1);
}
@ -155,3 +160,16 @@ ControllerView::PositionChanged(float value)
fController->SetPosition(value);
}
// #pragma mark -
void
ControllerView::CheckSkippable()
{
bool canSkipNext, canSkipPrevious;
fPlaylist->GetSkipInfo(&canSkipPrevious, &canSkipNext);
SetSkippable(canSkipPrevious, canSkipNext);
}

View File

@ -2,6 +2,7 @@
* Controller.cpp - Media Player for the Haiku Operating System
*
* Copyright (C) 2006 Marcus Overhagen <marcus@overhagen.de>
* Copyright (C) 2007 Stephan Aßmus <superstippi@gmx.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -26,35 +27,39 @@
class Controller;
class Playlist;
class Player;
class PlaylistObserver;
class ControllerView : public TransportControlGroup
{
public:
ControllerView(BRect frame, Controller *ctrl, Playlist *pl, Player *p);
~ControllerView();
ControllerView(BRect frame, Controller* controller,
Playlist* playlist);
~ControllerView();
private:
void AttachedToWindow();
void MessageReceived(BMessage *msg);
void Draw(BRect updateRect);
void AttachedToWindow();
void MessageReceived(BMessage *msg);
void Draw(BRect updateRect);
// TransportControlGroup interface
virtual uint32 EnabledButtons();
virtual void TogglePlaying();
virtual void Stop();
virtual void Rewind();
virtual void Forward();
virtual void SkipBackward();
virtual void SkipForward();
virtual void VolumeChanged(float value);
virtual void ToggleMute();
virtual void PositionChanged(float value);
virtual uint32 EnabledButtons();
virtual void TogglePlaying();
virtual void Stop();
virtual void Rewind();
virtual void Forward();
virtual void SkipBackward();
virtual void SkipForward();
virtual void VolumeChanged(float value);
virtual void ToggleMute();
virtual void PositionChanged(float value);
// ControllerView
void CheckSkippable();
private:
Controller * fController;
Playlist * fPlaylist;
Player * fPlayer;
Controller* fController;
Playlist* fPlaylist;
PlaylistObserver* fPlaylistObserver;
};
#endif

View File

@ -4,14 +4,36 @@ SetSubDirSupportedPlatformsBeOSCompatible ;
AddSubDirSupportedPlatforms libbe_test ;
# source directories
local sourceDirs =
supplier
;
local sourceDir ;
for sourceDir in $(sourceDirs) {
SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src apps mediaplayer $(sourceDir) ] ;
}
Application MediaPlayer :
# supplier
AudioSupplier.cpp
MediaTrackAudioSupplier.cpp
MediaTrackVideoSupplier.cpp
VideoSupplier.cpp
# .
AbstractLOAdapter.cpp
Controller.cpp
ControllerObserver.cpp
ControllerView.cpp
DrawingTidbits.cpp
InfoWin.cpp
ListViews.cpp
MainApp.cpp
MainWin.cpp
Playlist.cpp
PlaylistObserver.cpp
PlaylistWindow.cpp
SoundOutput.cpp
TransportButton.cpp
TransportControlGroup.cpp
@ -19,7 +41,8 @@ Application MediaPlayer :
VideoNode.cpp
VideoView.cpp
VolumeSlider.cpp
: be media tracker $(TARGET_LIBSTDC++)
: be media tracker translation $(TARGET_LIBSTDC++)
: MediaPlayer.rdef
;

View File

@ -0,0 +1,891 @@
/*
* Copyright 2006-2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include <stdio.h>
#include <malloc.h>
#include <new>
#include <Bitmap.h>
#include <Cursor.h>
#include <Entry.h>
#include <MessageRunner.h>
#include <Messenger.h>
#include <ScrollBar.h>
#include <ScrollView.h>
#include <String.h>
#include <Window.h>
#include "ListViews.h"
const unsigned char kCopyCursor[] = { 16, 1, 1, 1,
0x00, 0x00, 0x70, 0x00, 0x48, 0x00, 0x48, 0x00,
0x27, 0xc0, 0x24, 0xb8, 0x12, 0x54, 0x10, 0x02,
0x79, 0xe2, 0x99, 0x22, 0x85, 0x7a, 0x61, 0x4a,
0x19, 0xca, 0x04, 0x4a, 0x02, 0x78, 0x00, 0x00,
0x00, 0x00, 0x70, 0x00, 0x78, 0x00, 0x78, 0x00,
0x3f, 0xc0, 0x3f, 0xf8, 0x1f, 0xfc, 0x1f, 0xfe,
0x7f, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0x7f, 0xfe,
0x1f, 0xfe, 0x07, 0xfe, 0x03, 0xf8, 0x00, 0x00 };
#define MAX_DRAG_HEIGHT 200.0
#define ALPHA 170
#define TEXT_OFFSET 5.0
enum {
MSG_TICK = 'tick',
};
using std::nothrow;
// SimpleItem class
SimpleItem::SimpleItem( const char *name )
: BStringItem( name )
{
}
SimpleItem::~SimpleItem()
{
}
// SimpleItem::DrawItem
void
SimpleItem::Draw(BView *owner, BRect frame, uint32 flags)
{
DrawBackground(owner, frame, flags);
// label
owner->SetHighColor( 0, 0, 0, 255 );
font_height fh;
owner->GetFontHeight( &fh );
const char* text = Text();
BString truncatedString( text );
owner->TruncateString( &truncatedString, B_TRUNCATE_MIDDLE,
frame.Width() - TEXT_OFFSET - 4.0 );
float height = frame.Height();
float textHeight = fh.ascent + fh.descent;
BPoint textPoint;
textPoint.x = frame.left + TEXT_OFFSET;
textPoint.y = frame.top
+ ceilf(height / 2.0 - textHeight / 2.0
+ fh.ascent);
owner->DrawString(truncatedString.String(), textPoint);
}
// SimpleItem::DrawBackground
void
SimpleItem::DrawBackground(BView *owner, BRect frame, uint32 flags)
{
// stroke a blue frame around the item if it's focused
if (flags & FLAGS_FOCUSED) {
owner->SetLowColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
owner->StrokeRect(frame, B_SOLID_LOW);
frame.InsetBy(1.0, 1.0);
}
// figure out bg-color
rgb_color color = (rgb_color){ 255, 255, 255, 255 };
if ( flags & FLAGS_TINTED_LINE )
color = tint_color( color, 1.06 );
// background
if ( IsSelected() )
color = tint_color( color, B_DARKEN_2_TINT );
owner->SetLowColor( color );
owner->FillRect( frame, B_SOLID_LOW );
}
// DragSortableListView class
DragSortableListView::DragSortableListView(BRect frame, const char* name,
list_view_type type, uint32 resizingMode,
uint32 flags)
: BListView(frame, name, type, resizingMode, flags),
fDropRect(0.0, 0.0, -1.0, -1.0),
fScrollPulse(NULL),
fDropIndex(-1),
fLastClickedItem(NULL),
fScrollView(NULL),
fDragCommand(B_SIMPLE_DATA),
fFocusedIndex(-1)
{
SetViewColor(B_TRANSPARENT_32_BIT);
}
DragSortableListView::~DragSortableListView()
{
delete fScrollPulse;
}
// AttachedToWindow
void
DragSortableListView::AttachedToWindow()
{
BListView::AttachedToWindow();
// work arround a bug in BListView
BRect bounds = Bounds();
BListView::FrameResized(bounds.Width(), bounds.Height());
}
// DetachedFromWindow
void
DragSortableListView::DetachedFromWindow()
{
}
// FrameResized
void
DragSortableListView::FrameResized(float width, float height)
{
BListView::FrameResized(width, height);
Invalidate();
}
/*
// MakeFocus
void
DragSortableListView::MakeFocus(bool focused)
{
if (focused != IsFocus()) {
Invalidate();
BListView::MakeFocus(focused);
}
}
*/
// Draw
void
DragSortableListView::Draw( BRect updateRect )
{
int32 firstIndex = IndexOf(updateRect.LeftTop());
int32 lastIndex = IndexOf(updateRect.RightBottom());
if (firstIndex >= 0) {
if (lastIndex < firstIndex)
lastIndex = CountItems() - 1;
// update rect contains items
BRect r = updateRect;
for (int32 i = firstIndex; i <= lastIndex; i++) {
r = ItemFrame(i);
DrawListItem(this, i, r);
}
updateRect.top = r.bottom + 1.0;
if (updateRect.IsValid()) {
SetLowColor(255, 255, 255, 255);
FillRect(updateRect, B_SOLID_LOW);
}
} else {
SetLowColor(255, 255, 255, 255);
FillRect(updateRect, B_SOLID_LOW);
}
// drop anticipation indication
if (fDropRect.IsValid()) {
SetHighColor(255, 0, 0, 255);
StrokeRect(fDropRect);
}
/* // focus indication
if (IsFocus()) {
SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
StrokeRect(Bounds());
}*/
}
// ScrollTo
void
DragSortableListView::ScrollTo(BPoint where)
{
uint32 buttons;
BPoint point;
GetMouse(&point, &buttons, false);
uint32 transit = Bounds().Contains(point) ? B_INSIDE_VIEW : B_OUTSIDE_VIEW;
MouseMoved(point, transit, &fDragMessageCopy);
BListView::ScrollTo(where);
}
// TargetedByScrollView
void
DragSortableListView::TargetedByScrollView(BScrollView* scrollView)
{
fScrollView = scrollView;
BListView::TargetedByScrollView(scrollView);
}
// InitiateDrag
bool
DragSortableListView::InitiateDrag( BPoint point, int32 index, bool )
{
// supress drag&drop while an item is focused
if (fFocusedIndex >= 0)
return false;
bool success = false;
BListItem* item = ItemAt( CurrentSelection( 0 ) );
if ( !item ) {
// workarround a timing problem
Select( index );
item = ItemAt( index );
}
if ( item ) {
// create drag message
BMessage msg( fDragCommand );
MakeDragMessage( &msg );
// figure out drag rect
float width = Bounds().Width();
BRect dragRect(0.0, 0.0, width, -1.0);
// figure out, how many items fit into our bitmap
int32 numItems;
bool fade = false;
for (numItems = 0; BListItem* item = ItemAt( CurrentSelection( numItems ) ); numItems++) {
dragRect.bottom += ceilf( item->Height() ) + 1.0;
if ( dragRect.Height() > MAX_DRAG_HEIGHT ) {
fade = true;
dragRect.bottom = MAX_DRAG_HEIGHT;
numItems++;
break;
}
}
BBitmap* dragBitmap = new BBitmap( dragRect, B_RGB32, true );
if ( dragBitmap && dragBitmap->IsValid() ) {
if ( BView *v = new BView( dragBitmap->Bounds(), "helper", B_FOLLOW_NONE, B_WILL_DRAW ) ) {
dragBitmap->AddChild( v );
dragBitmap->Lock();
BRect itemBounds( dragRect) ;
itemBounds.bottom = 0.0;
// let all selected items, that fit into our drag_bitmap, draw
for ( int32 i = 0; i < numItems; i++ ) {
int32 index = CurrentSelection( i );
BListItem* item = ItemAt( index );
itemBounds.bottom = itemBounds.top + ceilf( item->Height() );
if ( itemBounds.bottom > dragRect.bottom )
itemBounds.bottom = dragRect.bottom;
DrawListItem( v, index, itemBounds );
itemBounds.top = itemBounds.bottom + 1.0;
}
// make a black frame arround the edge
v->SetHighColor( 0, 0, 0, 255 );
v->StrokeRect( v->Bounds() );
v->Sync();
uint8 *bits = (uint8 *)dragBitmap->Bits();
int32 height = (int32)dragBitmap->Bounds().Height() + 1;
int32 width = (int32)dragBitmap->Bounds().Width() + 1;
int32 bpr = dragBitmap->BytesPerRow();
if (fade) {
for ( int32 y = 0; y < height - ALPHA / 2; y++, bits += bpr ) {
uint8 *line = bits + 3;
for (uint8 *end = line + 4 * width; line < end; line += 4)
*line = ALPHA;
}
for ( int32 y = height - ALPHA / 2; y < height; y++, bits += bpr ) {
uint8 *line = bits + 3;
for (uint8 *end = line + 4 * width; line < end; line += 4)
*line = (height - y) << 1;
}
} else {
for ( int32 y = 0; y < height; y++, bits += bpr ) {
uint8 *line = bits + 3;
for (uint8 *end = line + 4 * width; line < end; line += 4)
*line = ALPHA;
}
}
dragBitmap->Unlock();
}
} else {
delete dragBitmap;
dragBitmap = NULL;
}
if (dragBitmap)
DragMessage( &msg, dragBitmap, B_OP_ALPHA, BPoint( 0.0, 0.0 ) );
else
DragMessage( &msg, dragRect.OffsetToCopy( point ), this );
_SetDragMessage(&msg);
success = true;
}
return success;
}
// WindowActivated
void
DragSortableListView::WindowActivated( bool active )
{
// workarround for buggy focus indication of BScrollView
if ( BView* view = Parent() )
view->Invalidate();
}
// MessageReceived
void
DragSortableListView::MessageReceived(BMessage* message)
{
if (AcceptDragMessage(message)) {
DragSortableListView *list = NULL;
if ( message->FindPointer( "list", (void **)&list ) == B_OK
&& list == this ) {
int32 count = CountItems();
if ( fDropIndex < 0 || fDropIndex > count )
fDropIndex = count;
BList items;
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 );
else
MoveItems( items, fDropIndex );
}
fDropIndex = -1;
}
} else {
switch ( message->what ) {
case MSG_TICK: {
float scrollV = 0.0;
BRect rect(Bounds());
BPoint point;
uint32 buttons;
GetMouse(&point, &buttons, false);
if (rect.Contains(point)) {
// calculate the vertical scrolling offset
float hotDist = rect.Height() * SCROLL_AREA;
if (point.y > rect.bottom - hotDist)
scrollV = hotDist - (rect.bottom - point.y);
else if (point.y < rect.top + hotDist)
scrollV = (point.y - rect.top) - hotDist;
}
// scroll
if (scrollV != 0.0 && fScrollView) {
if (BScrollBar* scrollBar = fScrollView->ScrollBar(B_VERTICAL)) {
float value = scrollBar->Value();
scrollBar->SetValue(scrollBar->Value() + scrollV);
if (scrollBar->Value() != value) {
// update mouse position
uint32 buttons;
BPoint point;
GetMouse(&point, &buttons, false);
uint32 transit = Bounds().Contains(point) ? B_INSIDE_VIEW : B_OUTSIDE_VIEW;
MouseMoved(point, transit, &fDragMessageCopy);
}
}
}
break;
}
// case B_MODIFIERS_CHANGED:
// ModifiersChanged();
// break;
case B_MOUSE_WHEEL_CHANGED: {
BListView::MessageReceived( message );
BPoint point;
uint32 buttons;
GetMouse(&point, &buttons, false);
uint32 transit = Bounds().Contains(point) ? B_INSIDE_VIEW : B_OUTSIDE_VIEW;
MouseMoved(point, transit, &fDragMessageCopy);
break;
}
default:
BListView::MessageReceived( message );
break;
}
}
}
// KeyDown
void
DragSortableListView::KeyDown( const char* bytes, int32 numBytes )
{
if ( numBytes < 1 )
return;
if ( ( bytes[0] == B_BACKSPACE ) || ( bytes[0] == B_DELETE ) )
RemoveSelected();
BListView::KeyDown( bytes, numBytes );
}
// MouseDown
void
DragSortableListView::MouseDown( BPoint where )
{
int32 clicks = 1;
uint32 buttons = 0;
Window()->CurrentMessage()->FindInt32("clicks", &clicks);
Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons);
int32 clickedIndex = -1;
for (int32 i = 0; BListItem* item = ItemAt(i); i++) {
if (ItemFrame(i).Contains(where)) {
if (clicks == 2) {
// only do something if user clicked the same item twice
if (fLastClickedItem == item)
DoubleClicked(i);
} else {
// remember last clicked item
fLastClickedItem = item;
}
clickedIndex = i;
break;
}
}
if (clickedIndex == -1)
fLastClickedItem = NULL;
BListItem* item = ItemAt(clickedIndex);
if (ListType() == B_MULTIPLE_SELECTION_LIST
&& item && (buttons & B_SECONDARY_MOUSE_BUTTON)) {
if (item->IsSelected())
Deselect(clickedIndex);
else
Select(clickedIndex, true);
} else {
BListView::MouseDown(where);
}
}
// MouseMoved
void
DragSortableListView::MouseMoved(BPoint where, uint32 transit, const BMessage *msg)
{
if (msg && AcceptDragMessage(msg)) {
switch (transit) {
case B_ENTERED_VIEW:
case B_INSIDE_VIEW: {
// remember drag message
// this is needed to react on modifier changes
_SetDragMessage(msg);
// set drop target through virtual function
SetDropTargetRect(msg, where);
// go into autoscrolling mode
BRect r = Bounds();
r.InsetBy(0.0, r.Height() * SCROLL_AREA);
SetAutoScrolling(!r.Contains(where));
break;
}
case B_EXITED_VIEW:
// forget drag message
_SetDragMessage(NULL);
SetAutoScrolling(false);
// fall through
case B_OUTSIDE_VIEW:
_RemoveDropAnticipationRect();
break;
}
} else {
_RemoveDropAnticipationRect();
BListView::MouseMoved(where, transit, msg);
_SetDragMessage(NULL);
SetAutoScrolling(false);
BCursor cursor(B_HAND_CURSOR);
SetViewCursor(&cursor, true);
}
fLastMousePos = where;
}
// MouseUp
void
DragSortableListView::MouseUp( BPoint where )
{
// remove drop mark
_SetDropAnticipationRect( BRect( 0.0, 0.0, -1.0, -1.0 ) );
SetAutoScrolling(false);
// be sure to forget drag message
_SetDragMessage(NULL);
BListView::MouseUp( where );
BCursor cursor(B_HAND_CURSOR);
SetViewCursor(&cursor, true);
}
// DrawItem
void
DragSortableListView::DrawItem( BListItem *item, BRect itemFrame, bool complete )
{
DrawListItem( this, IndexOf( item ), itemFrame );
/* if (IsFocus()) {
SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
StrokeRect(Bounds());
}*/
}
// MouseWheelChanged
bool
DragSortableListView::MouseWheelChanged(float x, float y)
{
BPoint where;
uint32 buttons;
GetMouse(&where, &buttons, false);
if (Bounds().Contains(where))
return true;
else
return false;
}
// SetDragCommand
void
DragSortableListView::SetDragCommand(uint32 command)
{
fDragCommand = command;
}
// ModifiersChaned
void
DragSortableListView::ModifiersChanged()
{
SetDropTargetRect(&fDragMessageCopy, fLastMousePos);
}
// SetItemFocused
void
DragSortableListView::SetItemFocused(int32 index)
{
InvalidateItem(fFocusedIndex);
InvalidateItem(index);
fFocusedIndex = index;
}
// AcceptDragMessage
bool
DragSortableListView::AcceptDragMessage(const BMessage* message) const
{
return message->what == fDragCommand;
}
// SetDropTargetRect
void
DragSortableListView::SetDropTargetRect(const BMessage* message, BPoint where)
{
if (AcceptDragMessage(message)) {
bool copy = modifiers() & B_SHIFT_KEY;
bool replaceAll = !message->HasPointer("list") && !copy;
BRect r = Bounds();
if (replaceAll) {
r.bottom--; // compensate for scrollbar offset
_SetDropAnticipationRect(r);
fDropIndex = -1;
} else {
// offset where by half of item height
r = ItemFrame(0);
where.y += r.Height() / 2.0;
int32 index = IndexOf(where);
if (index < 0)
index = CountItems();
_SetDropIndex(index);
const uchar* cursorData = copy ? kCopyCursor : B_HAND_CURSOR;
BCursor cursor(cursorData);
SetViewCursor(&cursor, true);
}
}
}
// SetAutoScrolling
void
DragSortableListView::SetAutoScrolling(bool enable)
{
if (fScrollPulse && enable)
return;
if (enable) {
BMessenger messenger(this, Window());
BMessage message(MSG_TICK);
fScrollPulse = new BMessageRunner(messenger, &message, 40000LL);
} else {
delete fScrollPulse;
fScrollPulse = NULL;
}
}
// DoesAutoScrolling
bool
DragSortableListView::DoesAutoScrolling() const
{
return fScrollPulse;
}
// ScrollTo
void
DragSortableListView::ScrollTo(int32 index)
{
if (index < 0)
index = 0;
if (index >= CountItems())
index = CountItems() - 1;
if (BListItem* item = ItemAt(index)) {
BRect itemFrame = ItemFrame(index);
BRect bounds = Bounds();
if (itemFrame.top < bounds.top) {
ScrollTo(itemFrame.LeftTop());
} else if (itemFrame.bottom > bounds.bottom) {
ScrollTo(BPoint(0.0, itemFrame.bottom - bounds.Height()));
}
}
}
// MoveItems
void
DragSortableListView::MoveItems( BList& items, 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 )
index--;
}
// else ??? -> blow up
}
for ( int32 i = 0; BListItem* item = (BListItem*)removedItems.ItemAt( i ); i++ )
{
if ( AddItem( item, index ) )
{
// after we're done, the newly inserted items will be selected
Select( index, true );
// next items will be inserted after this one
index++;
}
else
delete item;
}
}
// CopyItems
void
DragSortableListView::CopyItems( BList& items, int32 index )
{
DeselectAll();
// by inserting the items after we copied all items first, we avoid
// cloning an item we already inserted and messing everything up
// 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 ) )
delete item;
}
for ( int32 i = 0; BListItem* item = (BListItem*)clonedItems.ItemAt( i ); i++ )
{
if ( AddItem( item, index ) )
{
// after we're done, the newly inserted items will be selected
Select( index, true );
// next items will be inserted after this one
index++;
}
else
delete item;
}
}
// RemoveItemList
void
DragSortableListView::RemoveItemList( BList& items )
{
int32 count = items.CountItems();
for ( int32 i = 0; i < count; i++ )
{
BListItem* item = (BListItem*)items.ItemAt( i );
if ( RemoveItem( item ) )
delete item;
}
}
// RemoveSelected
void
DragSortableListView::RemoveSelected()
{
// if (fFocusedIndex >= 0)
// return;
BList items;
for ( int32 i = 0; BListItem* item = ItemAt( CurrentSelection( i ) ); i++ )
items.AddItem( (void*)item );
RemoveItemList( items );
}
// CountSelectedItems
int32
DragSortableListView::CountSelectedItems() const
{
int32 count = 0;
while ( CurrentSelection( count ) >= 0 )
count++;
return count;
}
// SelectAll
void
DragSortableListView::SelectAll()
{
Select(0, CountItems() - 1);
}
// DeleteItem
bool
DragSortableListView::DeleteItem(int32 index)
{
BListItem* item = ItemAt(index);
if (item && RemoveItem(item)) {
delete item;
return true;
}
return false;
}
// _SetDropAnticipationRect
void
DragSortableListView::_SetDropAnticipationRect(BRect r)
{
if (fDropRect != r) {
if (fDropRect.IsValid())
Invalidate(fDropRect);
fDropRect = r;
if (fDropRect.IsValid())
Invalidate(fDropRect);
}
}
// _SetDropIndex
void
DragSortableListView::_SetDropIndex(int32 index)
{
if (fDropIndex != index) {
fDropIndex = index;
if (fDropIndex >= 0) {
int32 count = CountItems();
if (fDropIndex == count) {
BRect r;
if (BListItem* item = ItemAt(count - 1)) {
r = ItemFrame(count - 1);
r.top = r.bottom;
r.bottom = r.top + 1.0;
} else {
r = Bounds();
r.bottom--; // compensate for scrollbars moved slightly out of window
}
_SetDropAnticipationRect(r);
} else {
BRect r = ItemFrame(fDropIndex);
r.top--;
r.bottom = r.top + 1.0;
_SetDropAnticipationRect(r);
}
}
}
}
// _RemoveDropAnticipationRect
void
DragSortableListView::_RemoveDropAnticipationRect()
{
_SetDropAnticipationRect(BRect(0.0, 0.0, -1.0, -1.0));
// _SetDropIndex(-1);
}
// _SetDragMessage
void
DragSortableListView::_SetDragMessage(const BMessage* message)
{
if (message)
fDragMessageCopy = *message;
else
fDragMessageCopy.what = 0;
}
// SimpleListView class
SimpleListView::SimpleListView(BRect frame, BMessage* selectionChangeMessage)
: DragSortableListView(frame, "playlist listview",
B_MULTIPLE_SELECTION_LIST, B_FOLLOW_ALL,
B_WILL_DRAW | B_NAVIGABLE | B_FRAME_EVENTS),
fSelectionChangeMessage(selectionChangeMessage)
{
}
// SimpleListView class
SimpleListView::SimpleListView(BRect frame, const char* name,
BMessage* selectionChangeMessage,
list_view_type type,
uint32 resizingMode, uint32 flags)
: DragSortableListView(frame, name, type, resizingMode, flags),
fSelectionChangeMessage(selectionChangeMessage)
{
}
// destructor
SimpleListView::~SimpleListView()
{
delete fSelectionChangeMessage;
}
// MessageReceived
void
SimpleListView::MessageReceived( BMessage* message)
{
switch (message->what) {
default:
DragSortableListView::MessageReceived(message);
break;
}
}
// SelectionChanged
void
SimpleListView::SelectionChanged()
{
BLooper* looper = Looper();
if (fSelectionChangeMessage && looper) {
BMessage message(*fSelectionChangeMessage);
looper->PostMessage(&message);
}
}
// CloneItem
BListItem*
SimpleListView::CloneItem(int32 atIndex) const
{
BListItem* clone = NULL;
if (SimpleItem* item = dynamic_cast<SimpleItem*>(ItemAt(atIndex)))
clone = new SimpleItem(item->Text());
return clone;
}
// DrawListItem
void
SimpleListView::DrawListItem(BView* owner, int32 index, BRect frame) const
{
if (SimpleItem* item = dynamic_cast<SimpleItem*>(ItemAt(index))) {
uint32 flags = FLAGS_NONE;
if (index == fFocusedIndex)
flags |= FLAGS_FOCUSED;
if (index % 2)
flags |= FLAGS_TINTED_LINE;
item->Draw(owner, frame, flags);
}
}
// MakeDragMessage
void
SimpleListView::MakeDragMessage(BMessage* message) const
{
if (message) {
message->AddPointer( "list", (void*)dynamic_cast<const DragSortableListView*>(this));
int32 index;
for (int32 i = 0; (index = CurrentSelection(i)) >= 0; i++)
message->AddInt32( "index", index );
}
}

View File

@ -0,0 +1,152 @@
/*
* Copyright 2006-2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef LIST_VIEWS_H
#define LIST_VIEWS_H
#include <ListItem.h>
#include <ListView.h>
#include <Message.h>
enum {
FLAGS_NONE = 0x00,
FLAGS_TINTED_LINE = 0x01,
FLAGS_FOCUSED = 0x02,
};
// portion of the listviews height that triggers autoscrolling
// when the mouse is over it with a dragmessage
#define SCROLL_AREA 0.1
class BMessageRunner;
class BMessageFilter;
class InterfaceWindow;
class BScrollView;
// SimpleItem
class SimpleItem : public BStringItem {
public:
SimpleItem(const char* name);
virtual ~SimpleItem();
virtual void Draw(BView* owner, BRect frame,
uint32 flags);
virtual void DrawBackground(BView* owner, BRect frame,
uint32 flags);
};
// DragSortableListView
class DragSortableListView : public BListView {
public:
DragSortableListView(BRect frame, const char* name,
list_view_type type = B_SINGLE_SELECTION_LIST,
uint32 resizingMode = B_FOLLOW_LEFT
| B_FOLLOW_TOP,
uint32 flags = B_WILL_DRAW | B_NAVIGABLE
| B_FRAME_EVENTS);
virtual ~DragSortableListView();
// BListView interface
virtual void AttachedToWindow();
virtual void DetachedFromWindow();
virtual void FrameResized(float width, float height);
virtual void Draw(BRect updateRect);
virtual void ScrollTo(BPoint where);
virtual void TargetedByScrollView(BScrollView* scrollView);
virtual bool InitiateDrag(BPoint point, int32 index,
bool wasSelected);
virtual void MessageReceived(BMessage* message);
virtual void KeyDown(const char* bytes, int32 numBytes);
virtual void MouseDown(BPoint where);
virtual void MouseMoved(BPoint where, uint32 transit,
const BMessage* dragMessage);
virtual void MouseUp(BPoint where);
virtual void WindowActivated(bool active);
virtual void DrawItem(BListItem *item, BRect itemFrame,
bool complete = false);
// DragSortableListView
virtual void SetDragCommand(uint32 command);
virtual void ModifiersChanged(); // called by window
virtual void DoubleClicked(int32 index) {}
virtual void SetItemFocused(int32 index);
virtual bool AcceptDragMessage(const BMessage* message) const;
virtual void SetDropTargetRect(const BMessage* message,
BPoint where);
// autoscrolling
void SetAutoScrolling(bool enable);
bool DoesAutoScrolling() const;
BScrollView* ScrollView() const
{ return fScrollView; }
void ScrollTo(int32 index);
bool MouseWheelChanged(float x, float y);
virtual void MoveItems(BList& items, int32 toIndex);
virtual void CopyItems(BList& items, int32 toIndex);
virtual void RemoveItemList(BList& indices);
void RemoveSelected(); // uses RemoveItemList()
int32 CountSelectedItems() const;
void SelectAll();
virtual bool DeleteItem(int32 index);
virtual BListItem* CloneItem(int32 atIndex) const = 0;
virtual void DrawListItem(BView* owner, int32 index,
BRect itemFrame) const = 0;
virtual void MakeDragMessage(BMessage* message) const = 0;
private:
void _RemoveDropAnticipationRect();
void _SetDragMessage(const BMessage* message);
BRect fDropRect;
BMessage fDragMessageCopy;
BMessageRunner* fScrollPulse;
BPoint fLastMousePos;
protected:
void _SetDropAnticipationRect(BRect r);
void _SetDropIndex(int32 index);
int32 fDropIndex;
BListItem* fLastClickedItem;
BScrollView* fScrollView;
uint32 fDragCommand;
int32 fFocusedIndex;
};
// SimpleListView
class SimpleListView : public DragSortableListView {
public:
SimpleListView(BRect frame,
BMessage* selectionChangeMessage = NULL);
SimpleListView(BRect frame, const char* name,
BMessage* selectionChangeMessage = NULL,
list_view_type type = B_MULTIPLE_SELECTION_LIST,
uint32 resizingMode = B_FOLLOW_ALL_SIDES,
uint32 flags = B_WILL_DRAW | B_NAVIGABLE
| B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE);
~SimpleListView();
// DragSortableListView interface
virtual void MessageReceived(BMessage* message);
virtual void SelectionChanged();
virtual BListItem* CloneItem(int32 atIndex) const;
virtual void DrawListItem(BView* owner, int32 index,
BRect itemFrame) const;
virtual void MakeDragMessage(BMessage* message) const;
private:
BMessage* fSelectionChangeMessage;
};
#endif // LIST_VIEWS_H

View File

@ -2,6 +2,7 @@
* MainWin.cpp - Media Player for the Haiku Operating System
*
* Copyright (C) 2006 Marcus Overhagen <marcus@overhagen.de>
* Copyright (C) 2007 Stephan Aßmus <superstippi@gmx.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -18,7 +19,6 @@
*
*/
#include "MainWin.h"
#include "MainApp.h"
#include <View.h>
#include <Screen.h>
@ -35,6 +35,11 @@
#include <Debug.h>
#include <math.h>
#include "ControllerObserver.h"
#include "PlaylistObserver.h"
#include "PlaylistWindow.h"
#include "MainApp.h"
#define NAME "MediaPlayer"
#define MIN_WIDTH 250
@ -48,6 +53,7 @@ enum
M_FILE_OPEN = 0x1000,
M_FILE_NEWPLAYER,
M_FILE_INFO,
M_FILE_PLAYLIST,
M_FILE_ABOUT,
M_FILE_CLOSE,
M_FILE_QUIT,
@ -79,6 +85,8 @@ enum
M_SELECT_AUDIO_TRACK_END = 0x00000fff,
M_SELECT_VIDEO_TRACK = 0x00010000,
M_SELECT_VIDEO_TRACK_END = 0x000fffff,
M_SET_PLAYLIST_POSITION
};
//#define printf(a...)
@ -88,11 +96,17 @@ MainWin::MainWin()
: BWindow(BRect(100,100,350,300), NAME, B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS /* | B_WILL_ACCEPT_FIRST_CLICK */)
, fFilePanel(NULL)
, fInfoWin(NULL)
, fPlaylistWindow(NULL)
, fInfoWinShowing(false)
, fHasFile(false)
, fHasVideo(false)
, fHasAudio(false)
, fPlaylist(new Playlist)
, fPlaylistObserver(new PlaylistObserver(this))
, fController(new Controller)
, fControllerObserver(new ControllerObserver(this,
OBSERVE_FILE_CHANGES | OBSERVE_TRACK_CHANGES
| OBSERVE_PLAYBACK_STATE_CHANGES | OBSERVE_POSITION_CHANGES))
, fIsFullscreen(false)
, fKeepAspectRatio(true)
, fAlwaysOnTop(false)
@ -121,6 +135,7 @@ MainWin::MainWin()
CreateMenu();
fBackground->AddChild(fMenuBar);
fMenuBar->ResizeToPreferred();
fMenuBarWidth = (int)fMenuBar->Frame().Width() + 1;
fMenuBarHeight = (int)fMenuBar->Frame().Height() + 1;
fMenuBar->SetResizingMode(B_FOLLOW_NONE);
@ -131,7 +146,7 @@ MainWin::MainWin()
// controls
rect = BRect(0, fMenuBarHeight + 11, fBackground->Bounds().right, fBackground->Bounds().bottom);
fControls = new ControllerView(rect, fController, fPlaylist, this);
fControls = new ControllerView(rect, fController, fPlaylist);
fBackground->AddChild(fControls);
fControls->ResizeToPreferred();
fControlsHeight = (int)fControls->Frame().Height() + 1;
@ -140,17 +155,18 @@ MainWin::MainWin()
// fControls->MoveTo(0, fBackground->Bounds().bottom - fControlsHeight + 1);
// fVideoView->ResizeTo(fBackground->Bounds().Width(), fBackground->Bounds().Height() - fMenuBarHeight - fControlsHeight);
fPlaylist->AddListener(fPlaylistObserver);
fController->SetVideoView(fVideoView);
fController->SetControllerView(fControls);
fController->AddListener(fControllerObserver);
fVideoView->IsOverlaySupported();
printf("fMenuBarHeight %d\n", fMenuBarHeight);
printf("fControlsHeight %d\n", fControlsHeight);
printf("fControlsWidth %d\n", fControlsWidth);
// printf("fMenuBarHeight %d\n", fMenuBarHeight);
// printf("fControlsHeight %d\n", fControlsHeight);
// printf("fControlsWidth %d\n", fControlsWidth);
SetupWindow();
Show();
}
@ -158,13 +174,27 @@ MainWin::MainWin()
MainWin::~MainWin()
{
printf("MainWin::~MainWin\n");
delete fPlaylist;
delete fController;
delete fFilePanel;
fPlaylist->RemoveListener(fPlaylistObserver);
fController->RemoveListener(fControllerObserver);
// give the views a chance to detach from any notifiers
// before we delete them
fBackground->RemoveSelf();
delete fBackground;
if (fInfoWin) {
fInfoWin->Lock();
fInfoWin->Quit();
}
if (fPlaylistWindow) {
fPlaylistWindow->Lock();
fPlaylistWindow->Quit();
}
delete fPlaylist;
delete fController;
delete fFilePanel;
}
@ -175,16 +205,26 @@ MainWin::OpenFile(const entry_ref &ref)
status_t err = fController->SetTo(ref);
if (err != B_OK) {
char s[300];
sprintf(s, "Can't open file\n\n%s\n\nError 0x%08lx\n(%s)\n",
ref.name, err, strerror(err));
(new BAlert("error", s, "OK"))->Go();
if (fPlaylist->CountItems() == 1) {
// display error if this is the only file we're supposed to play
char s[300];
sprintf(s, "Can't open file\n\n%s\n\nError 0x%08lx\n(%s)\n",
ref.name, err, strerror(err));
(new BAlert("error", s, "OK"))->Go();
} else {
// just go to the next file and don't bother user
// TODO: this makes it impossible to skip backwards
// over a non recognized file!
fPlaylist->SetCurrentRefIndex(fPlaylist->CurrentRefIndex() + 1);
}
fHasFile = false;
fHasVideo = false;
fHasAudio = false;
SetTitle(NAME);
} else {
fHasFile = true;
fHasVideo = fController->VideoTrackCount() != 0;
fHasAudio = fController->AudioTrackCount() != 0;
SetTitle(ref.name);
}
SetupWindow();
@ -198,11 +238,15 @@ MainWin::SetupWindow()
// Populate the track menus
SetupTrackMenus();
// Enable both if a file was loaded
fAudioMenu->SetEnabled(fHasFile);
fVideoMenu->SetEnabled(fHasFile);
fAudioTrackMenu->SetEnabled(fHasFile);
fVideoTrackMenu->SetEnabled(fHasFile);
// Select first track (might be "none") in both
fAudioMenu->ItemAt(0)->SetMarked(true);
fVideoMenu->ItemAt(0)->SetMarked(true);
fAudioTrackMenu->ItemAt(0)->SetMarked(true);
fVideoTrackMenu->ItemAt(0)->SetMarked(true);
fVideoMenu->SetEnabled(fHasVideo);
fAudioMenu->SetEnabled(fHasAudio);
fDebugMenu->SetEnabled(fHasVideo);
if (fHasVideo) {
fController->GetSize(&fSourceWidth, &fSourceHeight);
@ -214,7 +258,9 @@ MainWin::SetupWindow()
fWidthScale = 1.0;
fHeightScale = 1.0;
}
UpdateControlsEnabledStatus();
ResizeWindow(100);
fVideoView->MakeFocus();
@ -238,7 +284,11 @@ MainWin::ResizeWindow(int percent)
// Calculate and set the initial window size
int width = max_c(fControlsWidth, video_width);
int height = (fNoMenu ? 0 : fMenuBarHeight) + (fNoControls ? 0 : fControlsHeight) + video_height;
int height = (fNoControls ? 0 : fControlsHeight) + video_height;
if (!fNoMenu) {
width = max_c(width, fMenuBarWidth);
height += fMenuBarHeight;
}
SetWindowSizeLimits();
ResizeTo(width - 1, height - 1);
}
@ -247,10 +297,12 @@ MainWin::ResizeWindow(int percent)
void
MainWin::SetWindowSizeLimits()
{
int min_width = fNoControls ? MIN_WIDTH : fControlsWidth;
int min_height = (fNoMenu ? 0 : fMenuBarHeight) + (fNoControls ? 0 : fControlsHeight);
int minWidth = fNoControls ? MIN_WIDTH : fControlsWidth;
if (!fNoMenu)
minWidth = max_c(minWidth, fMenuBarWidth);
int minHeight = (fNoMenu ? 0 : fMenuBarHeight) + (fNoControls ? 0 : fControlsHeight);
SetSizeLimits(min_width - 1, 32000, min_height - 1, fHasVideo ? 32000 : min_height - 1);
SetSizeLimits(minWidth - 1, 32000, minHeight - 1, fHasVideo ? 32000 : minHeight - 1);
}
@ -258,48 +310,55 @@ void
MainWin::CreateMenu()
{
fFileMenu = new BMenu(NAME);
fViewMenu = new BMenu("View");
fPlaylistMenu = new BMenu("Playlist"B_UTF8_ELLIPSIS);
fAudioMenu = new BMenu("Audio");
fVideoMenu = new BMenu("Video");
fSettingsMenu = new BMenu("Settings");
fAudioMenu = new BMenu("Audio Track");
fVideoMenu = new BMenu("Video Track");
fAudioTrackMenu = new BMenu("Track");
fVideoTrackMenu = new BMenu("Track");
fDebugMenu = new BMenu("Debug");
fMenuBar->AddItem(fFileMenu);
fMenuBar->AddItem(fViewMenu);
fMenuBar->AddItem(fAudioMenu);
fMenuBar->AddItem(fVideoMenu);
fMenuBar->AddItem(fSettingsMenu);
fMenuBar->AddItem(fDebugMenu);
fFileMenu->AddItem(new BMenuItem("New Player", new BMessage(M_FILE_NEWPLAYER), 'N', B_COMMAND_KEY));
// fMenuBar->AddItem(fDebugMenu);
fFileMenu->AddItem(new BMenuItem("New Player"B_UTF8_ELLIPSIS, new BMessage(M_FILE_NEWPLAYER), 'N', B_COMMAND_KEY));
fFileMenu->AddSeparatorItem();
fFileMenu->AddItem(new BMenuItem("Open File"B_UTF8_ELLIPSIS, new BMessage(M_FILE_OPEN), 'O', B_COMMAND_KEY));
fFileMenu->AddItem(new BMenuItem("File Info"B_UTF8_ELLIPSIS, new BMessage(M_FILE_INFO), 'I', B_COMMAND_KEY));
fFileMenu->AddItem(fPlaylistMenu);
fPlaylistMenu->Superitem()->SetShortcut('P', B_COMMAND_KEY);
fPlaylistMenu->Superitem()->SetMessage(new BMessage(M_FILE_PLAYLIST));
fFileMenu->AddSeparatorItem();
fFileMenu->AddItem(new BMenuItem("About" NAME B_UTF8_ELLIPSIS, new BMessage(M_FILE_ABOUT)));
fFileMenu->AddSeparatorItem();
fFileMenu->AddItem(new BMenuItem("Close", new BMessage(M_FILE_CLOSE), 'W', B_COMMAND_KEY));
fFileMenu->AddItem(new BMenuItem("Quit", new BMessage(M_FILE_QUIT), 'Q', B_COMMAND_KEY));
fViewMenu->AddItem(new BMenuItem("50% scale", new BMessage(M_VIEW_50), '0', B_COMMAND_KEY));
fViewMenu->AddItem(new BMenuItem("100% scale", new BMessage(M_VIEW_100), '1', B_COMMAND_KEY));
fViewMenu->AddItem(new BMenuItem("200% scale", new BMessage(M_VIEW_200), '2', B_COMMAND_KEY));
fViewMenu->AddItem(new BMenuItem("300% scale", new BMessage(M_VIEW_300), '3', B_COMMAND_KEY));
fViewMenu->AddItem(new BMenuItem("400% scale", new BMessage(M_VIEW_400), '4', B_COMMAND_KEY));
fViewMenu->AddSeparatorItem();
fViewMenu->AddItem(new BMenuItem("Full Screen", new BMessage(M_TOGGLE_FULLSCREEN), 'F', B_COMMAND_KEY));
// fViewMenu->SetRadioMode(true);
// fViewMenu->AddSeparatorItem();
// fViewMenu->AddItem(new BMenuItem("Always on Top", new BMessage(M_TOGGLE_ALWAYS_ON_TOP), 'T', B_COMMAND_KEY));
fPlaylistMenu->SetRadioMode(true);
fAudioMenu->AddItem(fAudioTrackMenu);
fVideoMenu->AddItem(fVideoTrackMenu);
fVideoMenu->AddSeparatorItem();
fVideoMenu->AddItem(new BMenuItem("50% scale", new BMessage(M_VIEW_50), '0', B_COMMAND_KEY));
fVideoMenu->AddItem(new BMenuItem("100% scale", new BMessage(M_VIEW_100), '1', B_COMMAND_KEY));
fVideoMenu->AddItem(new BMenuItem("200% scale", new BMessage(M_VIEW_200), '2', B_COMMAND_KEY));
fVideoMenu->AddItem(new BMenuItem("300% scale", new BMessage(M_VIEW_300), '3', B_COMMAND_KEY));
fVideoMenu->AddItem(new BMenuItem("400% scale", new BMessage(M_VIEW_400), '4', B_COMMAND_KEY));
fVideoMenu->AddSeparatorItem();
fVideoMenu->AddItem(new BMenuItem("Full Screen", new BMessage(M_TOGGLE_FULLSCREEN), 'F', B_COMMAND_KEY));
fVideoMenu->AddItem(new BMenuItem("Keep Aspect Ratio", new BMessage(M_TOGGLE_KEEP_ASPECT_RATIO), 'K', B_COMMAND_KEY));
fSettingsMenu->AddItem(fAudioMenu);
fSettingsMenu->AddItem(fVideoMenu);
fSettingsMenu->AddSeparatorItem();
fSettingsMenu->AddItem(new BMenuItem("No Menu", new BMessage(M_TOGGLE_NO_MENU), 'M', B_COMMAND_KEY));
fSettingsMenu->AddItem(new BMenuItem("No Border", new BMessage(M_TOGGLE_NO_BORDER), 'B', B_COMMAND_KEY));
fSettingsMenu->AddItem(new BMenuItem("No Controls", new BMessage(M_TOGGLE_NO_CONTROLS), 'C', B_COMMAND_KEY));
fSettingsMenu->AddItem(new BMenuItem("Always on Top", new BMessage(M_TOGGLE_ALWAYS_ON_TOP), 'T', B_COMMAND_KEY));
fSettingsMenu->AddItem(new BMenuItem("Keep Aspect Ratio", new BMessage(M_TOGGLE_KEEP_ASPECT_RATIO), 'K', B_COMMAND_KEY));
fSettingsMenu->AddSeparatorItem();
fSettingsMenu->AddItem(new BMenuItem("Preferences"B_UTF8_ELLIPSIS, new BMessage(M_PREFERENCES), 'P', B_COMMAND_KEY));
// fSettingsMenu->AddSeparatorItem();
// fSettingsMenu->AddItem(new BMenuItem("Preferences"B_UTF8_ELLIPSIS, new BMessage(M_PREFERENCES), 'P', B_COMMAND_KEY));
fDebugMenu->AddItem(new BMenuItem("pixel aspect ratio 1.00000:1", new BMessage(M_ASPECT_100000_1)));
fDebugMenu->AddItem(new BMenuItem("pixel aspect ratio 1.06666:1", new BMessage(M_ASPECT_106666_1)));
@ -309,8 +368,8 @@ MainWin::CreateMenu()
fDebugMenu->AddItem(new BMenuItem("force 704 x 576, display aspect 4:3", new BMessage(M_ASPECT_704_576)));
fDebugMenu->AddItem(new BMenuItem("force 544 x 576, display aspect 4:3", new BMessage(M_ASPECT_544_576)));
fAudioMenu->SetRadioMode(true);
fVideoMenu->SetRadioMode(true);
fAudioTrackMenu->SetRadioMode(true);
fVideoTrackMenu->SetRadioMode(true);
/*
fSettingsMenu->ItemAt(3)->SetMarked(fIsFullscreen);
fSettingsMenu->ItemAt(5)->SetMarked(fNoMenu);
@ -325,8 +384,8 @@ MainWin::CreateMenu()
void
MainWin::SetupTrackMenus()
{
fAudioMenu->RemoveItems(0, fAudioMenu->CountItems(), true);
fVideoMenu->RemoveItems(0, fVideoMenu->CountItems(), true);
fAudioTrackMenu->RemoveItems(0, fAudioTrackMenu->CountItems(), true);
fVideoTrackMenu->RemoveItems(0, fVideoTrackMenu->CountItems(), true);
int c, i;
char s[100];
@ -334,18 +393,18 @@ MainWin::SetupTrackMenus()
c = fController->AudioTrackCount();
for (i = 0; i < c; i++) {
sprintf(s, "Track %d", i + 1);
fAudioMenu->AddItem(new BMenuItem(s, new BMessage(M_SELECT_AUDIO_TRACK + i)));
fAudioTrackMenu->AddItem(new BMenuItem(s, new BMessage(M_SELECT_AUDIO_TRACK + i)));
}
if (!c)
fAudioMenu->AddItem(new BMenuItem("none", new BMessage(M_DUMMY)));
fAudioTrackMenu->AddItem(new BMenuItem("none", new BMessage(M_DUMMY)));
c = fController->VideoTrackCount();
for (i = 0; i < c; i++) {
sprintf(s, "Track %d", i + 1);
fVideoMenu->AddItem(new BMenuItem(s, new BMessage(M_SELECT_VIDEO_TRACK + i)));
fVideoTrackMenu->AddItem(new BMenuItem(s, new BMessage(M_SELECT_VIDEO_TRACK + i)));
}
if (!c)
fVideoMenu->AddItem(new BMenuItem("none", new BMessage(M_DUMMY)));
fVideoTrackMenu->AddItem(new BMenuItem("none", new BMessage(M_DUMMY)));
}
@ -358,7 +417,7 @@ MainWin::QuitRequested()
void
MainWin::MouseDown(BMessage *msg)
MainWin::MouseDown(BMessage *msg, BView* originalHandler)
{
BPoint screen_where;
uint32 buttons = msg->FindInt32("buttons");
@ -366,8 +425,9 @@ MainWin::MouseDown(BMessage *msg)
// On Zeta, only "screen_where" is relyable, "where" and "be:view_where" seem to be broken
if (B_OK != msg->FindPoint("screen_where", &screen_where)) {
// Workaround for BeOS R5, it has no "screen_where"
fVideoView->GetMouse(&screen_where, &buttons, false);
fVideoView->ConvertToScreen(&screen_where);
if (!originalHandler || msg->FindPoint("where", &screen_where) < B_OK)
return;
originalHandler->ConvertToScreen(&screen_where);
}
// msg->PrintToStream();
@ -423,7 +483,7 @@ MainWin::MouseDown(BMessage *msg)
void
MainWin::MouseMoved(BMessage *msg)
MainWin::MouseMoved(BMessage *msg, BView* originalHandler)
{
// msg->PrintToStream();
@ -437,11 +497,13 @@ MainWin::MouseMoved(BMessage *msg)
printf("view where: %.0f, %.0f => ", mousePos.x, mousePos.y);
fVideoView->ConvertToScreen(&mousePos);
*/
// On Zeta, only "screen_where" is relyable, "where" and "be:view_where" seem to be broken
// On Zeta, only "screen_where" is relyable, "where"
// and "be:view_where" seem to be broken
if (B_OK != msg->FindPoint("screen_where", &mousePos)) {
// Workaround for BeOS R5, it has no "screen_where"
fVideoView->GetMouse(&mousePos, &buttons, false);
fVideoView->ConvertToScreen(&mousePos);
if (!originalHandler || msg->FindPoint("where", &mousePos) < B_OK)
return;
originalHandler->ConvertToScreen(&mousePos);
}
// printf("screen where: %.0f, %.0f => ", mousePos.x, mousePos.y);
float delta_x = mousePos.x - fMouseDownMousePos.x;
@ -470,6 +532,11 @@ MainWin::ShowContextMenu(const BPoint &screen_point)
BMenuItem *item;
menu->AddItem(item = new BMenuItem("Full Screen", new BMessage(M_TOGGLE_FULLSCREEN), 'F', B_COMMAND_KEY));
item->SetMarked(fIsFullscreen);
item->SetEnabled(fHasVideo);
menu->AddItem(item = new BMenuItem("Keep Aspect Ratio", new BMessage(M_TOGGLE_KEEP_ASPECT_RATIO), 'K', B_COMMAND_KEY));
item->SetMarked(fKeepAspectRatio);
item->SetEnabled(fHasVideo);
menu->AddSeparatorItem();
menu->AddItem(item = new BMenuItem("No Menu", new BMessage(M_TOGGLE_NO_MENU), 'M', B_COMMAND_KEY));
item->SetMarked(fNoMenu);
@ -477,8 +544,7 @@ MainWin::ShowContextMenu(const BPoint &screen_point)
item->SetMarked(fNoBorder);
menu->AddItem(item = new BMenuItem("Always on Top", new BMessage(M_TOGGLE_ALWAYS_ON_TOP), 'T', B_COMMAND_KEY));
item->SetMarked(fAlwaysOnTop);
menu->AddItem(item = new BMenuItem("Keep Aspect Ratio", new BMessage(M_TOGGLE_KEEP_ASPECT_RATIO), 'K', B_COMMAND_KEY));
item->SetMarked(fKeepAspectRatio);
menu->AddSeparatorItem();
menu->AddItem(new BMenuItem("About" NAME B_UTF8_ELLIPSIS, new BMessage(M_FILE_ABOUT)));
menu->AddSeparatorItem();
@ -592,6 +658,15 @@ MainWin::ShowFileInfo()
{
if (!fInfoWin)
fInfoWin = new InfoWin(this);
if (fInfoWin->Lock()) {
if (fInfoWin->IsHidden())
fInfoWin->Show();
else
fInfoWin->Activate();
fInfoWin->Unlock();
}
BMessenger msgr(fInfoWin);
BMessage m(M_UPDATE_INFO);
m.AddInt32("which", INFO_ALL);
@ -599,6 +674,27 @@ MainWin::ShowFileInfo()
msgr.SendMessage(B_WINDOW_ACTIVATED);
}
void
MainWin::ShowPlaylistWindow()
{
if (!fPlaylistWindow) {
fPlaylistWindow = new PlaylistWindow(BRect(150, 150, 400, 500),
fPlaylist);
fPlaylistWindow->Show();
return;
}
if (fPlaylistWindow->Lock()) {
if (fPlaylistWindow->IsHidden())
fPlaylistWindow->Show();
else
fPlaylistWindow->Activate();
fPlaylistWindow->Unlock();
}
}
void
MainWin::MaybeUpdateFileInfo(uint32 which)
{
@ -776,29 +872,110 @@ MainWin::ToggleKeepAspectRatio()
}
void
MainWin::UpdateControlsEnabledStatus()
{
uint32 enabledButtons = 0;
if (fHasVideo || fHasAudio) {
enabledButtons |= PLAYBACK_ENABLED | SEEK_ENABLED
| SEEK_BACK_ENABLED | SEEK_FORWARD_ENABLED;
}
if (fHasAudio)
enabledButtons |= VOLUME_ENABLED;
bool canSkipPrevious, canSkipNext;
fPlaylist->GetSkipInfo(&canSkipPrevious, &canSkipNext);
if (canSkipPrevious)
enabledButtons |= SKIP_BACK_ENABLED;
if (canSkipNext)
enabledButtons |= SKIP_FORWARD_ENABLED;
fControls->SetEnabled(enabledButtons);
}
void
MainWin::_UpdatePlaylistMenu()
{
fPlaylistMenu->RemoveItems(0, fPlaylistMenu->CountItems(), true);
// TODO: lock fPlaylist
int32 count = fPlaylist->CountItems();
for (int32 i = 0; i < count; i++) {
entry_ref ref;
if (fPlaylist->GetRefAt(i, &ref) < B_OK)
continue;
_AddPlaylistItem(ref, i);
}
fPlaylistMenu->SetTargetForItems(this);
_MarkPlaylistItem(fPlaylist->CurrentRefIndex());
}
void
MainWin::_AddPlaylistItem(const entry_ref& ref, int32 index)
{
BMessage* message = new BMessage(M_SET_PLAYLIST_POSITION);
message->AddInt32("index", index);
BMenuItem* item = new BMenuItem(ref.name, message);
fPlaylistMenu->AddItem(item, index);
}
void
MainWin::_RemovePlaylistItem(int32 index)
{
delete fPlaylistMenu->RemoveItem(index);
}
void
MainWin::_MarkPlaylistItem(int32 index)
{
if (BMenuItem* item = fPlaylistMenu->ItemAt(index)) {
item->SetMarked(true);
// ... and in case the menu is currently on screen:
if (fPlaylistMenu->LockLooper()) {
fPlaylistMenu->Invalidate();
fPlaylistMenu->UnlockLooper();
}
}
}
void
MainWin::RefsReceived(BMessage *msg)
{
printf("MainWin::RefsReceived\n");
// the playlist ist replaced by dropped files
fPlaylist->MakeEmpty();
// or the dropped files are appended to the end
// of the existing playlist if <shift> is pressed
bool add = modifiers() & B_SHIFT_KEY;
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++) {
BEntry entry(&ref, true);
if (!entry.Exists() || !entry.IsFile())
continue;
fPlaylist->AddRef(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->Sort();
// open first file
if (fPlaylist->NextRef(&ref) == B_OK)
OpenFile(ref);
}
@ -818,7 +995,10 @@ MainWin::KeyDown(BMessage *msg)
switch (raw_char) {
case B_SPACE:
PostMessage(M_TOGGLE_NO_BORDER_NO_MENU_NO_CONTROLS);
if (fController->IsPaused() || fController->IsStopped())
fController->Play();
else
fController->Pause();
return B_OK;
case B_ESCAPE:
@ -928,14 +1108,22 @@ MainWin::KeyDown(BMessage *msg)
void
MainWin::DispatchMessage(BMessage *msg, BHandler *handler)
{
if ((msg->what == B_MOUSE_DOWN) && (handler == fBackground || handler == fVideoView))
MouseDown(msg);
if ((msg->what == B_MOUSE_MOVED) && (handler == fBackground || handler == fVideoView))
MouseMoved(msg);
if ((msg->what == B_MOUSE_UP) && (handler == fBackground || handler == fVideoView))
if ((msg->what == B_MOUSE_DOWN)
&& (handler == fBackground || handler == fVideoView
|| handler == fControls))
MouseDown(msg, dynamic_cast<BView*>(handler));
if ((msg->what == B_MOUSE_MOVED)
&& (handler == fBackground || handler == fVideoView
|| handler == fControls))
MouseMoved(msg, dynamic_cast<BView*>(handler));
if ((msg->what == B_MOUSE_UP)
&& (handler == fBackground || handler == fVideoView))
MouseUp(msg);
if ((msg->what == B_KEY_DOWN) && (handler == fBackground || handler == fVideoView)) {
if ((msg->what == B_KEY_DOWN)
&& (handler == fBackground || handler == fVideoView)) {
// special case for PrintScreen key
if (msg->FindInt32("key") == B_PRINT_KEY) {
@ -969,6 +1157,75 @@ MainWin::MessageReceived(BMessage *msg)
if (msg->HasRef("refs"))
RefsReceived(msg);
break;
// PlaylistObserver messages
case MSG_PLAYLIST_REF_ADDED: {
entry_ref ref;
int32 index;
if (msg->FindRef("refs", &ref) == B_OK
&& msg->FindInt32("index", &index) == B_OK) {
_AddPlaylistItem(ref, index);
}
break;
}
case MSG_PLAYLIST_REF_REMOVED: {
int32 index;
if (msg->FindInt32("index", &index) == B_OK) {
_RemovePlaylistItem(index);
}
break;
}
case MSG_PLAYLIST_CURRENT_REF_CHANGED: {
entry_ref ref;
int32 index = fPlaylist->CurrentRefIndex();
if (fPlaylist->GetRefAt(index, &ref) == B_OK) {
printf("open ref: %s\n", ref.name);
OpenFile(ref);
_MarkPlaylistItem(index);
}
break;
}
// ControllerObserver messages
case MSG_CONTROLLER_FILE_FINISHED:
fPlaylist->SetCurrentRefIndex(fPlaylist->CurrentRefIndex() + 1);
break;
case MSG_CONTROLLER_FILE_CHANGED:
// TODO: move all other GUI changes as a reaction to this notification
// _UpdatePlaylistMenu();
break;
case MSG_CONTROLLER_VIDEO_TRACK_CHANGED: {
int32 index;
if (msg->FindInt32("index", &index) == B_OK) {
BMenuItem* item = fVideoTrackMenu->ItemAt(index);
if (item)
item->SetMarked(true);
}
break;
}
case MSG_CONTROLLER_AUDIO_TRACK_CHANGED: {
int32 index;
if (msg->FindInt32("index", &index) == B_OK) {
BMenuItem* item = fAudioTrackMenu->ItemAt(index);
if (item)
item->SetMarked(true);
}
break;
}
case MSG_CONTROLLER_PLAYBACK_STATE_CHANGED: {
uint32 state;
if (msg->FindInt32("state", (int32*)&state) == B_OK)
fControls->SetPlaybackState(state);
break;
}
case MSG_CONTROLLER_POSITION_CHANGED: {
float position;
if (msg->FindFloat("position", &position) == B_OK)
fControls->SetPosition(position);
break;
}
// menu item messages
case M_FILE_NEWPLAYER:
gMainApp->NewWindow();
break;
@ -983,6 +1240,9 @@ MainWin::MessageReceived(BMessage *msg)
case M_FILE_INFO:
ShowFileInfo();
break;
case M_FILE_PLAYLIST:
ShowPlaylistWindow();
break;
case M_FILE_ABOUT:
BAlert *alert;
alert = new BAlert("about", NAME"\n\n Written by Marcus Overhagen","Thanks");
@ -1176,8 +1436,39 @@ MainWin::MessageReceived(BMessage *msg)
break;
}
*/
case M_SET_PLAYLIST_POSITION: {
int32 index;
if (msg->FindInt32("index", &index) == B_OK)
fPlaylist->SetCurrentRefIndex(index);
break;
}
default:
// let BWindow handle the rest
BWindow::MessageReceived(msg);
}
}
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);
}
}

View File

@ -2,6 +2,7 @@
* MainWin.h - Media Player for the Haiku Operating System
*
* Copyright (C) 2006 Marcus Overhagen <marcus@overhagen.de>
* Copyright (C) 2007 Stephan Aßmus <superstippi@gmx.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -29,11 +30,13 @@
#include "ControllerView.h"
#include "InfoWin.h"
#include "VideoView.h"
#include "Player.h"
#include "Playlist.h"
class MainWin : public BWindow, public Player
{
class ControllerObserver;
class PlaylistObserver;
class PlaylistWindow;
class MainWin : public BWindow {
public:
MainWin();
~MainWin();
@ -45,8 +48,8 @@ public:
void MessageReceived(BMessage *msg);
bool QuitRequested();
void MouseDown(BMessage *msg);
void MouseMoved(BMessage *msg);
void MouseDown(BMessage *msg, BView* originalHandler);
void MouseMoved(BMessage *msg, BView* originalHandler);
void MouseUp(BMessage *msg);
status_t KeyDown(BMessage *msg);
@ -58,9 +61,9 @@ public:
void ResizeVideoView(int x, int y, int width, int height);
void ShowFileInfo();
void ShowPlaylistWindow();
void MaybeUpdateFileInfo(uint32 which=INFO_ALL);
// from Player
void OpenFile(const entry_ref &ref);
void VideoFormatChange(int width, int height, float width_scale, float height_scale);
@ -74,27 +77,39 @@ public:
void ToggleNoBorderNoMenu();
void ShowContextMenu(const BPoint &screen_point);
void UpdateControlsEnabledStatus();
void _UpdatePlaylistMenu();
void _AddPlaylistItem(const entry_ref& ref, int32 index);
void _RemovePlaylistItem(int32 index);
void _MarkPlaylistItem(int32 index);
BMenuBar * fMenuBar;
BView * fBackground;
VideoView * fVideoView;
BFilePanel * fFilePanel;
ControllerView * fControls;
InfoWin * fInfoWin;
PlaylistWindow* fPlaylistWindow;
bool fInfoWinShowing;
BMenu * fFileMenu;
BMenu * fViewMenu;
BMenu * fAudioMenu;
BMenu * fVideoMenu;
BMenu * fAudioTrackMenu;
BMenu * fVideoTrackMenu;
BMenu * fSettingsMenu;
BMenu * fPlaylistMenu;
BMenu * fDebugMenu;
bool fHasFile;
bool fHasVideo;
bool fHasAudio;
Playlist * fPlaylist;
PlaylistObserver* fPlaylistObserver;
Controller * fController;
ControllerObserver* fControllerObserver;
volatile bool fIsFullscreen;
volatile bool fKeepAspectRatio;
volatile bool fAlwaysOnTop;
@ -105,6 +120,7 @@ public:
int fSourceHeight;
float fWidthScale;
float fHeightScale;
int fMenuBarWidth;
int fMenuBarHeight;
int fControlsHeight;
int fControlsWidth;
@ -112,6 +128,10 @@ public:
bool fMouseDownTracking;
BPoint fMouseDownMousePos;
BPoint fMouseDownWindowPos;
private:
void _AppendToPlaylist(const entry_ref& ref,
Playlist* playlist);
};
#endif

View File

@ -0,0 +1,17 @@
/*
* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef PLAYBACK_STATE_H
#define PLAYBACK_STATE_H
enum {
PLAYBACK_STATE_STOPPED = 0,
PLAYBACK_STATE_PLAYING = 1,
PLAYBACK_STATE_PAUSED = 2,
};
#endif // PLAYBACK_STATE_H

View File

@ -1,10 +0,0 @@
#ifndef __PLAYER_H
#define __PLAYER_H
class Player
{
public:
virtual void OpenFile(const entry_ref &ref) = 0;
};
#endif

View File

@ -2,6 +2,7 @@
* Playlist.cpp - Media Player for the Haiku Operating System
*
* Copyright (C) 2006 Marcus Overhagen <marcus@overhagen.de>
* Copyright (C) 2007 Stephan Aßmus <superstippi@gmx.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -17,14 +18,31 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "Playlist.h"
#include <debugger.h>
#include <new.h>
#include <string.h>
#include "Playlist.h"
#include <Path.h>
using std::nothrow;
// TODO: using BList for objects is bad, replace it with a template
Playlist::Listener::Listener() {}
Playlist::Listener::~Listener() {}
void Playlist::Listener::RefAdded(const entry_ref& ref, int32 index) {}
void Playlist::Listener::RefRemoved(int32 index) {}
void Playlist::Listener::RefsSorted() {}
void Playlist::Listener::CurrentRefChanged(int32 newIndex) {}
// #pragma mark -
Playlist::Playlist()
: fList()
: fRefs()
, fCurrentIndex(-1)
{
}
@ -33,67 +51,274 @@ Playlist::Playlist()
Playlist::~Playlist()
{
MakeEmpty();
if (fListeners.CountItems() > 0)
debugger("Playlist::~Playlist() - there are still listeners attached!");
}
void
Playlist::MakeEmpty()
{
int count = fList.CountItems();
for (int i = count - 1; i >= 0; i--) {
entry_ref *ref = (entry_ref *)fList.RemoveItem(i);
int32 count = fRefs.CountItems();
for (int32 i = count - 1; i >= 0; i--) {
entry_ref* ref = (entry_ref*)fRefs.RemoveItem(i);
_NotifyRefRemoved(i);
delete ref;
}
fCurrentIndex = -1;
SetCurrentRefIndex(-1);
}
int
Playlist::CountItems()
int32
Playlist::CountItems() const
{
return fList.CountItems();
}
int
Playlist::playlist_cmp(const void *p1, const void *p2)
{
return strcasecmp((*(const entry_ref **)p1)->name, (*(const entry_ref **)p2)->name);
return fRefs.CountItems();
}
void
Playlist::Sort()
{
fList.SortItems(playlist_cmp);
fRefs.SortItems(playlist_cmp);
_NotifyRefsSorted();
}
bool
Playlist::AddRef(const entry_ref &ref)
{
return AddRef(ref, CountItems());
}
bool
Playlist::AddRef(const entry_ref &ref, int32 index)
{
entry_ref* copy = new (nothrow) entry_ref(ref);
if (!copy)
return false;
if (!fRefs.AddItem(copy, index)) {
delete copy;
return false;
}
_NotifyRefAdded(ref, index);
return true;
}
bool
Playlist::AdoptPlaylist(Playlist& other)
{
return AdoptPlaylist(other, CountItems());
}
bool
Playlist::AdoptPlaylist(Playlist& other, int32 index)
{
if (&other == this)
return false;
// NOTE: this is not intended to merge two "equal" playlists
// the given playlist is assumed to be a temporary "dummy"
if (fRefs.AddList(&other.fRefs, index)) {
// take care of the notifications
int32 count = other.fRefs.CountItems();
for (int32 i = index; i < index + count; i++) {
entry_ref* ref = (entry_ref*)fRefs.ItemAtFast(i);
_NotifyRefAdded(*ref, i);
}
// empty the other list, so that the entry_refs are no ours
other.fRefs.MakeEmpty();
return true;
}
return false;
}
entry_ref
Playlist::RemoveRef(int32 index)
{
entry_ref _ref;
entry_ref* ref = (entry_ref*)fRefs.RemoveItem(index);
if (!ref)
return _ref;
_NotifyRefRemoved(index);
_ref = *ref;
delete ref;
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;
//}
status_t
Playlist::GetRefAt(int32 index, entry_ref* _ref) const
{
if (!_ref)
return B_BAD_VALUE;
entry_ref* ref = (entry_ref*)fRefs.ItemAt(index);
if (!ref)
return B_BAD_INDEX;
*_ref = *ref;
return B_OK;
}
//bool
//Playlist::HasRef(const entry_ref& ref) const
//{
// return IndexOf(ref) >= 0;
//}
// #pragma mark -
//status_t
//Playlist::NextRef(entry_ref* ref)
//{
// int count = fRefs.CountItems();
// if (fCurrentIndex + 2 > count)
// return B_ERROR;
// *ref = *(entry_ref*)fRefs.ItemAt(++fCurrentIndex);
// return B_OK;
//}
//
//
//status_t
//Playlist::PrevRef(entry_ref* ref)
//{
// int count = fRefs.CountItems();
// if (count == 0 || fCurrentIndex <= 0)
// return B_ERROR;
// *ref = *(entry_ref*)fRefs.ItemAt(--fCurrentIndex);
// return B_OK;
//}
void
Playlist::SetCurrentRefIndex(int32 index)
{
if (index == fCurrentIndex)
return;
fCurrentIndex = index;
_NotifyCurrentRefChanged(fCurrentIndex);
}
int32
Playlist::CurrentRefIndex() const
{
return fCurrentIndex;
}
void
Playlist::AddRef(const entry_ref &ref)
Playlist::GetSkipInfo(bool* canSkipPrevious, bool* canSkipNext) const
{
fList.AddItem(new entry_ref(ref));
if (canSkipPrevious)
*canSkipPrevious = fCurrentIndex > 0;
if (canSkipNext)
*canSkipNext = fCurrentIndex < CountItems() - 1;
}
status_t
Playlist::NextRef(entry_ref *ref)
// pragma mark -
bool
Playlist::AddListener(Listener* listener)
{
int count = fList.CountItems();
if (fCurrentIndex + 2 > count)
return B_ERROR;
*ref = *(entry_ref *)fList.ItemAt(++fCurrentIndex);
return B_OK;
if (listener && !fListeners.HasItem(listener))
return fListeners.AddItem(listener);
return false;
}
status_t
Playlist::PrevRef(entry_ref *ref)
void
Playlist::RemoveListener(Listener* listener)
{
int count = fList.CountItems();
if (count == 0 || fCurrentIndex <= 0)
return B_ERROR;
*ref = *(entry_ref *)fList.ItemAt(--fCurrentIndex);
return B_OK;
fListeners.RemoveItem(listener);
}
// pragma mark -
int
Playlist::playlist_cmp(const void *p1, const void *p2)
{
// compare complete path
BEntry a(*(const entry_ref **)p1, false);
BEntry b(*(const entry_ref **)p2, false);
BPath aPath(&a);
BPath bPath(&b);
return strcmp(aPath.Path(), bPath.Path());
}
void
Playlist::_NotifyRefAdded(const entry_ref& ref, int32 index) const
{
BList listeners(fListeners);
int32 count = listeners.CountItems();
for (int32 i = 0; i < count; i++) {
Listener* listener = (Listener*)listeners.ItemAtFast(i);
listener->RefAdded(ref, index);
}
}
void
Playlist::_NotifyRefRemoved(int32 index) const
{
BList listeners(fListeners);
int32 count = listeners.CountItems();
for (int32 i = 0; i < count; i++) {
Listener* listener = (Listener*)listeners.ItemAtFast(i);
listener->RefRemoved(index);
}
}
void
Playlist::_NotifyRefsSorted() const
{
BList listeners(fListeners);
int32 count = listeners.CountItems();
for (int32 i = 0; i < count; i++) {
Listener* listener = (Listener*)listeners.ItemAtFast(i);
listener->RefsSorted();
}
}
void
Playlist::_NotifyCurrentRefChanged(int32 newIndex) const
{
BList listeners(fListeners);
int32 count = listeners.CountItems();
for (int32 i = 0; i < count; i++) {
Listener* listener = (Listener*)listeners.ItemAtFast(i);
listener->CurrentRefChanged(newIndex);
}
}

View File

@ -2,6 +2,7 @@
* Playlist.h - Media Player for the Haiku Operating System
*
* Copyright (C) 2006 Marcus Overhagen <marcus@overhagen.de>
* Copyright (C) 2007 Stephan Aßmus <superstippi@gmx.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -23,28 +24,71 @@
#include <Entry.h>
#include <List.h>
class Playlist
{
class Playlist {
public:
Playlist();
~Playlist();
class Listener {
public:
Listener();
virtual ~Listener();
virtual void RefAdded(const entry_ref& ref, int32 index);
virtual void RefRemoved(int32 index);
virtual void RefsSorted();
virtual void CurrentRefChanged(int32 newIndex);
};
public:
Playlist();
~Playlist();
void MakeEmpty();
int CountItems();
// list functionality
void MakeEmpty();
int32 CountItems() const;
void Sort();
void AddRef(const entry_ref &ref);
void Sort();
status_t NextRef(entry_ref *ref);
status_t PrevRef(entry_ref *ref);
bool AddRef(const entry_ref& ref);
bool AddRef(const entry_ref& ref, int32 index);
entry_ref RemoveRef(int32 index);
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
// TODO: replace with SetCurrentRef() and listener
// status_t NextRef(entry_ref* ref);
// status_t PrevRef(entry_ref* 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);
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;
private:
BList fList;
int fCurrentIndex;
BList fRefs;
BList fListeners;
int32 fCurrentIndex;
};
#endif

View File

@ -0,0 +1,64 @@
/*
* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "PlaylistObserver.h"
#include <Message.h>
PlaylistObserver::PlaylistObserver(BHandler* target)
: Playlist::Listener()
, AbstractLOAdapter(target)
{
}
PlaylistObserver::~PlaylistObserver()
{
}
void
PlaylistObserver::RefAdded(const entry_ref& ref, int32 index)
{
BMessage message(MSG_PLAYLIST_REF_ADDED);
message.AddRef("refs", &ref);
message.AddInt32("index", index);
DeliverMessage(message);
}
void
PlaylistObserver::RefRemoved(int32 index)
{
BMessage message(MSG_PLAYLIST_REF_REMOVED);
message.AddInt32("index", index);
DeliverMessage(message);
}
void
PlaylistObserver::RefsSorted()
{
BMessage message(MSG_PLAYLIST_REFS_SORTED);
DeliverMessage(message);
}
void
PlaylistObserver::CurrentRefChanged(int32 newIndex)
{
BMessage message(MSG_PLAYLIST_CURRENT_REF_CHANGED);
message.AddInt32("index", newIndex);
DeliverMessage(message);
}

View File

@ -0,0 +1,35 @@
/*
* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef PLAYLIST_OBSERVER_H
#define PLAYLIST_OBSERVER_H
#include "AbstractLOAdapter.h"
#include "Playlist.h"
enum {
MSG_PLAYLIST_REF_ADDED = 'plra',
MSG_PLAYLIST_REF_REMOVED = 'plrr',
MSG_PLAYLIST_REFS_SORTED = 'plrs',
MSG_PLAYLIST_CURRENT_REF_CHANGED = 'plcc'
};
class PlaylistObserver : public Playlist::Listener,
public AbstractLOAdapter {
public:
PlaylistObserver(BHandler* target);
virtual ~PlaylistObserver();
virtual void RefAdded(const entry_ref& ref, int32 index);
virtual void RefRemoved(int32 index);
virtual void RefsSorted();
virtual void CurrentRefChanged(int32 newIndex);
};
#endif // PLAYLIST_OBSERVER_H

View File

@ -0,0 +1,190 @@
/*
* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "PlaylistWindow.h"
#include <ScrollBar.h>
#include <ScrollView.h>
#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;
};
// #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)
: BWindow(frame, "Playlist", B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS)
{
frame = Bounds();
frame.right -= B_V_SCROLL_BAR_WIDTH;
PlaylistListView* listView = new PlaylistListView(frame, playlist);
fTopView = new BScrollView("playlist scrollview",
listView, B_FOLLOW_ALL, 0, false, true, B_NO_BORDER);
AddChild(fTopView);
}
PlaylistWindow::~PlaylistWindow()
{
// give listeners a chance to detach themselves
fTopView->RemoveSelf();
delete fTopView;
}
bool
PlaylistWindow::QuitRequested()
{
Hide();
return false;
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef PLAYLIST_WINDOW_H
#define PLAYLIST_WINDOW_H
#include <Window.h>
class Playlist;
class PlaylistWindow : public BWindow {
public:
PlaylistWindow(BRect frame,
Playlist* playlist);
virtual ~PlaylistWindow();
virtual bool QuitRequested();
private:
BView* fTopView;
};
#endif // PLAYLIST_WINDOW_H

View File

@ -32,11 +32,12 @@ const char* kDisabledSeekMessage = "Drop files to play";
SeekSlider::SeekSlider(BRect frame, const char* name, BMessage* message,
int32 minValue, int32 maxValue)
: BControl(frame, name, NULL, message, B_FOLLOW_NONE,
B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
fTracking(false),
fKnobPos(_KnobPosFor(Bounds(), Value())),
fMinValue(minValue),
fMaxValue(maxValue)
B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE)
, fTracking(false)
, fLastTrackTime(0)
, fKnobPos(_KnobPosFor(Bounds(), Value()))
, fMinValue(minValue)
, fMaxValue(maxValue)
{
BFont font(be_plain_font);
font.SetSize(9.0);
@ -67,26 +68,13 @@ SeekSlider::SetValue(int32 value)
BControl::SetValueNoUpdate(value);
#else
BControl::SetValue(value);
// this will Invalidate() the whole view
#endif
Invoke();
#if __HAIKU__
int32 oldKnobPos = fKnobPos;
fKnobPos = _KnobPosFor(Bounds(), Value());
// invalidate
if (oldKnobPos != fKnobPos) {
float knobWidth2 = SEEK_SLIDER_KNOB_WIDTH / 2.0;
BRect oldKnob(Bounds());
BRect newKnob(oldKnob);
oldKnob.left = oldKnobPos - knobWidth2;
oldKnob.right = oldKnobPos + knobWidth2;
newKnob.left = fKnobPos - knobWidth2;
newKnob.right = fKnobPos + knobWidth2;
Invalidate(oldKnob | newKnob);
}
#else
fKnobPos = _KnobPosFor(Bounds(), Value());
#endif
_SetKnobPosition(_KnobPosFor(Bounds(), Value()));
fLastTrackTime = system_time();
}
@ -138,7 +126,7 @@ SeekSlider::Draw(BRect updateRect)
float sliderStart = (r.left + knobWidth2);
for (int32 i = 0; i < dotCount; i++) {
dotPos.x = sliderStart + i * 6.0 + 5.0;
dotPos.x = sliderStart + i * 6.0;
StrokeLine(dotPos, BPoint(dotPos.x, dotPos.y + 6.0));
}
// slider handle
@ -298,11 +286,20 @@ SeekSlider::SetPosition(float position)
int32 value = fMinValue + (int32)floorf((fMaxValue - fMinValue) * position + 0.5);
if (value != Value()) {
BControl::SetValue(value);
_SetKnobPosition(_KnobPosFor(Bounds(), Value()));
}
}
bool
SeekSlider::IsTracking() const
{
if (fTracking)
return true;
return system_time() - fLastTrackTime < 250000;
}
// #pragma mark -
@ -349,5 +346,22 @@ SeekSlider::_StrokeFrame(BRect r, rgb_color left, rgb_color top,
}
void
SeekSlider::_SetKnobPosition(int32 knobPos)
{
if (fKnobPos == knobPos)
return;
float knobWidth2 = SEEK_SLIDER_KNOB_WIDTH / 2.0;
BRect oldKnob(Bounds());
BRect newKnob(oldKnob);
oldKnob.left = fKnobPos - knobWidth2;
oldKnob.right = fKnobPos + knobWidth2;
fKnobPos = knobPos;
newKnob.left = fKnobPos - knobWidth2;
newKnob.right = fKnobPos + knobWidth2;
Invalidate(oldKnob | newKnob);
}

View File

@ -34,6 +34,7 @@ class SeekSlider : public BControl {
// SeekSlider
void SetPosition(float position);
bool IsTracking() const;
private:
int32 _ValueFor(float x) const;
@ -44,8 +45,11 @@ private:
rgb_color top,
rgb_color right,
rgb_color bottom);
void _SetKnobPosition(int32 knobPos);
bool fTracking;
bigtime_t fLastTrackTime;
int32 fKnobPos;
int32 fMinValue;
int32 fMaxValue;

View File

@ -61,7 +61,14 @@ SoundOutput::InitCheck()
return B_ERROR;
return fSoundPlayer->InitCheck();
}
media_raw_audio_format
SoundOutput::Format() const
{
return fSoundPlayer->Format();
}
bigtime_t
SoundOutput::Latency()

View File

@ -32,13 +32,15 @@ public:
status_t InitCheck();
media_raw_audio_format Format() const;
bigtime_t Latency();
float Volume();
void SetVolume(float new_volume);
void Play(const void *data, size_t size);
private:
static void play_buffer(void *cookie, void *buffer, size_t size, const media_raw_audio_format & format);
void PlayBuffer(void *buffer);

View File

@ -18,8 +18,9 @@
#include <String.h>
#include "ButtonBitmaps.h"
#include "TransportButton.h"
#include "PlaybackState.h"
#include "SeekSlider.h"
#include "TransportButton.h"
#include "VolumeSlider.h"
enum {
@ -51,11 +52,12 @@ enum {
#define kPositionFactor 3000
// constructor
TransportControlGroup::TransportControlGroup(BRect frame)
TransportControlGroup::TransportControlGroup(BRect frame, bool useSkipButtons,
bool useWindButtons)
: BView(frame, "transport control group",
B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP,
B_WILL_DRAW | B_FRAME_EVENTS),
fBottomControlHeight(0.0)
B_WILL_DRAW | B_FRAME_EVENTS)
, fBottomControlHeight(0.0)
{
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
@ -69,40 +71,50 @@ TransportControlGroup::TransportControlGroup(BRect frame)
AddChild(fSeekSlider);
// Buttons
// Skip Back
frame.right = kRewindBitmapWidth - 1;
frame.bottom = kRewindBitmapHeight - 1;
fBottomControlHeight = kRewindBitmapHeight - 1.0;
fSkipBack = new TransportButton(frame, B_EMPTY_STRING,
kSkipBackBitmapBits,
kPressedSkipBackBitmapBits,
kDisabledSkipBackBitmapBits,
new BMessage(MSG_SKIP_BACKWARDS));
AddChild(fSkipBack);
if (useSkipButtons) {
// Skip Back
frame.right = kRewindBitmapWidth - 1;
frame.bottom = kRewindBitmapHeight - 1;
fBottomControlHeight = kRewindBitmapHeight - 1.0;
fSkipBack = new TransportButton(frame, B_EMPTY_STRING,
kSkipBackBitmapBits,
kPressedSkipBackBitmapBits,
kDisabledSkipBackBitmapBits,
new BMessage(MSG_SKIP_BACKWARDS));
AddChild(fSkipBack);
// Skip Foward
fSkipForward = new TransportButton(frame, B_EMPTY_STRING,
kSkipForwardBitmapBits,
kPressedSkipForwardBitmapBits,
kDisabledSkipForwardBitmapBits,
new BMessage(MSG_SKIP_FORWARD));
AddChild(fSkipForward);
} else {
fSkipBack = NULL;
fSkipForward = NULL;
}
// Skip Foward
fSkipForward = new TransportButton(frame, B_EMPTY_STRING,
kSkipForwardBitmapBits,
kPressedSkipForwardBitmapBits,
kDisabledSkipForwardBitmapBits,
new BMessage(MSG_SKIP_FORWARD));
AddChild(fSkipForward);
// Forward
fForward = new TransportButton(frame, B_EMPTY_STRING,
kForwardBitmapBits,
kPressedForwardBitmapBits,
kDisabledForwardBitmapBits,
new BMessage(MSG_FORWARD));
AddChild(fForward);
// Rewind
fRewind = new TransportButton(frame, B_EMPTY_STRING,
kRewindBitmapBits,
kPressedRewindBitmapBits,
kDisabledRewindBitmapBits,
new BMessage(MSG_REWIND));
AddChild(fRewind);
if (useWindButtons) {
// Forward
fForward = new TransportButton(frame, B_EMPTY_STRING,
kForwardBitmapBits,
kPressedForwardBitmapBits,
kDisabledForwardBitmapBits,
new BMessage(MSG_FORWARD));
AddChild(fForward);
// Rewind
fRewind = new TransportButton(frame, B_EMPTY_STRING,
kRewindBitmapBits,
kPressedRewindBitmapBits,
kDisabledRewindBitmapBits,
new BMessage(MSG_REWIND));
AddChild(fRewind);
} else {
fForward = NULL;
fRewind = NULL;
}
// Play Pause
frame.right = kPlayPauseBitmapWidth - 1;
@ -171,10 +183,14 @@ TransportControlGroup::AttachedToWindow()
// we are now a valid BHandler
fSeekSlider->SetTarget(this);
fVolumeSlider->SetTarget(this);
fSkipBack->SetTarget(this);
fSkipForward->SetTarget(this);
fRewind->SetTarget(this);
fForward->SetTarget(this);
if (fSkipBack)
fSkipBack->SetTarget(this);
if (fSkipForward)
fSkipForward->SetTarget(this);
if (fRewind)
fRewind->SetTarget(this);
if (fForward)
fForward->SetTarget(this);
fPlayPause->SetTarget(this);
fStop->SetTarget(this);
fMute->SetTarget(this);
@ -320,21 +336,26 @@ TransportControlGroup::_GainToDb(float gain)
void
TransportControlGroup::SetEnabled(uint32 buttons)
{
// if (B_OK != LockLooperWithTimeout(50000))
if (!LockLooper())
return;
fSeekSlider->SetEnabled(buttons & SEEK_ENABLED);
fVolumeSlider->SetEnabled(buttons & VOLUME_ENABLED);
fMute->SetEnabled(buttons & VOLUME_ENABLED);
fSkipBack->SetEnabled(buttons & SKIP_BACK_ENABLED);
fSkipForward->SetEnabled(buttons & SKIP_FORWARD_ENABLED);
fRewind->SetEnabled(buttons & SEEK_BACK_ENABLED);
fForward->SetEnabled(buttons & SEEK_FORWARD_ENABLED);
if (fSkipBack)
fSkipBack->SetEnabled(buttons & SKIP_BACK_ENABLED);
if (fSkipForward)
fSkipForward->SetEnabled(buttons & SKIP_FORWARD_ENABLED);
if (fRewind)
fRewind->SetEnabled(buttons & SEEK_BACK_ENABLED);
if (fForward)
fForward->SetEnabled(buttons & SEEK_FORWARD_ENABLED);
fPlayPause->SetEnabled(buttons & PLAYBACK_ENABLED);
fStop->SetEnabled(buttons & PLAYBACK_ENABLED);
UnlockLooper();
}
@ -344,9 +365,9 @@ TransportControlGroup::SetEnabled(uint32 buttons)
void
TransportControlGroup::SetPlaybackState(uint32 state)
{
// if (B_OK != LockLooperWithTimeout(50000))
if (!LockLooper())
return;
switch (state) {
case PLAYBACK_STATE_PLAYING:
fPlayPause->SetPlaying();
@ -358,6 +379,7 @@ TransportControlGroup::SetPlaybackState(uint32 state)
fPlayPause->SetStopped();
break;
}
UnlockLooper();
}
@ -365,11 +387,14 @@ TransportControlGroup::SetPlaybackState(uint32 state)
void
TransportControlGroup::SetSkippable(bool backward, bool forward)
{
// if (B_OK != LockLooperWithTimeout(50000))
if (!LockLooper())
return;
fSkipBack->SetEnabled(backward);
fSkipForward->SetEnabled(forward);
if (fSkipBack)
fSkipBack->SetEnabled(backward);
if (fSkipForward)
fSkipForward->SetEnabled(forward);
UnlockLooper();
}
@ -379,11 +404,12 @@ TransportControlGroup::SetSkippable(bool backward, bool forward)
void
TransportControlGroup::SetAudioEnabled(bool enabled)
{
// if (B_OK != LockLooperWithTimeout(50000))
if (!LockLooper())
return;
fMute->SetEnabled(enabled);
fVolumeSlider->SetEnabled(enabled);
UnlockLooper();
}
@ -391,10 +417,11 @@ TransportControlGroup::SetAudioEnabled(bool enabled)
void
TransportControlGroup::SetMuted(bool mute)
{
// if (B_OK != LockLooperWithTimeout(50000))
if (!LockLooper())
return;
fVolumeSlider->SetMuted(mute);
UnlockLooper();
}
@ -404,7 +431,10 @@ TransportControlGroup::SetVolume(float value)
{
if (B_OK != LockLooperWithTimeout(50000))
return;
fVolumeSlider->SetValue(_DbToGain(_ExponentialToLinear(_GainToDb(value))) * kVolumeFactor);
fVolumeSlider->SetValue(_DbToGain(_ExponentialToLinear(
_GainToDb(value))) * kVolumeFactor);
UnlockLooper();
}
@ -412,10 +442,10 @@ TransportControlGroup::SetVolume(float value)
void
TransportControlGroup::SetPosition(float value)
{
if (B_OK != LockLooperWithTimeout(50000))
if (fSeekSlider->IsTracking())
return;
fSeekSlider->SetPosition(value);
UnlockLooper();
}
@ -427,12 +457,17 @@ TransportControlGroup::_LayoutControls(BRect frame) const
{
BRect r(frame);
// calculate absolutly minimal width
float minWidth = fSkipBack->Bounds().Width();
minWidth += fRewind->Bounds().Width();
float minWidth = 0.0;
if (fSkipBack)
minWidth += fSkipBack->Bounds().Width();
if (fRewind)
minWidth += fRewind->Bounds().Width();
minWidth += fStop->Bounds().Width();
minWidth += fPlayPause->Bounds().Width();
minWidth += fForward->Bounds().Width();
minWidth += fSkipForward->Bounds().Width();
if (fForward)
minWidth += fForward->Bounds().Width();
if (fSkipForward)
minWidth += fSkipForward->Bounds().Width();
minWidth += fMute->Bounds().Width();
minWidth += VOLUME_MIN_WIDTH;
@ -449,28 +484,37 @@ TransportControlGroup::_LayoutControls(BRect frame) const
r.top = r.bottom + MIN_SPACE + 1.0;
r.bottom = frame.bottom;
// skip back
r.right = r.left + fSkipBack->Bounds().Width();
_LayoutControl(fSkipBack, r);
if (fSkipBack) {
r.right = r.left + fSkipBack->Bounds().Width();
_LayoutControl(fSkipBack, r);
r.left = r.right + space;
}
// rewind
r.left = r.right + space;
r.right = r.left + fRewind->Bounds().Width();
_LayoutControl(fRewind, r);
if (fRewind) {
r.right = r.left + fRewind->Bounds().Width();
_LayoutControl(fRewind, r);
r.left = r.right + space;
}
// stop
r.left = r.right + space;
r.right = r.left + fStop->Bounds().Width();
_LayoutControl(fStop, r);
// play/pause
r.left = r.right + space;
// play/pause
r.right = r.left + fPlayPause->Bounds().Width();
_LayoutControl(fPlayPause, r);
r.left = r.right + space;
// forward
r.left = r.right + space;
r.right = r.left + fForward->Bounds().Width();
_LayoutControl(fForward, r);
if (fForward) {
r.right = r.left + fForward->Bounds().Width();
_LayoutControl(fForward, r);
r.left = r.right + space;
}
// skip forward
r.left = r.right + space;
r.right = r.left + fSkipForward->Bounds().Width();
_LayoutControl(fSkipForward, r);
if (fSkipForward) {
r.right = r.left + fSkipForward->Bounds().Width();
_LayoutControl(fSkipForward, r);
r.left = r.right + space;
}
// speaker icon
r.left = r.right + space + space;
r.right = r.left + fMute->Bounds().Width();
@ -488,12 +532,16 @@ TransportControlGroup::_MinFrame() const
{
// add up width of controls along bottom (seek slider will likely adopt)
float minWidth = 2 * BORDER_INSET;
minWidth += fSkipBack->Bounds().Width() + MIN_SPACE;
minWidth += fRewind->Bounds().Width() + MIN_SPACE;
if (fSkipBack)
minWidth += fSkipBack->Bounds().Width() + MIN_SPACE;
if (fRewind)
minWidth += fRewind->Bounds().Width() + MIN_SPACE;
minWidth += fStop->Bounds().Width() + MIN_SPACE;
minWidth += fPlayPause->Bounds().Width() + MIN_SPACE;
minWidth += fForward->Bounds().Width() + MIN_SPACE;
minWidth += fSkipForward->Bounds().Width() + MIN_SPACE + MIN_SPACE;
if (fForward)
minWidth += fForward->Bounds().Width() + MIN_SPACE;
if (fSkipForward)
minWidth += fSkipForward->Bounds().Width() + MIN_SPACE + MIN_SPACE;
minWidth += fMute->Bounds().Width() + SPEAKER_SLIDER_DIST;
minWidth += VOLUME_MIN_WIDTH;

View File

@ -7,7 +7,7 @@
*/
// NOTE: Based on my code in the BeOS interface for the VLC media player
// that I did during the VLC 0.4.3 - 0.4.6 times. Code not done by me
// that I did during the VLC 0.4.3 - 0.4.6 times. Code not written by me
// removed. -Stephan Aßmus
#ifndef TRANSPORT_CONTROL_GROUP_H
@ -20,12 +20,6 @@ class TransportButton;
class SeekSlider;
class VolumeSlider;
enum {
PLAYBACK_STATE_PLAYING = 0,
PLAYBACK_STATE_PAUSED,
PLAYBACK_STATE_STOPPED,
};
enum {
SKIP_BACK_ENABLED = 1 << 0,
SEEK_BACK_ENABLED = 1 << 1,
@ -38,7 +32,9 @@ enum {
class TransportControlGroup : public BView {
public:
TransportControlGroup(BRect frame);
TransportControlGroup(BRect frame,
bool useSkipButtons = true,
bool useWindButtons = false);
virtual ~TransportControlGroup();
// BView interface
@ -106,7 +102,6 @@ class TransportControlGroup : public BView {
TransportButton* fStop;
TransportButton* fMute;
int fCurrentStatus;
float fBottomControlHeight;
};

View File

@ -161,37 +161,54 @@ VideoView::DrawFrame()
{
// printf("VideoView::DrawFrame\n");
bool want_overlay = fController->IsOverlayActive();
if (LockLooperWithTimeout(50000) != B_OK)
return;
if (!want_overlay && fOverlayActive) {
if (LockLooperWithTimeout(50000) == B_OK) {
ClearViewOverlay();
UnlockLooper();
fOverlayActive = false;
fController->LockBitmap();
BBitmap *bmp = fController->Bitmap();
if (bmp) {
bool want_overlay = bmp->ColorSpace() == B_YCbCr422;
if (!want_overlay && fOverlayActive) {
if (LockLooperWithTimeout(50000) == B_OK) {
ClearViewOverlay();
UnlockLooper();
fOverlayActive = false;
} else {
printf("can't ClearViewOverlay, as LockLooperWithTimeout failed\n");
return;
}
}
if (want_overlay && !fOverlayActive ) {
printf("trying to activate overlay...");
// reserve overlay channel
status_t ret = SetViewOverlay(bmp, bmp->Bounds(), Bounds(),
&fOverlayKeyColor, B_FOLLOW_ALL,
B_OVERLAY_FILTER_HORIZONTAL | B_OVERLAY_FILTER_VERTICAL);
if (ret == B_OK) {
printf("success\n");
fOverlayActive = true;
Invalidate();
} else {
printf("failed: %s\n", strerror(ret));
}
} else if (fOverlayActive) {
// transfer overlay channel
rgb_color overlayKey;
SetViewOverlay(bmp, bmp->Bounds(), Bounds(), &overlayKey,
B_FOLLOW_ALL, B_OVERLAY_TRANSFER_CHANNEL
| B_OVERLAY_FILTER_HORIZONTAL | B_OVERLAY_FILTER_VERTICAL);
} else {
printf("can't ClearViewOverlay, as LockLooperWithTimeout failed\n");
// no overlay
DrawBitmap(bmp, Bounds());
}
}
if (want_overlay && !fOverlayActive ) {
fController->LockBitmap();
BBitmap *bmp = fController->Bitmap();
if (bmp && LockLooperWithTimeout(50000) == B_OK) {
SetViewOverlay(bmp, bmp->Bounds(), Bounds(), &fOverlayKeyColor,
B_FOLLOW_ALL,
/*B_OVERLAY_TRANSFER_CHANNEL | */B_OVERLAY_FILTER_HORIZONTAL | B_OVERLAY_FILTER_VERTICAL );
fOverlayActive = true;
Invalidate();
UnlockLooper();
}
fController->UnlockBitmap();
}
if (!fOverlayActive) {
if (LockLooperWithTimeout(50000) != B_OK)
return;
Invalidate();
UnlockLooper();
}
fController->UnlockBitmap();
UnlockLooper();
}

View File

@ -51,7 +51,7 @@ private:
private:
Controller * fController;
bool fOverlayActive;
volatile bool fOverlayActive;
rgb_color fOverlayKeyColor;
};

View File

@ -0,0 +1,19 @@
/*
* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "AudioSupplier.h"
AudioSupplier::AudioSupplier()
{
}
AudioSupplier::~AudioSupplier()
{
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef AUDIO_SUPPLIER_H
#define AUDIO_SUPPLIER_H
#include <MediaDefs.h>
class AudioSupplier {
public:
AudioSupplier();
virtual ~AudioSupplier();
virtual media_format Format() const = 0;
virtual status_t ReadFrames(void* buffer, int64* framesRead,
bigtime_t* performanceTime) = 0;
virtual status_t SeekToTime(bigtime_t* performanceTime) = 0;
virtual bigtime_t Position() const = 0;
virtual bigtime_t Duration() const = 0;
};
#endif // AUDIO_SUPPLIER_H

View File

@ -0,0 +1,106 @@
/*
* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "MediaTrackAudioSupplier.h"
#include <new>
#include <stdio.h>
#include <string.h>
#include <MediaTrack.h>
using std::nothrow;
// constructor
MediaTrackAudioSupplier::MediaTrackAudioSupplier(BMediaTrack* track)
: AudioSupplier()
, fMediaTrack(track)
, fPerformanceTime(0)
, fDuration(0)
{
if (!fMediaTrack) {
printf("MediaTrackAudioSupplier() - no media track\n");
return;
}
fFormat.u.raw_audio = media_multi_audio_format::wildcard;
#ifdef __HAIKU__
fFormat.u.raw_audio.format = media_multi_audio_format::B_AUDIO_FLOAT;
#endif
status_t ret = fMediaTrack->DecodedFormat(&fFormat);
if (ret < B_OK) {
printf("MediaTrackAudioSupplier() - "
"fMediaTrack->DecodedFormat(): %s\n", strerror(ret));
return;
}
fDuration = fMediaTrack->Duration();
//
// for (bigtime_t time = 0; time < fDuration; time += 10000) {
// bigtime_t keyFrameTime = time;
// fMediaTrack->FindKeyFrameForTime(&keyFrameTime,
// B_MEDIA_SEEK_CLOSEST_BACKWARD);
// printf("audio keyframe time for time: %lld -> %lld\n", time, keyFrameTime);
// }
}
// destructor
MediaTrackAudioSupplier::~MediaTrackAudioSupplier()
{
}
media_format
MediaTrackAudioSupplier::Format() const
{
return fFormat;
}
status_t
MediaTrackAudioSupplier::ReadFrames(void* buffer, int64* framesRead,
bigtime_t* performanceTime)
{
if (!fMediaTrack)
return B_NO_INIT;
if (!buffer || !framesRead)
return B_BAD_VALUE;
media_header mediaHeader;
status_t ret = fMediaTrack->ReadFrames(buffer, framesRead, &mediaHeader);
if (ret < B_OK) {
printf("MediaTrackAudioSupplier::ReadFrame() - "
"error while reading frames: %s\n", strerror(ret));
} else {
fPerformanceTime = mediaHeader.start_time;
}
if (performanceTime)
*performanceTime = fPerformanceTime;
return ret;
}
status_t
MediaTrackAudioSupplier::SeekToTime(bigtime_t* performanceTime)
{
if (!fMediaTrack)
return B_NO_INIT;
bigtime_t _performanceTime = *performanceTime;
status_t ret = fMediaTrack->SeekToTime(performanceTime);
if (ret == B_OK) {
printf("seeked: %lld -> %lld\n", _performanceTime, *performanceTime);
fPerformanceTime = *performanceTime;
}
return ret;
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef MEDIA_TRACK_AUDIO_SUPPLIER_H
#define MEDIA_TRACK_AUDIO_SUPPLIER_H
#include "AudioSupplier.h"
class BMediaTrack;
class MediaTrackAudioSupplier : public AudioSupplier {
public:
MediaTrackAudioSupplier(BMediaTrack* track);
virtual ~MediaTrackAudioSupplier();
virtual media_format Format() const;
virtual status_t ReadFrames(void* buffer, int64* framesRead,
bigtime_t* performanceTime);
virtual status_t SeekToTime(bigtime_t* performanceTime);
virtual bigtime_t Position() const
{ return fPerformanceTime; }
virtual bigtime_t Duration() const
{ return fDuration; }
private:
BMediaTrack* fMediaTrack;
media_format fFormat;
bigtime_t fPerformanceTime;
bigtime_t fDuration;
};
#endif // MEDIA_TRACK_AUDIO_SUPPLIER_H

View File

@ -0,0 +1,281 @@
/*
* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "MediaTrackVideoSupplier.h"
#include <new>
#include <stdio.h>
#include <string.h>
#include <MediaTrack.h>
using std::nothrow;
#define DEBUG_DECODED_FRAME 0
#if DEBUG_DECODED_FRAME
# include <Bitmap.h>
# include <BitmapStream.h>
# include <File.h>
# include <TranslatorRoster.h>
#endif // DEBUG_DECODED_FRAME
static const char* string_for_color_space(color_space format);
// constructor
MediaTrackVideoSupplier::MediaTrackVideoSupplier(BMediaTrack* track,
color_space format)
: VideoSupplier()
, fVideoTrack(track)
, fPerformanceTime(0)
, fDuration(0)
{
if (!fVideoTrack) {
printf("MediaTrackVideoSupplier() - no video track\n");
return;
}
// get the encoded format
memset(&fFormat, 0, sizeof(media_format));
status_t ret = fVideoTrack->EncodedFormat(&fFormat);
if (ret < B_OK) {
printf("MediaTrackVideoSupplier::InitCheck() - "
"fVideoTrack->EncodedFormat(): %s\n", strerror(ret));
return;
}
// get ouput video frame size
uint32 width = fFormat.u.encoded_video.output.display.line_width;
uint32 height = fFormat.u.encoded_video.output.display.line_count;
// specifiy the decoded format. we derive this information from
// the encoded format (width & height).
memset(&fFormat, 0, sizeof(media_format));
// fFormat.u.raw_video.last_active = height - 1;
// fFormat.u.raw_video.orientation = B_VIDEO_TOP_LEFT_RIGHT;
// fFormat.u.raw_video.pixel_width_aspect = 1;
// fFormat.u.raw_video.pixel_height_aspect = 1;
fFormat.u.raw_video.display.format = format;
fFormat.u.raw_video.display.line_width = width;
fFormat.u.raw_video.display.line_count = height;
if (format == B_RGB32 || format == B_RGBA32)
fFormat.u.raw_video.display.bytes_per_row = width * 4;
else if (format == B_YCbCr422)
fFormat.u.raw_video.display.bytes_per_row = ((width * 2 + 3) / 4) * 4;
ret = fVideoTrack->DecodedFormat(&fFormat);
if (ret < B_OK) {
printf("MediaTrackVideoSupplier() - "
"fVideoTrack->DecodedFormat(): %s\n", strerror(ret));
return;
}
if (fFormat.u.raw_video.display.format != format) {
printf("MediaTrackVideoSupplier() - "
" codec changed colorspace of decoded format (%s -> %s)!\n"
" this is bad for performance, since colorspace conversion\n"
" needs to happen during playback.\n",
string_for_color_space(format),
string_for_color_space(fFormat.u.raw_video.display.format));
// check if the codec forgot to adjust bytes_per_row
uint32 minBPR;
format = fFormat.u.raw_video.display.format;
if (format == B_YCbCr422)
minBPR = ((width * 2 + 3) / 4) * 4;
else
minBPR = width * 4;
if (minBPR != fFormat.u.raw_video.display.bytes_per_row) {
printf(" -> stupid codec forgot to adjust bytes_per_row!\n");
fFormat.u.raw_video.display.bytes_per_row = minBPR;
fVideoTrack->DecodedFormat(&fFormat);
}
}
fDuration = fVideoTrack->Duration();
// for (bigtime_t time = 0; time < fDuration; time += 10000) {
// bigtime_t keyFrameTime = time;
// fVideoTrack->FindKeyFrameForTime(&keyFrameTime,
// B_MEDIA_SEEK_CLOSEST_BACKWARD);
// printf("keyframe time for time: %lld -> %lld\n", time, keyFrameTime);
// }
}
// destructor
MediaTrackVideoSupplier::~MediaTrackVideoSupplier()
{
}
media_format
MediaTrackVideoSupplier::Format() const
{
return fFormat;
}
status_t
MediaTrackVideoSupplier::ReadFrame(void* buffer, bigtime_t* performanceTime)
{
if (!fVideoTrack)
return B_NO_INIT;
if (!buffer)
return B_BAD_VALUE;
// read a frame
int64 frameCount = 1;
// TODO: how does this work for interlaced video (field count > 1)?
media_header mediaHeader;
status_t ret = fVideoTrack->ReadFrames(buffer, &frameCount, &mediaHeader);
if (ret < B_OK) {
printf("MediaTrackVideoSupplier::ReadFrame() - "
"error while reading frame of track: %s\n", strerror(ret));
} else {
fPerformanceTime = mediaHeader.start_time;
}
if (performanceTime)
*performanceTime = fPerformanceTime;
#if DEBUG_DECODED_FRAME
if (modifiers() & B_SHIFT_KEY) {
BFile fileStream("/boot/home/Desktop/decoded.png", B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
BTranslatorRoster* roster = BTranslatorRoster::Default();
BBitmap* bitmap = new BBitmap(Bounds(), 0, ColorSpace(), BytesPerRow());
memcpy(bitmap->Bits(), buffer, bitmap->BitsLength());
BBitmapStream bitmapStream(bitmap);
roster->Translate(&bitmapStream, NULL, NULL, &fileStream, B_PNG_FORMAT, 0);
bitmapStream.DetachBitmap(&bitmap);
delete bitmap;
}
#endif // DEBUG_DECODED_FRAME
return ret;
}
status_t
MediaTrackVideoSupplier::SeekToTime(bigtime_t* performanceTime)
{
if (!fVideoTrack)
return B_NO_INIT;
bigtime_t _performanceTime = *performanceTime;
status_t ret = fVideoTrack->SeekToTime(performanceTime);
if (ret == B_OK) {
printf("seeked: %lld -> %lld\n", _performanceTime, *performanceTime);
fPerformanceTime = *performanceTime;
}
return ret;
}
// #pragma mark -
BRect
MediaTrackVideoSupplier::Bounds() const
{
return BRect(0, 0, fFormat.u.raw_video.display.line_width - 1,
fFormat.u.raw_video.display.line_count - 1);
}
color_space
MediaTrackVideoSupplier::ColorSpace() const
{
return fFormat.u.raw_video.display.format;
}
uint32
MediaTrackVideoSupplier::BytesPerRow() const
{
return fFormat.u.raw_video.display.bytes_per_row;
}
// #pragma mark -
const char*
string_for_color_space(color_space format)
{
const char* name = "<unkown format>";
switch (format) {
case B_RGB32:
name = "B_RGB32";
break;
case B_RGBA32:
name = "B_RGBA32";
break;
case B_RGB32_BIG:
name = "B_RGB32_BIG";
break;
case B_RGBA32_BIG:
name = "B_RGBA32_BIG";
break;
case B_RGB24:
name = "B_RGB24";
break;
case B_RGB24_BIG:
name = "B_RGB24_BIG";
break;
case B_CMAP8:
name = "B_CMAP8";
break;
case B_GRAY8:
name = "B_GRAY8";
break;
case B_GRAY1:
name = "B_GRAY1";
break;
// YCbCr
case B_YCbCr422:
name = "B_YCbCr422";
break;
case B_YCbCr411:
name = "B_YCbCr411";
break;
case B_YCbCr444:
name = "B_YCbCr444";
break;
case B_YCbCr420:
name = "B_YCbCr420";
break;
// YUV
case B_YUV422:
name = "B_YUV422";
break;
case B_YUV411:
name = "B_YUV411";
break;
case B_YUV444:
name = "B_YUV444";
break;
case B_YUV420:
name = "B_YUV420";
break;
case B_YUV9:
name = "B_YUV9";
break;
case B_YUV12:
name = "B_YUV12";
break;
default:
break;
}
return name;
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef MEDIA_TRACK_VIDEO_SUPPLIER_H
#define MEDIA_TRACK_VIDEO_SUPPLIER_H
#include "VideoSupplier.h"
class BMediaTrack;
class MediaTrackVideoSupplier : public VideoSupplier {
public:
MediaTrackVideoSupplier(BMediaTrack* track,
color_space preferredFormat);
virtual ~MediaTrackVideoSupplier();
virtual media_format Format() const;
virtual status_t ReadFrame(void* buffer,
bigtime_t* performanceTime);
virtual status_t SeekToTime(bigtime_t* performanceTime);
virtual bigtime_t Position() const
{ return fPerformanceTime; }
virtual bigtime_t Duration() const
{ return fDuration; }
virtual BRect Bounds() const;
virtual color_space ColorSpace() const;
virtual uint32 BytesPerRow() const;
private:
BMediaTrack* fVideoTrack;
media_format fFormat;
bigtime_t fPerformanceTime;
bigtime_t fDuration;
};
#endif // MEDIA_TRACK_VIDEO_SUPPLIER_H

View File

@ -0,0 +1,19 @@
/*
* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "VideoSupplier.h"
VideoSupplier::VideoSupplier()
{
}
VideoSupplier::~VideoSupplier()
{
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef VIDEO_SUPPLIER_H
#define VIDEO_SUPPLIER_H
#include <MediaDefs.h>
class VideoSupplier {
public:
VideoSupplier();
virtual ~VideoSupplier();
virtual media_format Format() const = 0;
virtual status_t ReadFrame(void* buffer,
bigtime_t* performanceTime) = 0;
virtual status_t SeekToTime(bigtime_t* performanceTime) = 0;
virtual bigtime_t Position() const = 0;
virtual bigtime_t Duration() const = 0;
};
#endif // VIDEO_SUPPLIER_H